1 #!/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 """ 18 Generate Smali test files for test 967. 19 """ 20 21 import os 22 import sys 23 from pathlib import Path 24 25 BUILD_TOP = os.getenv("ANDROID_BUILD_TOP") 26 if 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. 31 sys.path.append(str(Path(BUILD_TOP)/"art"/"test"/"utils"/"python")) 32 33 from testgen.utils import get_copyright, subtree_sizes, gensym, filter_blanks 34 import testgen.mixins as mixins 35 36 from enum import Enum 37 from functools import total_ordering 38 import itertools 39 import string 40 41 # The max depth the type tree can have. 42 MAX_IFACE_DEPTH = 2 43 44 class 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 129 class 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 206 class 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 225 class 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 374 class 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 396 class 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 432 CONFLICT_TYPE = ConflictInterface() 433 434 class 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 623 def 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. 631 SUBTREES = [set(tuple(sorted(l)) for l in subtree_sizes(i)) 632 for i in range(MAX_IFACE_DEPTH + 1)] 633 634 def 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 646 def 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 662 def 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 676 def 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 688 if __name__ == '__main__': 689 main(sys.argv) 690