34 import java.util.stream.Stream; |
34 import java.util.stream.Stream; |
35 |
35 |
36 import static java.lang.String.format; |
36 import static java.lang.String.format; |
37 import static java.util.stream.Collectors.*; |
37 import static java.util.stream.Collectors.*; |
38 |
38 |
39 |
39 /** |
|
40 * Base class for LocalVariableTable and LocalVariableTypeTable attributes tests. |
|
41 * To add tests cases you should extend this class. |
|
42 * Then implement {@link #getVariableTables} to get LocalVariableTable or LocalVariableTypeTable attribute. |
|
43 * Then add method with local variables. |
|
44 * Finally, annotate method with information about expected variables and their types |
|
45 * by several {@link LocalVariableTestBase.ExpectedLocals} annotations. |
|
46 * To run test invoke {@link #test()} method. |
|
47 * If there are variables with the same name, set different scopes for them. |
|
48 * |
|
49 * @see #test() |
|
50 */ |
40 public abstract class LocalVariableTestBase extends TestBase { |
51 public abstract class LocalVariableTestBase extends TestBase { |
41 public static final int DEFAULT_SCOPE = 0; |
52 public static final int DEFAULT_SCOPE = 0; |
42 private final ClassFile classFile; |
53 private final ClassFile classFile; |
43 private final Class<?> clazz; |
54 private final Class<?> clazz; |
44 |
55 |
45 protected abstract List<VariableTable> getVariableTables(Code_attribute codeAttribute); |
56 /** |
46 |
57 * @param clazz class to test. Must contains annotated methods with expected results. |
|
58 */ |
47 public LocalVariableTestBase(Class<?> clazz) { |
59 public LocalVariableTestBase(Class<?> clazz) { |
48 this.clazz = clazz; |
60 this.clazz = clazz; |
49 try { |
61 try { |
50 this.classFile = ClassFile.read(getClassFile(clazz)); |
62 this.classFile = ClassFile.read(getClassFile(clazz)); |
51 } catch (IOException | ConstantPoolException e) { |
63 } catch (IOException | ConstantPoolException e) { |
52 throw new IllegalArgumentException("Can't read classfile for specified class", e); |
64 throw new IllegalArgumentException("Can't read classfile for specified class", e); |
53 } |
65 } |
54 } |
66 } |
55 |
67 |
56 |
68 protected abstract List<VariableTable> getVariableTables(Code_attribute codeAttribute); |
57 //info in the LocalVariableTable attribute is compared against expected info stored in annotations |
69 |
|
70 /** |
|
71 * Finds expected variables with their type in VariableTable. |
|
72 * Also does consistency checks, like variables from the same scope must point to different indexes. |
|
73 */ |
58 public void test() throws IOException { |
74 public void test() throws IOException { |
59 List<java.lang.reflect.Method> testMethods = Stream.of(clazz.getDeclaredMethods()) |
75 List<java.lang.reflect.Method> testMethods = Stream.of(clazz.getDeclaredMethods()) |
60 .filter(m -> m.getAnnotationsByType(ExpectedLocals.class).length > 0) |
76 .filter(m -> m.getAnnotationsByType(ExpectedLocals.class).length > 0) |
61 .collect(toList()); |
77 .collect(toList()); |
62 int failed = 0; |
78 int failed = 0; |
196 ex.printStackTrace(); |
212 ex.printStackTrace(); |
197 throw new AssertionFailedException("Issue while reading constant pool"); |
213 throw new AssertionFailedException("Issue while reading constant pool"); |
198 } |
214 } |
199 } |
215 } |
200 |
216 |
201 |
217 /** |
|
218 * LocalVariableTable and LocalVariableTypeTable are similar. |
|
219 * VariableTable interface is introduced to test this attributes in the same way without code duplication. |
|
220 */ |
202 interface VariableTable { |
221 interface VariableTable { |
203 |
222 |
204 int localVariableTableLength(); |
223 int localVariableTableLength(); |
205 |
224 |
206 List<VariableTable.Entry> entries(); |
225 List<VariableTable.Entry> entries(); |
229 "%n}", name(), type(), index(), startPC(), length()); |
248 "%n}", name(), type(), index(), startPC(), length()); |
230 } |
249 } |
231 } |
250 } |
232 } |
251 } |
233 |
252 |
|
253 /** |
|
254 * Used to store expected results in sources |
|
255 */ |
234 @Retention(RetentionPolicy.RUNTIME) |
256 @Retention(RetentionPolicy.RUNTIME) |
235 @Repeatable(Container.class) |
257 @Repeatable(Container.class) |
236 @interface ExpectedLocals { |
258 @interface ExpectedLocals { |
|
259 /** |
|
260 * @return name of a local variable |
|
261 */ |
237 String name(); |
262 String name(); |
238 |
263 |
|
264 /** |
|
265 * @return type of local variable in the internal format. |
|
266 */ |
239 String type(); |
267 String type(); |
240 |
268 |
241 //variables from different scopes can share local variable table index and/or name. |
269 //variables from different scopes can share the local variable table index and/or name. |
242 int scope() default DEFAULT_SCOPE; |
270 int scope() default DEFAULT_SCOPE; |
243 } |
271 } |
244 |
272 |
245 @Retention(RetentionPolicy.RUNTIME) |
273 @Retention(RetentionPolicy.RUNTIME) |
246 @interface Container { |
274 @interface Container { |