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