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""" 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# System.out.printf("{tree} calls %s\\n", v.getName()); 147# return; 148# }} catch (AbstractMethodError e) {{ 149# System.out.println("{tree} threw AbstractMethodError"); 150# }} catch (NoSuchMethodError e) {{ 151# System.out.println("{tree} threw NoSuchMethodError"); 152# }} catch (IncompatibleClassChangeError e) {{ 153# System.out.println("{tree} threw IncompatibleClassChangeError"); 154# }} catch (Throwable e) {{ 155# e.printStackTrace(System.out); 156# return; 157# }} 158# }} 159.method public static {fname}()V 160 .locals 7 161 sget-object v4, Ljava/lang/System;->out:Ljava/io/PrintStream; 162 163 :new_{fname}_try_start 164 new-instance v0, L{farg}; 165 invoke-direct {{v0}}, L{farg};-><init>()V 166 goto :call_{fname}_try_start 167 :new_{fname}_try_end 168 .catch Ljava/lang/Throwable; {{:new_{fname}_try_start .. :new_{fname}_try_end}} :new_error_{fname}_start 169 :new_error_{fname}_start 170 move-exception v6 171 const-string v5, "Unexpected error occurred which creating {farg} instance" 172 invoke-virtual {{v4,v5}}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V 173 invoke-virtual {{v6,v4}}, Ljava/lang/Throwable;->printStackTrace(Ljava/io/PrintStream;)V 174 return-void 175 :call_{fname}_try_start 176 const/4 v1, 1 177 new-array v2,v1, [Ljava/lang/Object; 178 const/4 v1, 0 179 invoke-virtual {{v0}}, L{farg};->getName()Ljava/lang/String; 180 move-result-object v3 181 aput-object v3,v2,v1 182 183 const-string v5, "{tree} calls %s\\n" 184 185 invoke-virtual {{v4,v5,v2}}, Ljava/io/PrintStream;->printf(Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream; 186 return-void 187 :call_{fname}_try_end 188 .catch Ljava/lang/AbstractMethodError; {{:call_{fname}_try_start .. :call_{fname}_try_end}} :AME_{fname}_start 189 .catch Ljava/lang/NoSuchMethodError; {{:call_{fname}_try_start .. :call_{fname}_try_end}} :NSME_{fname}_start 190 .catch Ljava/lang/IncompatibleClassChangeError; {{:call_{fname}_try_start .. :call_{fname}_try_end}} :ICCE_{fname}_start 191 .catch Ljava/lang/Throwable; {{:call_{fname}_try_start .. :call_{fname}_try_end}} :error_{fname}_start 192 :AME_{fname}_start 193 const-string v5, "{tree} threw AbstractMethodError" 194 invoke-virtual {{v4,v5}}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V 195 return-void 196 :NSME_{fname}_start 197 const-string v5, "{tree} threw NoSuchMethodError" 198 invoke-virtual {{v4,v5}}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V 199 return-void 200 :ICCE_{fname}_start 201 const-string v5, "{tree} threw IncompatibleClassChangeError" 202 invoke-virtual {{v4,v5}}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V 203 return-void 204 :error_{fname}_start 205 move-exception v6 206 invoke-virtual {{v6,v4}}, Ljava/lang/Throwable;->printStackTrace(Ljava/io/PrintStream;)V 207 return-void 208.end method 209""" 210 211 NSME_RESULT_TEMPLATE = "{tree} threw NoSuchMethodError" 212 ICCE_RESULT_TEMPLATE = "{tree} threw IncompatibleClassChangeError" 213 AME_RESULT_TEMPLATE = "{tree} threw AbstractMethodError" 214 NORMAL_RESULT_TEMPLATE = "{tree} calls {result}" 215 216 def __init__(self, farg): 217 """ 218 Initialize a test function for the given argument 219 """ 220 self.farg = farg 221 222 def get_expected(self): 223 """ 224 Get the expected output calling this function. 225 """ 226 exp = self.farg.get_called() 227 if exp.is_empty(): 228 return self.NSME_RESULT_TEMPLATE.format(tree = self.farg.get_tree()) 229 elif exp.is_abstract(): 230 return self.AME_RESULT_TEMPLATE.format(tree = self.farg.get_tree()) 231 elif exp.is_conflict(): 232 return self.ICCE_RESULT_TEMPLATE.format(tree = self.farg.get_tree()) 233 else: 234 assert exp.is_default() 235 return self.NORMAL_RESULT_TEMPLATE.format(tree = self.farg.get_tree(), 236 result = exp.get_tree()) 237 238 def get_name(self): 239 """ 240 Get the name of this function 241 """ 242 return "TEST_FUNC_{}".format(self.farg.get_name()) 243 244 def __str__(self): 245 """ 246 Print the smali code of this function. 247 """ 248 return self.TEST_FUNCTION_TEMPLATE.format(tree = self.farg.get_tree(), 249 fname = self.get_name(), 250 farg = self.farg.get_name()) 251 252class TestClass(mixins.DumpMixin, mixins.Named, mixins.NameComparableMixin, mixins.SmaliFileMixin): 253 """ 254 A class that will be instantiated to test default method resolution order. 255 """ 256 257 TEST_CLASS_TEMPLATE = """{copyright} 258 259.class public L{class_name}; 260.super Ljava/lang/Object; 261.implements L{iface_name}; 262 263# public class {class_name} implements {iface_name} {{ 264 265.method public constructor <init>()V 266 .registers 1 267 invoke-direct {{p0}}, Ljava/lang/Object;-><init>()V 268 return-void 269.end method 270 271{funcs} 272 273# }} 274""" 275 276 def __init__(self, iface): 277 """ 278 Initialize this test class which implements the given interface 279 """ 280 self.iface = iface 281 self.class_name = "CLASS_"+gensym() 282 283 def get_name(self): 284 """ 285 Get the name of this class 286 """ 287 return self.class_name 288 289 def get_tree(self): 290 """ 291 Print out a representation of the type tree of this class 292 """ 293 return "[{class_name} {iface_tree}]".format(class_name = self.class_name, 294 iface_tree = self.iface.get_tree()) 295 296 def __iter__(self): 297 """ 298 Step through all interfaces implemented transitively by this class 299 """ 300 yield self.iface 301 yield from self.iface 302 303 def get_called(self): 304 """ 305 Returns the interface that will be called when the method on this class is invoked or 306 CONFLICT_TYPE if there is no interface that will be called. 307 """ 308 return self.iface.get_called() 309 310 def __str__(self): 311 """ 312 Print the smali code of this class. 313 """ 314 return self.TEST_CLASS_TEMPLATE.format(copyright = get_copyright('smali'), 315 iface_name = self.iface.get_name(), 316 tree = self.get_tree(), 317 class_name = self.class_name, 318 funcs = "") 319 320class InterfaceType(Enum): 321 """ 322 An enumeration of all the different types of interfaces we can have. 323 324 default: It has a default method 325 abstract: It has a method declared but not defined 326 empty: It does not have the method 327 """ 328 default = 0 329 abstract = 1 330 empty = 2 331 332 def get_suffix(self): 333 if self == InterfaceType.default: 334 return "_DEFAULT" 335 elif self == InterfaceType.abstract: 336 return "_ABSTRACT" 337 elif self == InterfaceType.empty: 338 return "_EMPTY" 339 else: 340 raise TypeError("Interface type had illegal value.") 341 342class ConflictInterface: 343 """ 344 A singleton representing a conflict of default methods. 345 """ 346 347 def is_conflict(self): 348 """ 349 Returns true if this is a conflict interface and calling the method on this interface will 350 result in an IncompatibleClassChangeError. 351 """ 352 return True 353 354 def is_abstract(self): 355 """ 356 Returns true if this is an abstract interface and calling the method on this interface will 357 result in an AbstractMethodError. 358 """ 359 return False 360 361 def is_empty(self): 362 """ 363 Returns true if this is an abstract interface and calling the method on this interface will 364 result in a NoSuchMethodError. 365 """ 366 return False 367 368 def is_default(self): 369 """ 370 Returns true if this is a default interface and calling the method on this interface will 371 result in a method actually being called. 372 """ 373 return False 374 375CONFLICT_TYPE = ConflictInterface() 376 377class TestInterface(mixins.DumpMixin, mixins.Named, mixins.NameComparableMixin, mixins.SmaliFileMixin): 378 """ 379 An interface that will be used to test default method resolution order. 380 """ 381 382 TEST_INTERFACE_TEMPLATE = """{copyright} 383.class public abstract interface L{class_name}; 384.super Ljava/lang/Object; 385{implements_spec} 386 387# public interface {class_name} {extends} {ifaces} {{ 388 389{funcs} 390 391# }} 392""" 393 394 DEFAULT_FUNC_TEMPLATE = """ 395# public default String getName() {{ 396# return "{tree}"; 397# }} 398.method public getName()Ljava/lang/String; 399 .locals 1 400 const-string v0, "{tree}" 401 return-object v0 402.end method 403""" 404 405 ABSTRACT_FUNC_TEMPLATE = """ 406# public String getName(); 407.method public abstract getName()Ljava/lang/String; 408.end method 409""" 410 411 EMPTY_FUNC_TEMPLATE = """""" 412 413 IMPLEMENTS_TEMPLATE = """ 414.implements L{iface_name}; 415""" 416 417 def __init__(self, ifaces, iface_type, full_name = None): 418 """ 419 Initialize interface with the given super-interfaces 420 """ 421 self.ifaces = sorted(ifaces) 422 self.iface_type = iface_type 423 if full_name is None: 424 end = self.iface_type.get_suffix() 425 self.class_name = "INTERFACE_"+gensym()+end 426 else: 427 self.class_name = full_name 428 429 def get_specific_version(self, v): 430 """ 431 Returns a copy of this interface of the given type for use in partial compilation. 432 """ 433 return TestInterface(self.ifaces, v, full_name = self.class_name) 434 435 def get_super_types(self): 436 """ 437 Returns a set of all the supertypes of this interface 438 """ 439 return set(i2 for i2 in self) 440 441 def is_conflict(self): 442 """ 443 Returns true if this is a conflict interface and calling the method on this interface will 444 result in an IncompatibleClassChangeError. 445 """ 446 return False 447 448 def is_abstract(self): 449 """ 450 Returns true if this is an abstract interface and calling the method on this interface will 451 result in an AbstractMethodError. 452 """ 453 return self.iface_type == InterfaceType.abstract 454 455 def is_empty(self): 456 """ 457 Returns true if this is an abstract interface and calling the method on this interface will 458 result in a NoSuchMethodError. 459 """ 460 return self.iface_type == InterfaceType.empty 461 462 def is_default(self): 463 """ 464 Returns true if this is a default interface and calling the method on this interface will 465 result in a method actually being called. 466 """ 467 return self.iface_type == InterfaceType.default 468 469 def get_called(self): 470 """ 471 Returns the interface that will be called when the method on this class is invoked or 472 CONFLICT_TYPE if there is no interface that will be called. 473 """ 474 if not self.is_empty() or len(self.ifaces) == 0: 475 return self 476 else: 477 best = self 478 for super_iface in self.ifaces: 479 super_best = super_iface.get_called() 480 if super_best.is_conflict(): 481 return CONFLICT_TYPE 482 elif best.is_default(): 483 if super_best.is_default(): 484 return CONFLICT_TYPE 485 elif best.is_abstract(): 486 if super_best.is_default(): 487 best = super_best 488 else: 489 assert best.is_empty() 490 best = super_best 491 return best 492 493 def get_name(self): 494 """ 495 Get the name of this class 496 """ 497 return self.class_name 498 499 def get_tree(self): 500 """ 501 Print out a representation of the type tree of this class 502 """ 503 return "[{class_name} {iftree}]".format(class_name = self.get_name(), 504 iftree = print_tree(self.ifaces)) 505 506 def __iter__(self): 507 """ 508 Performs depth-first traversal of the interface tree this interface is the 509 root of. Does not filter out repeats. 510 """ 511 for i in self.ifaces: 512 yield i 513 yield from i 514 515 def __str__(self): 516 """ 517 Print the smali code of this interface. 518 """ 519 s_ifaces = " " 520 j_ifaces = " " 521 for i in self.ifaces: 522 s_ifaces += self.IMPLEMENTS_TEMPLATE.format(iface_name = i.get_name()) 523 j_ifaces += " {},".format(i.get_name()) 524 j_ifaces = j_ifaces[0:-1] 525 if self.is_default(): 526 funcs = self.DEFAULT_FUNC_TEMPLATE.format(tree = self.get_tree()) 527 elif self.is_abstract(): 528 funcs = self.ABSTRACT_FUNC_TEMPLATE.format() 529 else: 530 funcs = "" 531 return self.TEST_INTERFACE_TEMPLATE.format(copyright = get_copyright('smali'), 532 implements_spec = s_ifaces, 533 extends = "extends" if len(self.ifaces) else "", 534 ifaces = j_ifaces, 535 funcs = funcs, 536 tree = self.get_tree(), 537 class_name = self.class_name) 538 539def print_tree(ifaces): 540 """ 541 Prints a list of iface trees 542 """ 543 return " ".join(i.get_tree() for i in ifaces) 544 545# The deduplicated output of subtree_sizes for each size up to 546# MAX_LEAF_IFACE_PER_OBJECT. 547SUBTREES = [set(tuple(sorted(l)) for l in subtree_sizes(i)) 548 for i in range(MAX_IFACE_DEPTH + 1)] 549 550def create_test_classes(): 551 """ 552 Yield all the test classes with the different interface trees 553 """ 554 for num in range(1, MAX_IFACE_DEPTH + 1): 555 for iface in create_interface_trees(num): 556 yield TestClass(iface) 557 558def create_interface_trees(num): 559 """ 560 Yield all the interface trees up to 'num' depth. 561 """ 562 if num == 0: 563 for iftype in InterfaceType: 564 yield TestInterface(tuple(), iftype) 565 return 566 for split in SUBTREES[num]: 567 ifaces = [] 568 for sub in split: 569 ifaces.append(list(create_interface_trees(sub))) 570 yield TestInterface(tuple(), InterfaceType.default) 571 for supers in itertools.product(*ifaces): 572 for iftype in InterfaceType: 573 if iftype == InterfaceType.default: 574 # We can just stop at defaults. We have other tests that a default can override an 575 # abstract and this cuts down on the number of cases significantly, improving speed of 576 # this test. 577 continue 578 yield TestInterface(supers, iftype) 579 580def create_all_test_files(): 581 """ 582 Creates all the objects representing the files in this test. They just need to 583 be dumped. 584 """ 585 mc = MainClass() 586 classes = {mc} 587 for clazz in create_test_classes(): 588 classes.add(clazz) 589 for i in clazz: 590 classes.add(i) 591 mc.add_test(clazz) 592 return mc, classes 593 594def main(argv): 595 smali_dir = Path(argv[1]) 596 if not smali_dir.exists() or not smali_dir.is_dir(): 597 print("{} is not a valid smali dir".format(smali_dir), file=sys.stderr) 598 sys.exit(1) 599 expected_txt = Path(argv[2]) 600 mainclass, all_files = create_all_test_files() 601 with expected_txt.open('w') as out: 602 print(mainclass.get_expected(), file=out) 603 for f in all_files: 604 f.dump(smali_dir) 605 606if __name__ == '__main__': 607 main(sys.argv) 608