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 Java test files for test 961.
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 functools import total_ordering
37import itertools
38import string
39
40# The max depth the type tree can have. Includes the class object in the tree.
41# Increasing this increases the number of generated files significantly. This
42# value was chosen as it is fairly quick to run and very comprehensive, checking
43# every possible interface tree up to 5 layers deep.
44MAX_IFACE_DEPTH = 5
45
46class MainClass(mixins.DumpMixin, mixins.Named, mixins.JavaFileMixin):
47  """
48  A Main.java file containing the Main class and the main function. It will run
49  all the test functions we have.
50  """
51
52  MAIN_CLASS_TEMPLATE = """{copyright}
53class Main {{
54{test_groups}
55{main_func}
56}}
57"""
58
59  MAIN_FUNCTION_TEMPLATE = """
60  public static void main(String[] args) {{
61    {test_group_invoke}
62  }}
63"""
64
65  TEST_GROUP_INVOKE_TEMPLATE = """
66    {test_name}();
67"""
68
69  def __init__(self):
70    """
71    Initialize this MainClass. We start out with no tests.
72    """
73    self.tests = set()
74
75  def get_expected(self):
76    """
77    Get the expected output of this test.
78    """
79    all_tests = sorted(self.tests)
80    return filter_blanks("\n".join(a.get_expected() for a in all_tests))
81
82  def add_test(self, ty):
83    """
84    Add a test for the concrete type 'ty'
85    """
86    self.tests.add(Func(ty))
87
88  def get_name(self):
89    """
90    Get the name of this class
91    """
92    return "Main"
93
94  def __str__(self):
95    """
96    Print the MainClass java code.
97    """
98    all_tests = sorted(self.tests)
99    test_invoke = ""
100    test_groups = ""
101    for t in all_tests:
102      test_groups += str(t)
103    for t in all_tests:
104      test_invoke += self.TEST_GROUP_INVOKE_TEMPLATE.format(test_name=t.get_name())
105    main_func = self.MAIN_FUNCTION_TEMPLATE.format(test_group_invoke=test_invoke)
106
107    return self.MAIN_CLASS_TEMPLATE.format(copyright = get_copyright("java"),
108                                           test_groups = test_groups,
109                                           main_func = main_func)
110
111class Func(mixins.Named, mixins.NameComparableMixin):
112  """
113  A function that tests the functionality of a concrete type. Should only be
114  constructed by MainClass.add_test.
115  """
116
117  TEST_FUNCTION_TEMPLATE = """
118  public static void {fname}() {{
119    try {{
120      {farg} v = new {farg}();
121        System.out.printf("%s calls default method on %s\\n",
122                          v.CalledClassName(),
123                          v.CalledInterfaceName());
124        return;
125    }} catch (Error e) {{
126      e.printStackTrace(System.out);
127      return;
128    }}
129  }}
130"""
131
132  def __init__(self, farg):
133    """
134    Initialize a test function for the given argument
135    """
136    self.farg = farg
137
138  def get_expected(self):
139    """
140    Get the expected output calling this function.
141    """
142    return "{tree} calls default method on {iface_tree}".format(
143        tree = self.farg.get_tree(), iface_tree = self.farg.get_called().get_tree())
144
145  def get_name(self):
146    """
147    Get the name of this function
148    """
149    return "TEST_FUNC_{}".format(self.farg.get_name())
150
151  def __str__(self):
152    """
153    Print the java code of this function.
154    """
155    return self.TEST_FUNCTION_TEMPLATE.format(fname=self.get_name(), farg=self.farg.get_name())
156
157class TestClass(mixins.DumpMixin, mixins.Named, mixins.NameComparableMixin, mixins.JavaFileMixin):
158  """
159  A class that will be instantiated to test default method resolution order.
160  """
161
162  TEST_CLASS_TEMPLATE = """{copyright}
163public class {class_name} implements {iface_name} {{
164  public String CalledClassName() {{
165    return "{tree}";
166  }}
167}}
168"""
169
170  def __init__(self, iface):
171    """
172    Initialize this test class which implements the given interface
173    """
174    self.iface = iface
175    self.class_name = "CLASS_"+gensym()
176
177  def get_name(self):
178    """
179    Get the name of this class
180    """
181    return self.class_name
182
183  def get_tree(self):
184    """
185    Print out a representation of the type tree of this class
186    """
187    return "[{class_name} {iface_tree}]".format(class_name = self.class_name,
188                                                iface_tree = self.iface.get_tree())
189
190  def __iter__(self):
191    """
192    Step through all interfaces implemented transitively by this class
193    """
194    yield self.iface
195    yield from self.iface
196
197  def get_called(self):
198    """
199    Get the interface whose default method would be called when calling the
200    CalledInterfaceName function.
201    """
202    all_ifaces = set(iface for iface in self if iface.default)
203    for i in all_ifaces:
204      if all(map(lambda j: i not in j.get_super_types(), all_ifaces)):
205        return i
206    raise Exception("UNREACHABLE! Unable to find default method!")
207
208  def __str__(self):
209    """
210    Print the java code of this class.
211    """
212    return self.TEST_CLASS_TEMPLATE.format(copyright = get_copyright('java'),
213                                           iface_name = self.iface.get_name(),
214                                           tree = self.get_tree(),
215                                           class_name = self.class_name)
216
217class TestInterface(mixins.DumpMixin, mixins.Named, mixins.NameComparableMixin, mixins.JavaFileMixin):
218  """
219  An interface that will be used to test default method resolution order.
220  """
221
222  TEST_INTERFACE_TEMPLATE = """{copyright}
223public interface {class_name} {extends} {ifaces} {{
224  public String CalledClassName();
225
226{funcs}
227}}
228"""
229
230  DEFAULT_FUNC_TEMPLATE = """
231  public default String CalledInterfaceName() {{
232    return "{tree}";
233  }}
234"""
235
236  def __init__(self, ifaces, default):
237    """
238    Initialize interface with the given super-interfaces
239    """
240    self.ifaces = sorted(ifaces)
241    self.default = default
242    end = "_DEFAULT" if default else ""
243    self.class_name = "INTERFACE_"+gensym()+end
244
245  def get_super_types(self):
246    """
247    Returns a set of all the supertypes of this interface
248    """
249    return set(i2 for i2 in self)
250
251  def get_name(self):
252    """
253    Get the name of this class
254    """
255    return self.class_name
256
257  def get_tree(self):
258    """
259    Print out a representation of the type tree of this class
260    """
261    return "[{class_name} {iftree}]".format(class_name = self.get_name(),
262                                            iftree = print_tree(self.ifaces))
263
264  def __iter__(self):
265    """
266    Performs depth-first traversal of the interface tree this interface is the
267    root of. Does not filter out repeats.
268    """
269    for i in self.ifaces:
270      yield i
271      yield from i
272
273  def __str__(self):
274    """
275    Print the java code of this interface.
276    """
277    j_ifaces = " "
278    for i in self.ifaces:
279      j_ifaces += " {},".format(i.get_name())
280    j_ifaces = j_ifaces[0:-1]
281    if self.default:
282      funcs = self.DEFAULT_FUNC_TEMPLATE.format(ifaces = j_ifaces,
283                                                tree = self.get_tree(),
284                                                class_name = self.class_name)
285    else:
286      funcs = ""
287    return self.TEST_INTERFACE_TEMPLATE.format(copyright = get_copyright('java'),
288                                               extends = "extends" if len(self.ifaces) else "",
289                                               ifaces = j_ifaces,
290                                               funcs = funcs,
291                                               tree = self.get_tree(),
292                                               class_name = self.class_name)
293
294def print_tree(ifaces):
295  """
296  Prints a list of iface trees
297  """
298  return " ".join(i.get_tree() for i in  ifaces)
299
300# The deduplicated output of subtree_sizes for each size up to
301# MAX_LEAF_IFACE_PER_OBJECT.
302SUBTREES = [set(tuple(sorted(l)) for l in subtree_sizes(i))
303            for i in range(MAX_IFACE_DEPTH + 1)]
304
305def create_interface_trees():
306  """
307  Return all legal interface trees
308  """
309  def dump_supers(s):
310    """
311    Does depth first traversal of all the interfaces in the list.
312    """
313    for i in s:
314      yield i
315      yield from i
316
317  def create_interface_trees_inner(num, allow_default):
318    for split in SUBTREES[num]:
319      ifaces = []
320      for sub in split:
321        if sub == 1:
322          ifaces.append([TestInterface([], allow_default)])
323          if allow_default:
324            ifaces[-1].append(TestInterface([], False))
325        else:
326          ifaces.append(list(create_interface_trees_inner(sub, allow_default)))
327      for supers in itertools.product(*ifaces):
328        all_supers = sorted(set(dump_supers(supers)) - set(supers))
329        for i in range(len(all_supers) + 1):
330          for combo in itertools.combinations(all_supers, i):
331            yield TestInterface(list(combo) + list(supers), allow_default)
332      if allow_default:
333        for i in range(len(split)):
334          ifaces = []
335          for sub, cs in zip(split, itertools.count()):
336            if sub == 1:
337              ifaces.append([TestInterface([], i == cs)])
338            else:
339              ifaces.append(list(create_interface_trees_inner(sub, i == cs)))
340          for supers in itertools.product(*ifaces):
341            all_supers = sorted(set(dump_supers(supers)) - set(supers))
342            for i in range(len(all_supers) + 1):
343              for combo in itertools.combinations(all_supers, i):
344                yield TestInterface(list(combo) + list(supers), False)
345
346  for num in range(1, MAX_IFACE_DEPTH):
347    yield from create_interface_trees_inner(num, True)
348
349def create_all_test_files():
350  """
351  Creates all the objects representing the files in this test. They just need to
352  be dumped.
353  """
354  mc = MainClass()
355  classes = {mc}
356  for tree in create_interface_trees():
357    classes.add(tree)
358    for i in tree:
359      classes.add(i)
360    test_class = TestClass(tree)
361    mc.add_test(test_class)
362    classes.add(test_class)
363  return mc, classes
364
365def main(argv):
366  java_dir = Path(argv[1])
367  if not java_dir.exists() or not java_dir.is_dir():
368    print("{} is not a valid java dir".format(java_dir), file=sys.stderr)
369    sys.exit(1)
370  expected_txt = Path(argv[2])
371  mainclass, all_files = create_all_test_files()
372  with expected_txt.open('w') as out:
373    print(mainclass.get_expected(), file=out)
374  for f in all_files:
375    f.dump(java_dir)
376
377if __name__ == '__main__':
378  main(sys.argv)
379