Wed, 27 Apr 2016 01:34:52 +0800
Initial load
http://hg.openjdk.java.net/jdk8u/jdk8u/langtools/
changeset: 2573:53ca196be1ae
tag: jdk8u25-b17
aoqi@0 | 1 | /* |
aoqi@0 | 2 | * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. |
aoqi@0 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
aoqi@0 | 4 | * |
aoqi@0 | 5 | * This code is free software; you can redistribute it and/or modify it |
aoqi@0 | 6 | * under the terms of the GNU General Public License version 2 only, as |
aoqi@0 | 7 | * published by the Free Software Foundation. |
aoqi@0 | 8 | * |
aoqi@0 | 9 | * This code is distributed in the hope that it will be useful, but WITHOUT |
aoqi@0 | 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
aoqi@0 | 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
aoqi@0 | 12 | * version 2 for more details (a copy is included in the LICENSE file that |
aoqi@0 | 13 | * accompanied this code). |
aoqi@0 | 14 | * |
aoqi@0 | 15 | * You should have received a copy of the GNU General Public License version |
aoqi@0 | 16 | * 2 along with this work; if not, write to the Free Software Foundation, |
aoqi@0 | 17 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
aoqi@0 | 18 | * |
aoqi@0 | 19 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
aoqi@0 | 20 | * or visit www.oracle.com if you need additional information or have any |
aoqi@0 | 21 | * questions. |
aoqi@0 | 22 | */ |
aoqi@0 | 23 | |
aoqi@0 | 24 | /* |
aoqi@0 | 25 | * @test |
aoqi@0 | 26 | * @bug 8005681 |
aoqi@0 | 27 | * @summary Repeated annotations on new,array,cast. |
aoqi@0 | 28 | */ |
aoqi@0 | 29 | import java.lang.annotation.*; |
aoqi@0 | 30 | import java.io.*; |
aoqi@0 | 31 | import java.util.List; |
aoqi@0 | 32 | import com.sun.tools.classfile.*; |
aoqi@0 | 33 | |
aoqi@0 | 34 | import java.lang.annotation.*; |
aoqi@0 | 35 | import static java.lang.annotation.RetentionPolicy.*; |
aoqi@0 | 36 | import static java.lang.annotation.ElementType.*; |
aoqi@0 | 37 | |
aoqi@0 | 38 | public class TestNewCastArray { |
aoqi@0 | 39 | int errors = 0; |
aoqi@0 | 40 | List<String> failedTests = new java.util.LinkedList<>(); |
aoqi@0 | 41 | |
aoqi@0 | 42 | // 'b' tests fail with only even numbers of annotations (8005681). |
aoqi@0 | 43 | String[] testclasses = {"Test1", |
aoqi@0 | 44 | "Test2a", "Test3a", "Test4a", "Test5a", |
aoqi@0 | 45 | "Test2b", "Test3b", "Test4b", "Test5b" |
aoqi@0 | 46 | }; |
aoqi@0 | 47 | |
aoqi@0 | 48 | public static void main(String[] args) throws Exception { |
aoqi@0 | 49 | new TestNewCastArray().run(); |
aoqi@0 | 50 | } |
aoqi@0 | 51 | |
aoqi@0 | 52 | void check(String testcase, int expected, int actual) { |
aoqi@0 | 53 | String res = testcase + ": (expected) " + expected + ", " + actual + " (actual): "; |
aoqi@0 | 54 | if(expected == actual) { |
aoqi@0 | 55 | res = res.concat("PASS"); |
aoqi@0 | 56 | } else { |
aoqi@0 | 57 | errors++; |
aoqi@0 | 58 | res = res.concat("FAIL"); |
aoqi@0 | 59 | failedTests.add(res); |
aoqi@0 | 60 | } |
aoqi@0 | 61 | System.out.println(res); |
aoqi@0 | 62 | } |
aoqi@0 | 63 | |
aoqi@0 | 64 | void report() { |
aoqi@0 | 65 | if(errors!=0) { |
aoqi@0 | 66 | System.err.println("Failed tests: " + errors + |
aoqi@0 | 67 | "\nfailed test cases:\n"); |
aoqi@0 | 68 | for(String t: failedTests) |
aoqi@0 | 69 | System.err.println(" " + t); |
aoqi@0 | 70 | throw new RuntimeException("FAIL: There were test failures."); |
aoqi@0 | 71 | } else |
aoqi@0 | 72 | System.out.println("PASS"); |
aoqi@0 | 73 | } |
aoqi@0 | 74 | |
aoqi@0 | 75 | void test(String clazz, String ttype, ClassFile cf, Method m, Field f, |
aoqi@0 | 76 | String name, boolean codeattr) { |
aoqi@0 | 77 | int actual = 0; |
aoqi@0 | 78 | int expected = 0, cexpected = 0; |
aoqi@0 | 79 | int index = 0; |
aoqi@0 | 80 | String memberName = null; |
aoqi@0 | 81 | Attribute attr = null; |
aoqi@0 | 82 | Code_attribute cAttr = null; |
aoqi@0 | 83 | String testcase = "undefined"; |
aoqi@0 | 84 | try { |
aoqi@0 | 85 | switch(ttype) { |
aoqi@0 | 86 | case "METHOD": |
aoqi@0 | 87 | index = m.attributes.getIndex(cf.constant_pool, name); |
aoqi@0 | 88 | memberName = m.getName(cf.constant_pool); |
aoqi@0 | 89 | if(index != -1) |
aoqi@0 | 90 | attr = m.attributes.get(index); |
aoqi@0 | 91 | break; |
aoqi@0 | 92 | case "MCODE": |
aoqi@0 | 93 | memberName = m.getName(cf.constant_pool); |
aoqi@0 | 94 | //fetch index of and code attribute and annotations from code attribute. |
aoqi@0 | 95 | index = m.attributes.getIndex(cf.constant_pool, Attribute.Code); |
aoqi@0 | 96 | if(index!= -1) { |
aoqi@0 | 97 | attr = m.attributes.get(index); |
aoqi@0 | 98 | assert attr instanceof Code_attribute; |
aoqi@0 | 99 | cAttr = (Code_attribute)attr; |
aoqi@0 | 100 | index = cAttr.attributes.getIndex(cf.constant_pool, name); |
aoqi@0 | 101 | if(index!= -1) |
aoqi@0 | 102 | attr = cAttr.attributes.get(index); |
aoqi@0 | 103 | } |
aoqi@0 | 104 | break; |
aoqi@0 | 105 | case "FIELD": |
aoqi@0 | 106 | index = f.attributes.getIndex(cf.constant_pool, name); |
aoqi@0 | 107 | memberName = f.getName(cf.constant_pool); |
aoqi@0 | 108 | if(index != -1) |
aoqi@0 | 109 | attr = f.attributes.get(index); |
aoqi@0 | 110 | break; |
aoqi@0 | 111 | case "CODE": |
aoqi@0 | 112 | memberName = f.getName(cf.constant_pool); |
aoqi@0 | 113 | //fetch index of and code attribute and annotations from code attribute. |
aoqi@0 | 114 | index = cf.attributes.getIndex(cf.constant_pool, Attribute.Code); |
aoqi@0 | 115 | if(index!= -1) { |
aoqi@0 | 116 | attr = cf.attributes.get(index); |
aoqi@0 | 117 | assert attr instanceof Code_attribute; |
aoqi@0 | 118 | cAttr = (Code_attribute)attr; |
aoqi@0 | 119 | index = cAttr.attributes.getIndex(cf.constant_pool, name); |
aoqi@0 | 120 | if(index!= -1) |
aoqi@0 | 121 | attr = cAttr.attributes.get(index); |
aoqi@0 | 122 | } |
aoqi@0 | 123 | break; |
aoqi@0 | 124 | default: |
aoqi@0 | 125 | break; |
aoqi@0 | 126 | } |
aoqi@0 | 127 | } catch(ConstantPoolException cpe) { cpe.printStackTrace(); } |
aoqi@0 | 128 | testcase = clazz+" "+ttype + ": " + memberName + ", " + name; |
aoqi@0 | 129 | if(index != -1) { |
aoqi@0 | 130 | //count RuntimeTypeAnnotations |
aoqi@0 | 131 | assert attr instanceof RuntimeTypeAnnotations_attribute; |
aoqi@0 | 132 | RuntimeTypeAnnotations_attribute tAttr = |
aoqi@0 | 133 | (RuntimeTypeAnnotations_attribute)attr; |
aoqi@0 | 134 | actual += tAttr.annotations.length; |
aoqi@0 | 135 | } |
aoqi@0 | 136 | if(memberName.compareTo("<init>")==0) memberName=clazz+memberName; |
aoqi@0 | 137 | switch ( memberName ) { |
aoqi@0 | 138 | //METHOD: |
aoqi@0 | 139 | case "Test1<init>": expected=0; break; |
aoqi@0 | 140 | case "testr22_22": expected=4; break; |
aoqi@0 | 141 | case "testr11_11": expected=4; break; |
aoqi@0 | 142 | case "testr12_21": expected=4; break; |
aoqi@0 | 143 | case "testr20_02": expected=2; break; |
aoqi@0 | 144 | |
aoqi@0 | 145 | case "Test2a<init>": cexpected=0; break; |
aoqi@0 | 146 | case "test00_00_11_11": cexpected=4; break; |
aoqi@0 | 147 | case "test21_12_21_12": cexpected=8; break; |
aoqi@0 | 148 | case "test_new1": cexpected=2; break; |
aoqi@0 | 149 | case "test_new2": cexpected=2; break; |
aoqi@0 | 150 | case "test_cast1": cexpected=2; break; |
aoqi@0 | 151 | case "test_cast2": cexpected=2; break; |
aoqi@0 | 152 | |
aoqi@0 | 153 | case "Test2b<init>": cexpected=0; break; |
aoqi@0 | 154 | case "test20_02_20_02": cexpected=4; break; |
aoqi@0 | 155 | case "test22_22_22_22": cexpected=8; break; |
aoqi@0 | 156 | case "test_new3": cexpected=1; break; |
aoqi@0 | 157 | case "test_new4": cexpected=1; break; |
aoqi@0 | 158 | case "test_new5": cexpected=2; break; |
aoqi@0 | 159 | case "test_cast3": cexpected=1; break; |
aoqi@0 | 160 | case "test_cast4": cexpected=2; break; |
aoqi@0 | 161 | |
aoqi@0 | 162 | case "Test3a<init>": cexpected=10; break; |
aoqi@0 | 163 | case "SA_21_12c": cexpected = 0; break; |
aoqi@0 | 164 | case "SA_01_10c": expected = 0; break; |
aoqi@0 | 165 | case "SA_11_11c": expected = 0; break; |
aoqi@0 | 166 | |
aoqi@0 | 167 | case "Test3b<init>": cexpected=6; break; |
aoqi@0 | 168 | case "SA_22_22c": cexpected = 0; break; |
aoqi@0 | 169 | case "SA_20_02c": cexpected = 0; break; |
aoqi@0 | 170 | |
aoqi@0 | 171 | case "Test3c<init>": cexpected=8; break; |
aoqi@0 | 172 | case "SA_10_10": cexpected = 0; break; |
aoqi@0 | 173 | case "SA_10_01": cexpected = 0; break; |
aoqi@0 | 174 | case "SA_21_12": cexpected = 0; break; |
aoqi@0 | 175 | |
aoqi@0 | 176 | case "Test3d<init>": cexpected=6; break; |
aoqi@0 | 177 | case "SA_20_02": cexpected = 0; break; |
aoqi@0 | 178 | case "SA_22_22": cexpected = 0; break; |
aoqi@0 | 179 | |
aoqi@0 | 180 | case "Test4a<init>": cexpected=4; break; |
aoqi@0 | 181 | case "nS_21": cexpected = 0; break; |
aoqi@0 | 182 | case "nS_12": cexpected = 0; break; |
aoqi@0 | 183 | |
aoqi@0 | 184 | case "Test4b<init>": cexpected=4; break; |
aoqi@0 | 185 | case "nS20": cexpected = 0; break; |
aoqi@0 | 186 | case "nS02": cexpected = 0; break; |
aoqi@0 | 187 | case "nS22": cexpected = 0; break; |
aoqi@0 | 188 | |
aoqi@0 | 189 | case "Test5a<init>": cexpected=4; break; |
aoqi@0 | 190 | case "ci11": expected = 0; break; |
aoqi@0 | 191 | case "ci21": expected = 0; break; |
aoqi@0 | 192 | |
aoqi@0 | 193 | case "Test5b<init>": cexpected=3; break; |
aoqi@0 | 194 | case "ci2": expected = 0; break; |
aoqi@0 | 195 | case "ci22": expected = 0; break; |
aoqi@0 | 196 | |
aoqi@0 | 197 | default: expected = 0; break; |
aoqi@0 | 198 | } |
aoqi@0 | 199 | if(codeattr) |
aoqi@0 | 200 | check(testcase, cexpected, actual); |
aoqi@0 | 201 | else |
aoqi@0 | 202 | check(testcase, expected, actual); |
aoqi@0 | 203 | } |
aoqi@0 | 204 | |
aoqi@0 | 205 | public void run() { |
aoqi@0 | 206 | ClassFile cf = null; |
aoqi@0 | 207 | InputStream in = null; |
aoqi@0 | 208 | for( String clazz : testclasses) { |
aoqi@0 | 209 | String testclazz = "TestNewCastArray$" + clazz + ".class"; |
aoqi@0 | 210 | System.out.println("Testing " + testclazz); |
aoqi@0 | 211 | try { |
aoqi@0 | 212 | in = getClass().getResource(testclazz).openStream(); |
aoqi@0 | 213 | cf = ClassFile.read(in); |
aoqi@0 | 214 | in.close(); |
aoqi@0 | 215 | } catch(Exception e) { e.printStackTrace(); } |
aoqi@0 | 216 | |
aoqi@0 | 217 | if(clazz.startsWith("Test1")) { |
aoqi@0 | 218 | for (Field f: cf.fields) |
aoqi@0 | 219 | test(clazz, "FIELD", cf, null, f, Attribute.RuntimeVisibleTypeAnnotations, false); |
aoqi@0 | 220 | for (Method m: cf.methods) |
aoqi@0 | 221 | test(clazz, "METHOD", cf, m, null, Attribute.RuntimeVisibleTypeAnnotations, false); |
aoqi@0 | 222 | } else { |
aoqi@0 | 223 | for (Field f: cf.fields) |
aoqi@0 | 224 | test(clazz, "CODE", cf, null, f, Attribute.RuntimeVisibleTypeAnnotations, true); |
aoqi@0 | 225 | for (Method m: cf.methods) |
aoqi@0 | 226 | test(clazz, "MCODE", cf, m, null, Attribute.RuntimeVisibleTypeAnnotations, true); |
aoqi@0 | 227 | } |
aoqi@0 | 228 | } |
aoqi@0 | 229 | report(); |
aoqi@0 | 230 | } |
aoqi@0 | 231 | |
aoqi@0 | 232 | //////// test class ////////////////////////// |
aoqi@0 | 233 | // "Test1" not in code attribute. |
aoqi@0 | 234 | // on arrays on and in method return |
aoqi@0 | 235 | static class Test1 { |
aoqi@0 | 236 | Test1(){} |
aoqi@0 | 237 | // OK expect 5, got 5 |
aoqi@0 | 238 | String @A @A @B @B[] @A @A @B @B [] testr22_22(Test1 this, String param, String ... vararg) { |
aoqi@0 | 239 | String [][] sarray = new String [2][2]; |
aoqi@0 | 240 | return sarray; |
aoqi@0 | 241 | } |
aoqi@0 | 242 | // OK expect 5, got 5 |
aoqi@0 | 243 | String @A @B [] @A @B [] testr11_11(Test1 this, String param, String ... vararg) { |
aoqi@0 | 244 | String [][] sarray = new String [2][2]; |
aoqi@0 | 245 | return sarray; |
aoqi@0 | 246 | } |
aoqi@0 | 247 | // OK expect 5, got 5 |
aoqi@0 | 248 | String @A @B @B []@B @B @A[] testr12_21(Test1 this, String param, String ... vararg) { |
aoqi@0 | 249 | String [][] sarray = new String [2][2]; |
aoqi@0 | 250 | return sarray; |
aoqi@0 | 251 | } |
aoqi@0 | 252 | // OK expect 3, got 3 |
aoqi@0 | 253 | String @A @A [] @B @B [] testr20_02(Test1 this, String param, String ... vararg) { |
aoqi@0 | 254 | String [][] sarray = new String [2][2]; |
aoqi@0 | 255 | return sarray; |
aoqi@0 | 256 | } |
aoqi@0 | 257 | } |
aoqi@0 | 258 | |
aoqi@0 | 259 | // Inside method body (in method's code attribute) |
aoqi@0 | 260 | static class Test2a { |
aoqi@0 | 261 | Test2a(){} |
aoqi@0 | 262 | Object o = new Integer(1); |
aoqi@0 | 263 | // expect 4 |
aoqi@0 | 264 | String[][] test00_00_11_11(Test2a this, String param, String ... vararg) { |
aoqi@0 | 265 | String [] [] sarray = new String @A @B[2] @A @B [2]; |
aoqi@0 | 266 | return sarray; |
aoqi@0 | 267 | } |
aoqi@0 | 268 | |
aoqi@0 | 269 | // expect 8 |
aoqi@0 | 270 | String[][] test21_12_21_12(Test2a this, String param, String ... vararg) { |
aoqi@0 | 271 | String @A @A @B [] @A @B @B [] sarray = new String @A @A @B[2] @A @B @B [2]; |
aoqi@0 | 272 | return sarray; |
aoqi@0 | 273 | } |
aoqi@0 | 274 | |
aoqi@0 | 275 | void test_new1() { String nS_21 = new @A @A @B String("Hello"); } |
aoqi@0 | 276 | void test_new2() { String nS_12 = new @A @B @B String("Hello"); } |
aoqi@0 | 277 | void test_cast1() { String tcs11 = (@A @B String)o; } |
aoqi@0 | 278 | void test_cast2() { String tcs21 = (@A @A @B String)o; } |
aoqi@0 | 279 | } |
aoqi@0 | 280 | |
aoqi@0 | 281 | static class Test2b { |
aoqi@0 | 282 | Test2b(){} |
aoqi@0 | 283 | Object o = new Integer(1); |
aoqi@0 | 284 | // expect 4 |
aoqi@0 | 285 | String[][] test20_02_20_02(Test2b this, String param, String ... vararg) { |
aoqi@0 | 286 | String @A @A [] @B @B [] sarray = new String @A @A[2] @B @B [2]; |
aoqi@0 | 287 | return sarray; |
aoqi@0 | 288 | } |
aoqi@0 | 289 | |
aoqi@0 | 290 | // expect 8 |
aoqi@0 | 291 | String[][] test22_22_22_22(Test2b this, String param, String ... vararg) { |
aoqi@0 | 292 | String @A @A @B @B [] @A @A @B @B [] sarray = new String @A @A @B @B [2] @A @A @B @B [2]; |
aoqi@0 | 293 | return sarray; |
aoqi@0 | 294 | } |
aoqi@0 | 295 | |
aoqi@0 | 296 | void test_new3() { String nS20 = new @A @A String("Hello"); } |
aoqi@0 | 297 | void test_new4() { String nS02 = new @B @B String("Hello"); } |
aoqi@0 | 298 | void test_new5() { String nS22 = new @A @A @B @B String("Hello"); } |
aoqi@0 | 299 | void test_cast3() { String tcs2 = (@A @A String)o; } |
aoqi@0 | 300 | void test_cast4() { String tcs22 = (@A @A @B @B String)o;} |
aoqi@0 | 301 | } |
aoqi@0 | 302 | |
aoqi@0 | 303 | // array levels |
aoqi@0 | 304 | static class Test3a { |
aoqi@0 | 305 | Test3a(){} |
aoqi@0 | 306 | // expect 4+2+4=10 |
aoqi@0 | 307 | String [][] SA_21_12c = new String @A @A @B [2] @A @B @B[2]; |
aoqi@0 | 308 | String [][] SA_01_10c = new String @B [2] @A [2]; |
aoqi@0 | 309 | String [][] SA_11_11c = new String @A @B [2] @A @B [2]; |
aoqi@0 | 310 | } |
aoqi@0 | 311 | |
aoqi@0 | 312 | static class Test3b { |
aoqi@0 | 313 | Test3b(){} |
aoqi@0 | 314 | // expect 4+2=6 |
aoqi@0 | 315 | String [][] SA_22_22c = new String @A @A @B @B[2] @A @A @B @B[2]; |
aoqi@0 | 316 | String [][] SA_20_02c = new String @A @A [2] @B @B[2]; |
aoqi@0 | 317 | } |
aoqi@0 | 318 | static class Test3c { |
aoqi@0 | 319 | Test3c(){} |
aoqi@0 | 320 | // OK expect 4 |
aoqi@0 | 321 | String @A [] @A[] SA_10_10 = new String [2][2]; |
aoqi@0 | 322 | String @A [] @B[] SA_10_01 = new String [2][2]; |
aoqi@0 | 323 | String @A @A @B[] @A @B @B [] SA_21_12 = new String [2][2]; |
aoqi@0 | 324 | } |
aoqi@0 | 325 | |
aoqi@0 | 326 | static class Test3d { |
aoqi@0 | 327 | Test3d(){} |
aoqi@0 | 328 | // OK expect 4 |
aoqi@0 | 329 | String @A @A [] @B @B [] SA_20_02 = new String [2][2]; |
aoqi@0 | 330 | String @A @A @B @B[] @A @A @B @B [] SA_22_22 = new String [2][2]; |
aoqi@0 | 331 | } |
aoqi@0 | 332 | |
aoqi@0 | 333 | // on new |
aoqi@0 | 334 | static class Test4a { |
aoqi@0 | 335 | Test4a(){} |
aoqi@0 | 336 | // expect 2+2=4 |
aoqi@0 | 337 | String nS_21 = new @A @A @B String("Hello"); |
aoqi@0 | 338 | String nS_12 = new @A @B @B String("Hello"); |
aoqi@0 | 339 | } |
aoqi@0 | 340 | |
aoqi@0 | 341 | static class Test4b { |
aoqi@0 | 342 | Test4b(){} |
aoqi@0 | 343 | // expect 1+1+2=4 |
aoqi@0 | 344 | String nS20 = new @A @A String("Hello"); |
aoqi@0 | 345 | String nS02 = new @B @B String("Hello"); |
aoqi@0 | 346 | String nS22 = new @A @A @B @B String("Hello"); |
aoqi@0 | 347 | } |
aoqi@0 | 348 | |
aoqi@0 | 349 | // Cast expressions |
aoqi@0 | 350 | static class Test5a { |
aoqi@0 | 351 | Test5a(){} |
aoqi@0 | 352 | Object o = new Integer(1); |
aoqi@0 | 353 | // expect 2+2=4 |
aoqi@0 | 354 | Integer ci11 = (@A @B Integer)o; // OK expect 3, got 3 |
aoqi@0 | 355 | Integer ci21 = (@A @A @B Integer)o; // OK expect 3, got 3 |
aoqi@0 | 356 | } |
aoqi@0 | 357 | |
aoqi@0 | 358 | static class Test5b { |
aoqi@0 | 359 | Test5b(){} |
aoqi@0 | 360 | Object o = new Integer(1); |
aoqi@0 | 361 | // Cast expressions |
aoqi@0 | 362 | // expect 1+2=3 |
aoqi@0 | 363 | Integer ci2 = (@A @A Integer)o; // FAIL expect 2, got 1 |
aoqi@0 | 364 | Integer ci22 = (@A @A @B @B Integer)o; // FAIL expect 3, got 1 |
aoqi@0 | 365 | } |
aoqi@0 | 366 | |
aoqi@0 | 367 | @Retention(RUNTIME) @Target({TYPE_USE}) @Repeatable( AC.class ) @interface A { } |
aoqi@0 | 368 | @Retention(RUNTIME) @Target({TYPE_USE}) @Repeatable( BC.class ) @interface B { } |
aoqi@0 | 369 | @Retention(RUNTIME) @Target({FIELD}) @Repeatable( FC.class ) @interface F { } |
aoqi@0 | 370 | @Retention(RUNTIME) @Target({TYPE_USE}) @interface AC { A[] value(); } |
aoqi@0 | 371 | @Retention(RUNTIME) @Target({TYPE_USE}) @interface BC { B[] value(); } |
aoqi@0 | 372 | @Retention(RUNTIME) @Target({FIELD}) @interface FC { F[] value(); } |
aoqi@0 | 373 | |
aoqi@0 | 374 | } |
aoqi@0 | 375 |