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 = 2
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)
690