blob: f01c9043b9ba3661e2c47868fa05d699700438b2 [file] [log] [blame]
Alex Light705ad492015-09-21 11:36:30 -07001#!/usr/bin/python3
2#
3# Copyright (C) 2015 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""
18Generate Smali test files for test 967.
19"""
20
21import os
22import sys
23from pathlib import Path
24
25BUILD_TOP = os.getenv("ANDROID_BUILD_TOP")
26if BUILD_TOP is None:
27 print("ANDROID_BUILD_TOP not set. Please run build/envsetup.sh", file=sys.stderr)
28 sys.exit(1)
29
30# Allow us to import utils and mixins.
31sys.path.append(str(Path(BUILD_TOP)/"art"/"test"/"utils"/"python"))
32
33from testgen.utils import get_copyright, subtree_sizes, gensym, filter_blanks
34import testgen.mixins as mixins
35
36from enum import Enum
37from functools import total_ordering
38import itertools
39import string
40
41# The max depth the type tree can have.
42MAX_IFACE_DEPTH = 3
43
44class MainClass(mixins.DumpMixin, mixins.Named, mixins.SmaliFileMixin):
45 """
46 A Main.smali file containing the Main class and the main function. It will run
47 all the test functions we have.
48 """
49
50 MAIN_CLASS_TEMPLATE = """{copyright}
51
52.class public LMain;
53.super Ljava/lang/Object;
54
55# class Main {{
56
57.method public constructor <init>()V
58 .registers 1
59 invoke-direct {{p0}}, Ljava/lang/Object;-><init>()V
60 return-void
61.end method
62
63{test_funcs}
64
65{main_func}
66
67# }}
68"""
69
70 MAIN_FUNCTION_TEMPLATE = """
71# public static void main(String[] args) {{
72.method public static main([Ljava/lang/String;)V
73 .locals 0
74
75 {test_group_invoke}
76
77 return-void
78.end method
79# }}
80"""
81
82 TEST_GROUP_INVOKE_TEMPLATE = """
83# {test_name}();
84 invoke-static {{}}, {test_name}()V
85"""
86
87 def __init__(self):
88 """
89 Initialize this MainClass. We start out with no tests.
90 """
91 self.tests = set()
92
93 def get_expected(self):
94 """
95 Get the expected output of this test.
96 """
97 all_tests = sorted(self.tests)
98 return filter_blanks("\n".join(a.get_expected() for a in all_tests))
99
100 def add_test(self, ty):
101 """
102 Add a test for the concrete type 'ty'
103 """
104 self.tests.add(Func(ty))
105
106 def get_name(self):
107 """
108 Get the name of this class
109 """
110 return "Main"
111
112 def __str__(self):
113 """
114 Print the MainClass smali code.
115 """
116 all_tests = sorted(self.tests)
117 test_invoke = ""
118 test_funcs = ""
119 for t in all_tests:
120 test_funcs += str(t)
121 for t in all_tests:
122 test_invoke += self.TEST_GROUP_INVOKE_TEMPLATE.format(test_name=t.get_name())
123 main_func = self.MAIN_FUNCTION_TEMPLATE.format(test_group_invoke=test_invoke)
124
125 return self.MAIN_CLASS_TEMPLATE.format(copyright = get_copyright("smali"),
126 test_funcs = test_funcs,
127 main_func = main_func)
128
129class Func(mixins.Named, mixins.NameComparableMixin):
130 """
131 A function that tests the functionality of a concrete type. Should only be
132 constructed by MainClass.add_test.
133 """
134
135 TEST_FUNCTION_TEMPLATE = """
136# public static void {fname}() {{
137# {farg} v = null;
138# try {{
139# v = new {farg}();
140# }} catch (Throwable e) {{
141# System.out.println("Unexpected error occurred which creating {farg} instance");
142# e.printStackTrace(System.out);
143# return;
144# }}
145# try {{
146# v.callSupers();
147# return;
148# }} catch (Throwable e) {{
149# e.printStackTrace(System.out);
150# return;
151# }}
152# }}
153.method public static {fname}()V
154 .locals 7
155 sget-object v4, Ljava/lang/System;->out:Ljava/io/PrintStream;
156
157 :new_{fname}_try_start
158 new-instance v0, L{farg};
159 invoke-direct {{v0}}, L{farg};-><init>()V
160 goto :call_{fname}_try_start
161 :new_{fname}_try_end
162 .catch Ljava/lang/Throwable; {{:new_{fname}_try_start .. :new_{fname}_try_end}} :new_error_{fname}_start
163 :new_error_{fname}_start
164 move-exception v6
165 const-string v5, "Unexpected error occurred which creating {farg} instance"
166 invoke-virtual {{v4,v5}}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
167 invoke-virtual {{v6,v4}}, Ljava/lang/Throwable;->printStackTrace(Ljava/io/PrintStream;)V
168 return-void
169 :call_{fname}_try_start
170 invoke-virtual {{v0}}, L{farg};->callSupers()V
171 return-void
172 :call_{fname}_try_end
173 .catch Ljava/lang/Throwable; {{:call_{fname}_try_start .. :call_{fname}_try_end}} :error_{fname}_start
174 :error_{fname}_start
175 move-exception v6
176 invoke-virtual {{v6,v4}}, Ljava/lang/Throwable;->printStackTrace(Ljava/io/PrintStream;)V
177 return-void
178.end method
179"""
180
181 def __init__(self, farg):
182 """
183 Initialize a test function for the given argument
184 """
185 self.farg = farg
186
187 def get_expected(self):
188 """
189 Get the expected output calling this function.
190 """
191 return "\n".join(self.farg.get_expected())
192
193 def get_name(self):
194 """
195 Get the name of this function
196 """
197 return "TEST_FUNC_{}".format(self.farg.get_name())
198
199 def __str__(self):
200 """
201 Print the smali code of this function.
202 """
203 return self.TEST_FUNCTION_TEMPLATE.format(fname = self.get_name(),
204 farg = self.farg.get_name())
205
206class InterfaceCallResponse(Enum):
207 """
208 An enumeration of all the different types of responses to an interface call we can have
209 """
210 NoError = 0
211 NoSuchMethodError = 1
212 AbstractMethodError = 2
213 IncompatibleClassChangeError = 3
214
215 def get_output_format(self):
216 if self == InterfaceCallResponse.NoError:
217 return "No exception thrown for {iface_name}.super.call() on {tree}\n"
218 elif self == InterfaceCallResponse.AbstractMethodError:
219 return "AbstractMethodError thrown for {iface_name}.super.call() on {tree}\n"
220 elif self == InterfaceCallResponse.NoSuchMethodError:
221 return "NoSuchMethodError thrown for {iface_name}.super.call() on {tree}\n"
222 else:
223 return "IncompatibleClassChangeError thrown for {iface_name}.super.call() on {tree}\n"
224
225class TestClass(mixins.DumpMixin, mixins.Named, mixins.NameComparableMixin, mixins.SmaliFileMixin):
226 """
227 A class that will be instantiated to test interface super behavior.
228 """
229
230 TEST_CLASS_TEMPLATE = """{copyright}
231
232.class public L{class_name};
233.super Ljava/lang/Object;
234{implements_spec}
235
236# public class {class_name} implements {ifaces} {{
237
238.method public constructor <init>()V
239 .registers 1
240 invoke-direct {{p0}}, Ljava/lang/Object;-><init>()V
241 return-void
242.end method
243
244# public void call() {{
245# throw new Error("{class_name}.call(v) should never get called!");
246# }}
247.method public call()V
248 .locals 2
249 new-instance v0, Ljava/lang/Error;
250 const-string v1, "{class_name}.call(v) should never get called!"
251 invoke-direct {{v0, v1}}, Ljava/lang/Error;-><init>(Ljava/lang/String;)V
252 throw v0
253.end method
254
255# public void callSupers() {{
256.method public callSupers()V
257 .locals 4
258 sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
259
260 {super_calls}
261
262 return-void
263.end method
264# }}
265
266# }}
267"""
268 SUPER_CALL_TEMPLATE = """
269# try {{
270# System.out.println("Calling {iface_name}.super.call() on {tree}");
271# {iface_name}.super.call();
272# System.out.println("No exception thrown for {iface_name}.super.call() on {tree}");
273# }} catch (AbstractMethodError ame) {{
274# System.out.println("AbstractMethodError thrown for {iface_name}.super.call() on {tree}");
275# }} catch (NoSuchMethodError nsme) {{
276# System.out.println("NoSuchMethodError thrown for {iface_name}.super.call() on {tree}");
277# }} catch (IncompatibleClassChangeError icce) {{
278# System.out.println("IncompatibleClassChangeError thrown for {iface_name}.super.call() on {tree}");
279# }} catch (Throwable t) {{
280# System.out.println("Unknown error thrown for {iface_name}.super.call() on {tree}");
281# throw t;
282# }}
283 :call_{class_name}_{iface_name}_try_start
284 const-string v1, "Calling {iface_name}.super.call() on {tree}"
285 invoke-virtual {{v0, v1}}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
286 invoke-super {{p0}}, L{iface_name};->call()V
287 const-string v1, "No exception thrown for {iface_name}.super.call() on {tree}"
288 invoke-virtual {{v0, v1}}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
289 goto :call_{class_name}_{iface_name}_end
290 :call_{class_name}_{iface_name}_try_end
291 .catch Ljava/lang/AbstractMethodError; {{:call_{class_name}_{iface_name}_try_start .. :call_{class_name}_{iface_name}_try_end}} :AME_{class_name}_{iface_name}_start
292 .catch Ljava/lang/NoSuchMethodError; {{:call_{class_name}_{iface_name}_try_start .. :call_{class_name}_{iface_name}_try_end}} :NSME_{class_name}_{iface_name}_start
293 .catch Ljava/lang/IncompatibleClassChangeError; {{:call_{class_name}_{iface_name}_try_start .. :call_{class_name}_{iface_name}_try_end}} :ICCE_{class_name}_{iface_name}_start
294 .catch Ljava/lang/Throwable; {{:call_{class_name}_{iface_name}_try_start .. :call_{class_name}_{iface_name}_try_end}} :error_{class_name}_{iface_name}_start
295 :AME_{class_name}_{iface_name}_start
296 const-string v1, "AbstractMethodError thrown for {iface_name}.super.call() on {tree}"
297 invoke-virtual {{v0, v1}}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
298 goto :call_{class_name}_{iface_name}_end
299 :NSME_{class_name}_{iface_name}_start
300 const-string v1, "NoSuchMethodError thrown for {iface_name}.super.call() on {tree}"
301 invoke-virtual {{v0, v1}}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
302 goto :call_{class_name}_{iface_name}_end
303 :ICCE_{class_name}_{iface_name}_start
304 const-string v1, "IncompatibleClassChangeError thrown for {iface_name}.super.call() on {tree}"
305 invoke-virtual {{v0, v1}}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
306 goto :call_{class_name}_{iface_name}_end
307 :error_{class_name}_{iface_name}_start
308 move-exception v2
309 const-string v1, "Unknown error thrown for {iface_name}.super.call() on {tree}"
310 invoke-virtual {{v0, v1}}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
311 throw v2
312 :call_{class_name}_{iface_name}_end
313"""
314
315 IMPLEMENTS_TEMPLATE = """
316.implements L{iface_name};
317"""
318
319 OUTPUT_PREFIX = "Calling {iface_name}.super.call() on {tree}\n"
320
321 def __init__(self, ifaces):
322 """
323 Initialize this test class which implements the given interfaces
324 """
325 self.ifaces = ifaces
326 self.class_name = "CLASS_"+gensym()
327
328 def get_name(self):
329 """
330 Get the name of this class
331 """
332 return self.class_name
333
334 def get_tree(self):
335 """
336 Print out a representation of the type tree of this class
337 """
338 return "[{class_name} {iface_tree}]".format(class_name = self.class_name,
339 iface_tree = print_tree(self.ifaces))
340
341 def __iter__(self):
342 """
343 Step through all interfaces implemented transitively by this class
344 """
345 for i in self.ifaces:
346 yield i
347 yield from i
348
349 def get_expected(self):
350 for iface in self.ifaces:
351 yield self.OUTPUT_PREFIX.format(iface_name = iface.get_name(), tree = self.get_tree())
352 yield from iface.get_expected()
353 yield iface.get_response().get_output_format().format(iface_name = iface.get_name(),
354 tree = self.get_tree())
355
356 def __str__(self):
357 """
358 Print the smali code of this class.
359 """
360 s_ifaces = '\n'.join(map(lambda a: self.IMPLEMENTS_TEMPLATE.format(iface_name = a.get_name()),
361 self.ifaces))
362 j_ifaces = ', '.join(map(lambda a: a.get_name(), self.ifaces))
363 super_template = self.SUPER_CALL_TEMPLATE
364 super_calls = "\n".join(super_template.format(iface_name = iface.get_name(),
365 class_name = self.get_name(),
366 tree = self.get_tree()) for iface in self.ifaces)
367 return self.TEST_CLASS_TEMPLATE.format(copyright = get_copyright('smali'),
368 ifaces = j_ifaces,
369 implements_spec = s_ifaces,
370 tree = self.get_tree(),
371 class_name = self.class_name,
372 super_calls = super_calls)
373
374class InterfaceType(Enum):
375 """
376 An enumeration of all the different types of interfaces we can have.
377
378 default: It has a default method
379 abstract: It has a method declared but not defined
380 empty: It does not have the method
381 """
382 default = 0
383 abstract = 1
384 empty = 2
385
386 def get_suffix(self):
387 if self == InterfaceType.default:
388 return "_DEFAULT"
389 elif self == InterfaceType.abstract:
390 return "_ABSTRACT"
391 elif self == InterfaceType.empty:
392 return "_EMPTY"
393 else:
394 raise TypeError("Interface type had illegal value.")
395
396class ConflictInterface:
397 """
398 A singleton representing a conflict of default methods.
399 """
400
401 def is_conflict(self):
402 """
403 Returns true if this is a conflict interface and calling the method on this interface will
404 result in an IncompatibleClassChangeError.
405 """
406 return True
407
408 def is_abstract(self):
409 """
410 Returns true if this is an abstract interface and calling the method on this interface will
411 result in an AbstractMethodError.
412 """
413 return False
414
415 def is_empty(self):
416 """
417 Returns true if this is an abstract interface and calling the method on this interface will
418 result in a NoSuchMethodError.
419 """
420 return False
421
422 def is_default(self):
423 """
424 Returns true if this is a default interface and calling the method on this interface will
425 result in a method actually being called.
426 """
427 return False
428
429 def get_response(self):
430 return InterfaceCallResponse.IncompatibleClassChangeError
431
432CONFLICT_TYPE = ConflictInterface()
433
434class TestInterface(mixins.DumpMixin, mixins.Named, mixins.NameComparableMixin, mixins.SmaliFileMixin):
435 """
436 An interface that will be used to test default method resolution order.
437 """
438
439 TEST_INTERFACE_TEMPLATE = """{copyright}
440.class public abstract interface L{class_name};
441.super Ljava/lang/Object;
442{implements_spec}
443
444# public interface {class_name} {extends} {ifaces} {{
445
446{func}
447
448# }}
449"""
450
451 SUPER_CALL_TEMPLATE = TestClass.SUPER_CALL_TEMPLATE
452 OUTPUT_PREFIX = TestClass.OUTPUT_PREFIX
453
454 DEFAULT_FUNC_TEMPLATE = """
455# public default void call() {{
456.method public call()V
457 .locals 4
458 sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
459
460 {super_calls}
461
462 return-void
463.end method
464# }}
465"""
466
467 ABSTRACT_FUNC_TEMPLATE = """
468# public void call();
469.method public abstract call()V
470.end method
471"""
472
473 EMPTY_FUNC_TEMPLATE = """"""
474
475 IMPLEMENTS_TEMPLATE = """
476.implements L{iface_name};
477"""
478
479 def __init__(self, ifaces, iface_type, full_name = None):
480 """
481 Initialize interface with the given super-interfaces
482 """
483 self.ifaces = sorted(ifaces)
484 self.iface_type = iface_type
485 if full_name is None:
486 end = self.iface_type.get_suffix()
487 self.class_name = "INTERFACE_"+gensym()+end
488 else:
489 self.class_name = full_name
490
491 def get_specific_version(self, v):
492 """
493 Returns a copy of this interface of the given type for use in partial compilation.
494 """
495 return TestInterface(self.ifaces, v, full_name = self.class_name)
496
497 def get_super_types(self):
498 """
499 Returns a set of all the supertypes of this interface
500 """
501 return set(i2 for i2 in self)
502
503 def is_conflict(self):
504 """
505 Returns true if this is a conflict interface and calling the method on this interface will
506 result in an IncompatibleClassChangeError.
507 """
508 return False
509
510 def is_abstract(self):
511 """
512 Returns true if this is an abstract interface and calling the method on this interface will
513 result in an AbstractMethodError.
514 """
515 return self.iface_type == InterfaceType.abstract
516
517 def is_empty(self):
518 """
519 Returns true if this is an abstract interface and calling the method on this interface will
520 result in a NoSuchMethodError.
521 """
522 return self.iface_type == InterfaceType.empty
523
524 def is_default(self):
525 """
526 Returns true if this is a default interface and calling the method on this interface will
527 result in a method actually being called.
528 """
529 return self.iface_type == InterfaceType.default
530
531 def get_expected(self):
532 response = self.get_response()
533 if response == InterfaceCallResponse.NoError:
534 for iface in self.ifaces:
535 if self.is_default():
536 yield self.OUTPUT_PREFIX.format(iface_name = iface.get_name(), tree = self.get_tree())
537 yield from iface.get_expected()
538 if self.is_default():
539 yield iface.get_response().get_output_format().format(iface_name = iface.get_name(),
540 tree = self.get_tree())
541
542 def get_response(self):
543 if self.is_default():
544 return InterfaceCallResponse.NoError
545 elif self.is_abstract():
546 return InterfaceCallResponse.AbstractMethodError
547 elif len(self.ifaces) == 0:
548 return InterfaceCallResponse.NoSuchMethodError
549 else:
550 return self.get_called().get_response()
551
552 def get_called(self):
553 """
554 Returns the interface that will be called when the method on this class is invoked or
555 CONFLICT_TYPE if there is no interface that will be called.
556 """
557 if not self.is_empty() or len(self.ifaces) == 0:
558 return self
559 else:
560 best = self
561 for super_iface in self.ifaces:
562 super_best = super_iface.get_called()
563 if super_best.is_conflict():
564 return CONFLICT_TYPE
565 elif best.is_default():
566 if super_best.is_default():
567 return CONFLICT_TYPE
568 elif best.is_abstract():
569 if super_best.is_default():
570 best = super_best
571 else:
572 assert best.is_empty()
573 best = super_best
574 return best
575
576 def get_name(self):
577 """
578 Get the name of this class
579 """
580 return self.class_name
581
582 def get_tree(self):
583 """
584 Print out a representation of the type tree of this class
585 """
586 return "[{class_name} {iftree}]".format(class_name = self.get_name(),
587 iftree = print_tree(self.ifaces))
588
589 def __iter__(self):
590 """
591 Performs depth-first traversal of the interface tree this interface is the
592 root of. Does not filter out repeats.
593 """
594 for i in self.ifaces:
595 yield i
596 yield from i
597
598 def __str__(self):
599 """
600 Print the smali code of this interface.
601 """
602 s_ifaces = '\n'.join(map(lambda a: self.IMPLEMENTS_TEMPLATE.format(iface_name = a.get_name()),
603 self.ifaces))
604 j_ifaces = ', '.join(map(lambda a: a.get_name(), self.ifaces))
605 if self.is_default():
606 super_template = self.SUPER_CALL_TEMPLATE
607 super_calls ="\n".join(super_template.format(iface_name = iface.get_name(),
608 class_name = self.get_name(),
609 tree = self.get_tree()) for iface in self.ifaces)
610 funcs = self.DEFAULT_FUNC_TEMPLATE.format(super_calls = super_calls)
611 elif self.is_abstract():
612 funcs = self.ABSTRACT_FUNC_TEMPLATE.format()
613 else:
614 funcs = ""
615 return self.TEST_INTERFACE_TEMPLATE.format(copyright = get_copyright('smali'),
616 implements_spec = s_ifaces,
617 extends = "extends" if len(self.ifaces) else "",
618 ifaces = j_ifaces,
619 func = funcs,
620 tree = self.get_tree(),
621 class_name = self.class_name)
622
623def print_tree(ifaces):
624 """
625 Prints a list of iface trees
626 """
627 return " ".join(i.get_tree() for i in ifaces)
628
629# The deduplicated output of subtree_sizes for each size up to
630# MAX_LEAF_IFACE_PER_OBJECT.
631SUBTREES = [set(tuple(sorted(l)) for l in subtree_sizes(i))
632 for i in range(MAX_IFACE_DEPTH + 1)]
633
634def create_test_classes():
635 """
636 Yield all the test classes with the different interface trees
637 """
638 for num in range(1, MAX_IFACE_DEPTH + 1):
639 for split in SUBTREES[num]:
640 ifaces = []
641 for sub in split:
642 ifaces.append(list(create_interface_trees(sub)))
643 for supers in itertools.product(*ifaces):
644 yield TestClass(supers)
645
646def create_interface_trees(num):
647 """
648 Yield all the interface trees up to 'num' depth.
649 """
650 if num == 0:
651 for iftype in InterfaceType:
652 yield TestInterface(tuple(), iftype)
653 return
654 for split in SUBTREES[num]:
655 ifaces = []
656 for sub in split:
657 ifaces.append(list(create_interface_trees(sub)))
658 for supers in itertools.product(*ifaces):
659 for iftype in InterfaceType:
660 yield TestInterface(supers, iftype)
661
662def create_all_test_files():
663 """
664 Creates all the objects representing the files in this test. They just need to
665 be dumped.
666 """
667 mc = MainClass()
668 classes = {mc}
669 for clazz in create_test_classes():
670 classes.add(clazz)
671 for i in clazz:
672 classes.add(i)
673 mc.add_test(clazz)
674 return mc, classes
675
676def main(argv):
677 smali_dir = Path(argv[1])
678 if not smali_dir.exists() or not smali_dir.is_dir():
679 print("{} is not a valid smali dir".format(smali_dir), file=sys.stderr)
680 sys.exit(1)
681 expected_txt = Path(argv[2])
682 mainclass, all_files = create_all_test_files()
683 with expected_txt.open('w') as out:
684 print(mainclass.get_expected(), file=out)
685 for f in all_files:
686 f.dump(smali_dir)
687
688if __name__ == '__main__':
689 main(sys.argv)