1#!/usr/bin/python3
2#
3# Copyright (C) 2018 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 712-varhandle-invocations
19"""
20
21from enum import Enum
22from pathlib import Path
23from random import Random
24from string import Template
25
26import io
27import re
28import sys
29
30class JavaType(object):
31    def __init__(self, name, examples, supports_bitwise=False, supports_numeric=False):
32        self.name=name
33        self.examples=examples
34        self.supports_bitwise=supports_bitwise
35        self.supports_numeric=supports_numeric
36
37    def is_value_type(self):
38        return False
39
40    def __repr__(self):
41        return self.name
42
43    def __str__(self):
44        return self.name
45
46class ValueType(JavaType):
47    def __init__(self, name, boxed_type, examples, ordinal=-1, width=-1, supports_bitwise=True, supports_numeric=True):
48        JavaType.__init__(self, name, examples, supports_bitwise, supports_numeric)
49        self.ordinal=ordinal
50        self.width=width
51        self.boxed_type=boxed_type
52
53    def boxing_method(self):
54        return self.boxed_type + ".valueOf"
55
56    def unboxing_method(self):
57        return self.name + "Value"
58
59    def is_value_type(self):
60        return True
61
62    def __eq__(self, other):
63        return self.ordinal == other.ordinal
64
65    def __hash__(self):
66        return self.ordinal
67
68    def __le__(self, other):
69        return self.ordinal < other.ordinal
70
71    def __repr__(self):
72        return self.name
73
74    def __str__(self):
75        return self.name
76
77BOOLEAN_TYPE = ValueType("boolean", "Boolean", [ "true", "false" ], ordinal = 0, width = 1, supports_numeric=False)
78BYTE_TYPE=ValueType("byte", "Byte", [ "(byte) -128", "(byte) -61", "(byte) 7", "(byte) 127", "(byte) 33" ], ordinal=1, width=1)
79SHORT_TYPE=ValueType("short", "Short", [ "(short) -32768", "(short) -384", "(short) 32767", "(short) 0xaa55" ], ordinal=2, width=2)
80CHAR_TYPE=ValueType("char", "Character", [ r"'A'", r"'#'", r"'$'", r"'Z'", r"'t'", r"'c'" ], ordinal=3, width=2)
81INT_TYPE=ValueType("int", "Integer", [ "-0x01234567", "0x7f6e5d4c", "0x12345678", "0x10215220", "42" ], ordinal=4, width=4)
82LONG_TYPE=ValueType("long", "Long", [ "-0x0123456789abcdefl", "0x789abcdef0123456l", "0xfedcba9876543210l" ], ordinal=5, width=8)
83FLOAT_TYPE=ValueType("float", "Float", [ "-7.77e23f", "1.234e-17f", "3.40e36f", "-8.888e3f", "4.442e11f" ], ordinal=6, width=4, supports_bitwise=False)
84DOUBLE_TYPE=ValueType("double", "Double", [ "-1.0e-200", "1.11e200", "3.141", "1.1111", "6.022e23", "6.626e-34" ], ordinal=7, width=4, supports_bitwise=False)
85
86VALUE_TYPES = { BOOLEAN_TYPE, BYTE_TYPE, SHORT_TYPE, CHAR_TYPE, INT_TYPE, LONG_TYPE, FLOAT_TYPE, DOUBLE_TYPE }
87
88WIDENING_CONVERSIONS = {
89    BOOLEAN_TYPE : set(),
90    BYTE_TYPE : { SHORT_TYPE, INT_TYPE, LONG_TYPE, FLOAT_TYPE, DOUBLE_TYPE },
91    SHORT_TYPE : { INT_TYPE, LONG_TYPE, FLOAT_TYPE, DOUBLE_TYPE },
92    CHAR_TYPE : { INT_TYPE, LONG_TYPE, FLOAT_TYPE, DOUBLE_TYPE },
93    INT_TYPE : { LONG_TYPE, FLOAT_TYPE, DOUBLE_TYPE },
94    LONG_TYPE : { FLOAT_TYPE, DOUBLE_TYPE },
95    FLOAT_TYPE : { DOUBLE_TYPE },
96    DOUBLE_TYPE : set()
97}
98
99def types_that_widen_to(var_type):
100    types_that_widen = { var_type }
101    for src_type in WIDENING_CONVERSIONS:
102        if var_type in WIDENING_CONVERSIONS[src_type]:
103            types_that_widen.add(src_type)
104    return types_that_widen
105
106class VarHandleKind(object):
107    ALL_SUPPORTED_TYPES = VALUE_TYPES
108    VIEW_SUPPORTED_TYPES = list(filter(lambda x : x.width >= 2, ALL_SUPPORTED_TYPES))
109
110    def __init__(self, name, imports=[], declarations=[], lookup='', coordinates=[], get_value='', may_throw_read_only=False):
111        self.name = name
112        self.imports = imports
113        self.declarations = declarations
114        self.lookup = lookup
115        self.coordinates = coordinates
116        self.get_value_ = get_value
117        self.may_throw_read_only = may_throw_read_only
118
119    def get_name(self):
120        return self.name
121
122    def get_coordinates(self):
123        return self.coordinates
124
125    def get_field_declarations(self, dictionary):
126        return list(map(lambda d: Template(d).safe_substitute(dictionary), self.declarations))
127
128    def get_imports(self):
129        return self.imports
130
131    def get_lookup(self, dictionary):
132        return Template(self.lookup).safe_substitute(dictionary)
133
134    def get_supported_types(self):
135        return VarHandleKind.VIEW_SUPPORTED_TYPES if self.is_view() else VarHandleKind.ALL_SUPPORTED_TYPES
136
137    def is_view(self):
138        return "View" in self.name
139
140    def get_value(self, dictionary):
141        return Template(self.get_value_).safe_substitute(dictionary)
142
143FIELD_VAR_HANDLE = VarHandleKind("Field",
144                                 [
145                                     'java.lang.invoke.MethodHandles',
146                                     'java.lang.invoke.VarHandle'
147                                 ],
148                                 [
149                                     "${var_type} field = ${initial_value}"
150                                 ],
151                                 'MethodHandles.lookup().findVarHandle(${test_class}.class, "field", ${var_type}.class)',
152                                 [
153                                     'this'
154                                 ],
155                                 'field',
156                                 may_throw_read_only = False)
157
158FINAL_FIELD_VAR_HANDLE = VarHandleKind("FinalField",
159                                       [
160                                           'java.lang.invoke.MethodHandles',
161                                           'java.lang.invoke.VarHandle'
162                                       ],
163                                       [
164                                           "${var_type} field = ${initial_value}"
165                                       ],
166                                       'MethodHandles.lookup().findVarHandle(${test_class}.class, "field", ${var_type}.class)',
167                                       [
168                                           'this'
169                                       ],
170                                       'field',
171                                       may_throw_read_only = False)
172
173STATIC_FIELD_VAR_HANDLE = VarHandleKind("StaticField",
174                                        [
175                                            'java.lang.invoke.MethodHandles',
176                                            'java.lang.invoke.VarHandle'
177                                        ],
178                                        [
179                                            "static ${var_type} field = ${initial_value}"
180                                        ],
181                                        'MethodHandles.lookup().findStaticVarHandle(${test_class}.class, "field", ${var_type}.class)',
182                                        [],
183                                        'field',
184                                        may_throw_read_only = False)
185
186STATIC_FINAL_FIELD_VAR_HANDLE = VarHandleKind("StaticFinalField",
187                                              [
188                                                  'java.lang.invoke.MethodHandles',
189                                                  'java.lang.invoke.VarHandle'
190                                              ],
191                                              [
192                                                  "static ${var_type} field = ${initial_value}"
193                                              ],
194                                              'MethodHandles.lookup().findStaticVarHandle(${test_class}.class, "field", ${var_type}.class)',
195                                              [],
196                                              'field',
197                                              may_throw_read_only = False)
198
199ARRAY_ELEMENT_VAR_HANDLE = VarHandleKind("ArrayElement",
200                                         [
201                                             'java.lang.invoke.MethodHandles',
202                                             'java.lang.invoke.VarHandle'
203                                         ],
204                                         [
205                                             "${var_type}[] array = new ${var_type}[11]",
206                                             "int index = 3",
207                                             "{ array[index] = ${initial_value}; }"
208                                         ],
209                                         'MethodHandles.arrayElementVarHandle(${var_type}[].class)',
210                                         [ 'array', 'index'],
211                                         'array[index]',
212                                         may_throw_read_only = False)
213
214BYTE_ARRAY_LE_VIEW_VAR_HANDLE = VarHandleKind("ByteArrayViewLE",
215                                              [
216                                                  'java.lang.invoke.MethodHandles',
217                                                  'java.lang.invoke.VarHandle',
218                                                  'java.nio.ByteOrder'
219                                              ],
220                                              [
221                                                  "byte[] array = new byte[27]",
222                                                  "int index = 8",
223                                                  "{"
224                                                  "  index = VarHandleUnitTestHelpers.alignedOffset_${var_type}(array, index);"
225                                                  "  VarHandleUnitTestHelpers.setBytesAs_${var_type}(array, index, ${initial_value}, ByteOrder.LITTLE_ENDIAN);"
226                                                  "}"
227                                              ],
228                                              'MethodHandles.byteArrayViewVarHandle(${var_type}[].class, ByteOrder.LITTLE_ENDIAN)',
229                                              [
230                                                  'array',
231                                                  'index'
232                                              ],
233                                              'VarHandleUnitTestHelpers.getBytesAs_${var_type}(array, index, ByteOrder.LITTLE_ENDIAN)',
234                                              may_throw_read_only = False)
235
236BYTE_ARRAY_BE_VIEW_VAR_HANDLE = VarHandleKind("ByteArrayViewBE",
237                                              [
238                                                  'java.lang.invoke.MethodHandles',
239                                                  'java.lang.invoke.VarHandle',
240                                                  'java.nio.ByteOrder'
241                                              ],
242                                              [
243                                                  "byte[] array = new byte[27]",
244                                                  "int index = 8",
245                                                  "{"
246                                                  "  index = VarHandleUnitTestHelpers.alignedOffset_${var_type}(array, index);"
247                                                  "  VarHandleUnitTestHelpers.setBytesAs_${var_type}(array, index, ${initial_value}, ByteOrder.BIG_ENDIAN);"
248                                                  "}"
249                                              ],
250                                              'MethodHandles.byteArrayViewVarHandle(${var_type}[].class, ByteOrder.BIG_ENDIAN)',
251                                              [
252                                                  'array',
253                                                  'index'
254                                              ],
255                                              'VarHandleUnitTestHelpers.getBytesAs_${var_type}(array, index, ByteOrder.BIG_ENDIAN)',
256                                              may_throw_read_only = False)
257
258DIRECT_BYTE_BUFFER_LE_VIEW_VAR_HANDLE = VarHandleKind("DirectByteBufferViewLE",
259                                                      [
260                                                          'java.lang.invoke.MethodHandles',
261                                                          'java.lang.invoke.VarHandle',
262                                                          'java.nio.ByteBuffer',
263                                                          'java.nio.ByteOrder'
264                                                      ],
265                                                      [
266                                                          "ByteBuffer bb = ByteBuffer.allocateDirect(31)",
267                                                          "int index = 8",
268                                                          "{"
269                                                          "  index = VarHandleUnitTestHelpers.alignedOffset_${var_type}(bb, index);"
270                                                          "  VarHandleUnitTestHelpers.setBytesAs_${var_type}(bb, index, ${initial_value}, ByteOrder.LITTLE_ENDIAN);"
271                                                          "}"
272                                                      ],
273                                                      'MethodHandles.byteBufferViewVarHandle(${var_type}[].class, ByteOrder.LITTLE_ENDIAN)',
274                                                      [
275                                                          'bb',
276                                                          'index'
277                                                      ],
278                                                      'VarHandleUnitTestHelpers.getBytesAs_${var_type}(bb, index, ByteOrder.LITTLE_ENDIAN)',
279                                                      may_throw_read_only = False)
280
281DIRECT_BYTE_BUFFER_BE_VIEW_VAR_HANDLE = VarHandleKind("DirectByteBufferViewBE",
282                                                      [
283                                                          'java.lang.invoke.MethodHandles',
284                                                          'java.lang.invoke.VarHandle',
285                                                          'java.nio.ByteBuffer',
286                                                          'java.nio.ByteOrder'
287                                                      ],
288                                                      [
289                                                          "ByteBuffer bb = ByteBuffer.allocateDirect(31)",
290                                                          "int index = 8",
291                                                          "{"
292                                                          "  index = VarHandleUnitTestHelpers.alignedOffset_${var_type}(bb, index);"
293                                                          "  VarHandleUnitTestHelpers.setBytesAs_${var_type}(bb, index, ${initial_value}, ByteOrder.BIG_ENDIAN);"
294                                                          "}"
295                                                      ],
296                                                      'MethodHandles.byteBufferViewVarHandle(${var_type}[].class, ByteOrder.BIG_ENDIAN)',
297                                                      [
298                                                          'bb',
299                                                          'index'
300                                                      ],
301                                                      'VarHandleUnitTestHelpers.getBytesAs_${var_type}(bb, index, ByteOrder.BIG_ENDIAN)',
302                                                      may_throw_read_only = False)
303
304HEAP_BYTE_BUFFER_LE_VIEW_VAR_HANDLE = VarHandleKind("HeapByteBufferViewLE",
305                                                    [
306                                                        'java.lang.invoke.MethodHandles',
307                                                        'java.lang.invoke.VarHandle',
308                                                        'java.nio.ByteBuffer',
309                                                        'java.nio.ByteOrder'
310                                                    ],
311                                                    [
312                                                        "byte[] array = new byte[36]",
313                                                        "int offset = 8",
314                                                        "ByteBuffer bb = ByteBuffer.wrap(array, offset, array.length - offset)",
315                                                        "int index = 8",
316                                                        "{"
317                                                        "  index = VarHandleUnitTestHelpers.alignedOffset_${var_type}(bb, index);"
318                                                        "  VarHandleUnitTestHelpers.setBytesAs_${var_type}(bb, index, ${initial_value}, ByteOrder.LITTLE_ENDIAN);"
319                                                        "}"
320                                                    ],
321                                                    'MethodHandles.byteBufferViewVarHandle(${var_type}[].class, ByteOrder.LITTLE_ENDIAN)',
322                                                    [
323                                                        'bb',
324                                                        'index'
325                                                    ],
326                                                    'VarHandleUnitTestHelpers.getBytesAs_${var_type}(bb, index, ByteOrder.LITTLE_ENDIAN)',
327                                                    may_throw_read_only = False)
328
329HEAP_BYTE_BUFFER_BE_VIEW_VAR_HANDLE = VarHandleKind("HeapByteBufferViewBE",
330                                                    [
331                                                        'java.lang.invoke.MethodHandles',
332                                                        'java.lang.invoke.VarHandle',
333                                                        'java.nio.ByteBuffer',
334                                                        'java.nio.ByteOrder'
335                                                    ],
336                                                    [
337                                                        "byte[] array = new byte[47]",
338                                                        "int offset = 8",
339                                                        "ByteBuffer bb = ByteBuffer.wrap(array, offset, array.length - offset)",
340                                                        "int index = 8",
341                                                        "{"
342                                                        "  index = VarHandleUnitTestHelpers.alignedOffset_${var_type}(bb, index);"
343                                                        "  VarHandleUnitTestHelpers.setBytesAs_${var_type}(bb, index, ${initial_value}, ByteOrder.BIG_ENDIAN);"
344                                                        "}"
345                                                    ],
346                                                    'MethodHandles.byteBufferViewVarHandle(${var_type}[].class, ByteOrder.BIG_ENDIAN)',
347                                                    [
348                                                        'bb',
349                                                        'index'
350                                                    ],
351                                                    'VarHandleUnitTestHelpers.getBytesAs_${var_type}(bb, index, ByteOrder.BIG_ENDIAN)',
352                                                    may_throw_read_only = False)
353
354HEAP_BYTE_BUFFER_RO_LE_VIEW_VAR_HANDLE = VarHandleKind("HeapByteBufferReadOnlyViewLE",
355                                                       [
356                                                           'java.lang.invoke.MethodHandles',
357                                                           'java.lang.invoke.VarHandle',
358                                                           'java.nio.ByteBuffer',
359                                                           'java.nio.ByteOrder',
360                                                           'java.nio.ReadOnlyBufferException'
361                                                       ],
362                                                       [
363                                                           "byte[] array = new byte[43]",
364                                                           "int index = 8",
365                                                           "ByteBuffer bb",
366                                                           "{"
367                                                           "  bb = ByteBuffer.wrap(array).asReadOnlyBuffer();"
368                                                           "  index = VarHandleUnitTestHelpers.alignedOffset_${var_type}(bb, index);"
369                                                           "  VarHandleUnitTestHelpers.setBytesAs_${var_type}(array, index, ${initial_value}, ByteOrder.LITTLE_ENDIAN);"
370                                                           "  bb = bb.asReadOnlyBuffer();"
371
372                                                           "}"
373                                                       ],
374                                                       'MethodHandles.byteBufferViewVarHandle(${var_type}[].class, ByteOrder.LITTLE_ENDIAN)',
375                                                       [
376                                                           'bb',
377                                                           'index'
378                                                       ],
379                                                       'VarHandleUnitTestHelpers.getBytesAs_${var_type}(bb, index, ByteOrder.LITTLE_ENDIAN)',
380                                                       may_throw_read_only = True)
381
382HEAP_BYTE_BUFFER_RO_BE_VIEW_VAR_HANDLE = VarHandleKind("HeapByteBufferReadOnlyViewBE",
383                                                       [
384                                                           'java.lang.invoke.MethodHandles',
385                                                           'java.lang.invoke.VarHandle',
386                                                           'java.nio.ByteBuffer',
387                                                           'java.nio.ByteOrder',
388                                                           'java.nio.ReadOnlyBufferException'
389                                                       ],
390                                                       [
391                                                           "byte[] array = new byte[29]",
392                                                           "int index",
393                                                           "ByteBuffer bb",
394                                                           "{"
395                                                           "  bb = ByteBuffer.wrap(array);"
396                                                           "  index = VarHandleUnitTestHelpers.alignedOffset_${var_type}(bb, 8);"
397                                                           "  VarHandleUnitTestHelpers.setBytesAs_${var_type}(array, index, ${initial_value}, ByteOrder.BIG_ENDIAN);"
398                                                           "  bb = bb.asReadOnlyBuffer();"
399                                                           "}"
400                                                       ],
401                                                       'MethodHandles.byteBufferViewVarHandle(${var_type}[].class, ByteOrder.BIG_ENDIAN)',
402                                                       [
403                                                           'bb',
404                                                           'index'
405                                                       ],
406                                                       'VarHandleUnitTestHelpers.getBytesAs_${var_type}(bb, index, ByteOrder.BIG_ENDIAN)',
407                                                       may_throw_read_only = True)
408
409ALL_FIELD_VAR_HANDLE_KINDS = [
410    FIELD_VAR_HANDLE,
411    FINAL_FIELD_VAR_HANDLE,
412    STATIC_FIELD_VAR_HANDLE,
413    STATIC_FINAL_FIELD_VAR_HANDLE
414]
415
416ALL_BYTE_VIEW_VAR_HANDLE_KINDS = [
417    BYTE_ARRAY_LE_VIEW_VAR_HANDLE,
418    BYTE_ARRAY_BE_VIEW_VAR_HANDLE,
419    DIRECT_BYTE_BUFFER_LE_VIEW_VAR_HANDLE,
420    DIRECT_BYTE_BUFFER_BE_VIEW_VAR_HANDLE,
421    HEAP_BYTE_BUFFER_LE_VIEW_VAR_HANDLE,
422    HEAP_BYTE_BUFFER_BE_VIEW_VAR_HANDLE,
423    HEAP_BYTE_BUFFER_RO_LE_VIEW_VAR_HANDLE,
424    HEAP_BYTE_BUFFER_RO_BE_VIEW_VAR_HANDLE
425]
426
427ALL_VAR_HANDLE_KINDS = ALL_FIELD_VAR_HANDLE_KINDS + [ ARRAY_ELEMENT_VAR_HANDLE ] + ALL_BYTE_VIEW_VAR_HANDLE_KINDS
428
429class AccessModeForm(Enum):
430    GET = 0
431    SET = 1
432    STRONG_COMPARE_AND_SET = 2
433    WEAK_COMPARE_AND_SET = 3
434    COMPARE_AND_EXCHANGE = 4
435    GET_AND_SET = 5
436    GET_AND_UPDATE_BITWISE = 6
437    GET_AND_UPDATE_NUMERIC = 7
438
439class VarHandleAccessor:
440    def __init__(self, method_name):
441        self.method_name = method_name
442        self.access_mode = self.get_access_mode(method_name)
443        self.access_mode_form = self.get_access_mode_form(method_name)
444
445    def get_return_type(self, var_type):
446        if self.access_mode_form == AccessModeForm.SET:
447            return None
448        elif (self.access_mode_form == AccessModeForm.STRONG_COMPARE_AND_SET or
449              self.access_mode_form == AccessModeForm.WEAK_COMPARE_AND_SET):
450            return BOOLEAN_TYPE
451        else:
452            return var_type
453
454    def get_number_of_var_type_arguments(self):
455        if self.access_mode_form == AccessModeForm.GET:
456            return 0
457        elif (self.access_mode_form == AccessModeForm.SET or
458              self.access_mode_form == AccessModeForm.GET_AND_SET or
459              self.access_mode_form == AccessModeForm.GET_AND_UPDATE_BITWISE or
460              self.access_mode_form == AccessModeForm.GET_AND_UPDATE_NUMERIC):
461            return 1
462        elif (self.access_mode_form == AccessModeForm.STRONG_COMPARE_AND_SET or
463              self.access_mode_form == AccessModeForm.WEAK_COMPARE_AND_SET or
464              self.access_mode_form == AccessModeForm.COMPARE_AND_EXCHANGE):
465            return 2
466        else:
467            raise ValueError(self.access_mode_form)
468
469    def is_read_only(self):
470        return self.access_mode_form == AccessModeForm.GET
471
472    def get_java_bitwise_operator(self):
473        if "BitwiseAnd" in self.method_name:
474            return "&"
475        elif "BitwiseOr" in self.method_name:
476            return "|"
477        elif "BitwiseXor" in self.method_name:
478            return "^"
479        raise ValueError(self.method_name)
480
481    def get_java_numeric_operator(self):
482        if "Add" in self.method_name:
483            return "+"
484        raise ValueError(self.method_name)
485
486    @staticmethod
487    def get_access_mode(accessor_method):
488        """Converts an access method name to AccessMode value. For example, getAndSet becomes GET_AND_SET"""
489        return re.sub('([A-Z])', r'_\1', accessor_method).upper()
490
491    @staticmethod
492    def get_access_mode_form(accessor_method):
493        prefix_mode_list = [
494            ('getAndAdd', AccessModeForm.GET_AND_UPDATE_NUMERIC),
495            ('getAndBitwise', AccessModeForm.GET_AND_UPDATE_BITWISE),
496            ('getAndSet', AccessModeForm.GET_AND_SET),
497            ('get', AccessModeForm.GET),
498            ('set', AccessModeForm.SET),
499            ('compareAndSet', AccessModeForm.STRONG_COMPARE_AND_SET),
500            ('weakCompareAndSet', AccessModeForm.WEAK_COMPARE_AND_SET),
501            ('compareAndExchange', AccessModeForm.COMPARE_AND_EXCHANGE)]
502        for prefix, mode in prefix_mode_list:
503            if accessor_method.startswith(prefix):
504                return mode
505        raise ValueError(accessor_method)
506
507VAR_HANDLE_ACCESSORS = [
508    VarHandleAccessor('get'),
509    VarHandleAccessor('set'),
510    VarHandleAccessor('getVolatile'),
511    VarHandleAccessor('setVolatile'),
512    VarHandleAccessor('getAcquire'),
513    VarHandleAccessor('setRelease'),
514    VarHandleAccessor('getOpaque'),
515    VarHandleAccessor('setOpaque'),
516    VarHandleAccessor('compareAndSet'),
517    VarHandleAccessor('compareAndExchange'),
518    VarHandleAccessor('compareAndExchangeAcquire'),
519    VarHandleAccessor('compareAndExchangeRelease'),
520    VarHandleAccessor('weakCompareAndSetPlain'),
521    VarHandleAccessor('weakCompareAndSet'),
522    VarHandleAccessor('weakCompareAndSetAcquire'),
523    VarHandleAccessor('weakCompareAndSetRelease'),
524    VarHandleAccessor('getAndSet'),
525    VarHandleAccessor('getAndSetAcquire'),
526    VarHandleAccessor('getAndSetRelease'),
527    VarHandleAccessor('getAndAdd'),
528    VarHandleAccessor('getAndAddAcquire'),
529    VarHandleAccessor('getAndAddRelease'),
530    VarHandleAccessor('getAndBitwiseOr'),
531    VarHandleAccessor('getAndBitwiseOrRelease'),
532    VarHandleAccessor('getAndBitwiseOrAcquire'),
533    VarHandleAccessor('getAndBitwiseAnd'),
534    VarHandleAccessor('getAndBitwiseAndRelease'),
535    VarHandleAccessor('getAndBitwiseAndAcquire'),
536    VarHandleAccessor('getAndBitwiseXor'),
537    VarHandleAccessor('getAndBitwiseXorRelease'),
538    VarHandleAccessor('getAndBitwiseXorAcquire')
539]
540
541# Pseudo-RNG used for arbitrary decisions
542RANDOM = Random(0)
543
544BANNER = '// This file is generated by util-src/generate_java.py do not directly modify!'
545
546# List of generated test classes
547GENERATED_TEST_CLASSES = []
548
549def java_file_for_class(class_name):
550    return class_name + ".java"
551
552def capitalize_first(word):
553    return word[0].upper() + word[1:]
554
555def indent_code(code):
556    """Applies rudimentary indentation to code"""
557    return code
558
559def build_template_dictionary(test_class, var_handle_kind, accessor, var_type):
560    initial_value = RANDOM.choice(var_type.examples)
561    updated_value = RANDOM.choice(list(filter(lambda v : v != initial_value, var_type.examples)))
562    coordinates = ", ".join(var_handle_kind.get_coordinates())
563    if accessor.get_number_of_var_type_arguments() != 0 and coordinates != "":
564        coordinates += ", "
565    dictionary = {
566        'accessor_method' : accessor.method_name,
567        'access_mode' : accessor.access_mode,
568        'banner' : BANNER,
569        'coordinates' : coordinates,
570        'initial_value' : initial_value,
571        'test_class' : test_class,
572        'updated_value' : updated_value,
573        'var_type' : var_type,
574    }
575    dictionary['imports'] = ";\n".join(list(map(lambda x: "import " + x, var_handle_kind.get_imports())))
576    dictionary['lookup'] = var_handle_kind.get_lookup(dictionary)
577    dictionary['field_declarations'] = ";\n".join(var_handle_kind.get_field_declarations(dictionary))
578    dictionary['read_value'] = var_handle_kind.get_value(dictionary)
579    return dictionary
580
581def emit_accessor_test(var_handle_kind, accessor, var_type, output_path):
582    test_class = var_handle_kind.get_name() + capitalize_first(accessor.method_name) + capitalize_first(var_type.name)
583    GENERATED_TEST_CLASSES.append(test_class)
584    src_file_path = output_path / java_file_for_class(test_class)
585    expansions = build_template_dictionary(test_class, var_handle_kind, accessor, var_type)
586    # Compute test operation
587    if accessor.access_mode_form == AccessModeForm.GET:
588        test_template = Template("""
589        ${var_type} value = (${var_type}) vh.${accessor_method}(${coordinates});
590        assertEquals(${initial_value}, value);""")
591    elif accessor.access_mode_form == AccessModeForm.SET:
592        test_template = Template("""
593        vh.${accessor_method}(${coordinates}${updated_value});
594        assertEquals(${updated_value}, ${read_value});""")
595    elif accessor.access_mode_form == AccessModeForm.STRONG_COMPARE_AND_SET:
596        test_template = Template("""
597        assertEquals(${initial_value}, ${read_value});
598        // Test an update that should succeed.
599        boolean applied = (boolean) vh.${accessor_method}(${coordinates}${initial_value}, ${updated_value});
600        assertEquals(${updated_value}, ${read_value});
601        assertTrue(applied);
602        // Test an update that should fail.
603        applied = (boolean) vh.${accessor_method}(${coordinates}${initial_value}, ${initial_value});
604        assertFalse(applied);
605        assertEquals(${updated_value}, ${read_value});""")
606    elif accessor.access_mode_form == AccessModeForm.WEAK_COMPARE_AND_SET:
607        test_template = Template("""
608        assertEquals(${initial_value}, ${read_value});
609        // Test an update that should succeed.
610        int attempts = 10000;
611        boolean applied;
612        do {
613            applied = (boolean) vh.${accessor_method}(${coordinates}${initial_value}, ${updated_value});
614        } while (applied == false && attempts-- > 0);
615        assertEquals(${updated_value}, ${read_value});
616        assertTrue(attempts > 0);
617        // Test an update that should fail.
618        applied = (boolean) vh.${accessor_method}(${coordinates}${initial_value}, ${initial_value});
619        assertFalse(applied);
620        assertEquals(${updated_value}, ${read_value});""")
621    elif accessor.access_mode_form == AccessModeForm.COMPARE_AND_EXCHANGE:
622        test_template = Template("""
623        // This update should succeed.
624        ${var_type} witness_value = (${var_type}) vh.${accessor_method}(${coordinates}${initial_value}, ${updated_value});
625        assertEquals(${initial_value}, witness_value);
626        assertEquals(${updated_value}, ${read_value});
627        // This update should fail.
628        witness_value = (${var_type}) vh.${accessor_method}(${coordinates}${initial_value}, ${initial_value});
629        assertEquals(${updated_value}, witness_value);
630        assertEquals(${updated_value}, ${read_value});""")
631    elif accessor.access_mode_form == AccessModeForm.GET_AND_SET:
632        test_template = Template("""
633        ${var_type} old_value = (${var_type}) vh.${accessor_method}(${coordinates}${updated_value});
634        assertEquals(${initial_value}, old_value);
635        assertEquals(${updated_value}, ${read_value});""")
636    elif accessor.access_mode_form == AccessModeForm.GET_AND_UPDATE_BITWISE:
637        if var_type.supports_bitwise == True:
638            expansions['binop'] = accessor.get_java_bitwise_operator()
639            test_template = Template("""
640            ${var_type} old_value = (${var_type}) vh.${accessor_method}(${coordinates}${updated_value});
641            assertEquals(${initial_value}, old_value);
642            assertEquals(${initial_value} ${binop} ${updated_value}, ${read_value});""")
643        else:
644            test_template = Template("""
645            vh.${accessor_method}(${coordinates}${initial_value}, ${updated_value});
646            failUnreachable();""")
647    elif accessor.access_mode_form == AccessModeForm.GET_AND_UPDATE_NUMERIC:
648        if var_type.supports_numeric == True:
649            expansions['binop'] = accessor.get_java_numeric_operator()
650            test_template = Template("""
651            ${var_type} old_value = (${var_type}) vh.${accessor_method}(${coordinates}${updated_value});
652            assertEquals(${initial_value}, old_value);
653            ${var_type} expected_value = (${var_type}) (${initial_value} ${binop} ${updated_value});
654            assertEquals(expected_value, ${read_value});""")
655        else:
656            test_template = Template("""
657            vh.${accessor_method}(${coordinates}${initial_value}, ${updated_value});
658            failUnreachable();""")
659    else:
660        raise ValueError(accessor.access_mode_form)
661
662    if var_handle_kind.may_throw_read_only and not accessor.is_read_only():
663        # ByteBufferViews can be read-only and dynamically raise ReadOnlyBufferException.
664        expansions['try_statement'] = "try {"
665        expansions['catch_statement'] = "failUnreachable();\n} catch (ReadOnlyBufferException ex) {}"
666    else:
667        expansions['try_statement'] = ""
668        expansions['catch_statement'] = ""
669
670    expansions['test_body'] = test_template.safe_substitute(expansions)
671
672    s = Template("""${banner}
673
674${imports};
675
676class ${test_class} extends VarHandleUnitTest {
677    ${field_declarations};
678    static final VarHandle vh;
679    static {
680        try {
681            vh = ${lookup};
682        } catch (Exception e) {
683            throw new RuntimeException("Unexpected initialization exception", e);
684        }
685    }
686
687    @Override
688    public void doTest() throws Exception {
689        if (!vh.isAccessModeSupported(VarHandle.AccessMode.${access_mode})) {
690            try {
691                ${test_body}
692                failUnreachable();
693            } catch (UnsupportedOperationException ex) {}
694        } else {
695            ${try_statement}
696            ${test_body}
697            ${catch_statement}
698        }
699    }
700
701    public static void main(String[] args) {
702         new ${test_class}().run();
703    }
704}
705""").safe_substitute(expansions)
706    with src_file_path.open("w") as src_file:
707        print(s, file=src_file)
708
709def emit_value_type_accessor_tests(output_path):
710    for var_handle_kind in ALL_VAR_HANDLE_KINDS:
711        for accessor in VAR_HANDLE_ACCESSORS:
712            for var_type in var_handle_kind.get_supported_types():
713                emit_accessor_test(var_handle_kind, accessor, var_type, output_path)
714
715def emit_reference_accessor_tests(output_path):
716    ref_type = JavaType("Widget", [ "Widget.ONE", "Widget.TWO", "null" ])
717    for var_handle_kind in ALL_VAR_HANDLE_KINDS:
718        if var_handle_kind.is_view():
719            # Views as reference type arrays are not supported. They
720            # fail instantiation. This is tested in 710-varhandle-creation.
721            continue
722        for accessor in VAR_HANDLE_ACCESSORS:
723            emit_accessor_test(var_handle_kind, accessor, ref_type, output_path)
724
725def emit_boxing_value_type_accessor_test(accessor, var_type, output_path):
726    test_class = "Boxing" + capitalize_first(accessor.method_name) + capitalize_first(var_type.name)
727    GENERATED_TEST_CLASSES.append(test_class)
728    src_file_path = output_path / java_file_for_class(test_class)
729    var_handle_kind = FIELD_VAR_HANDLE
730    expansions = build_template_dictionary(test_class, var_handle_kind, accessor, var_type)
731    template = Template("""
732${banner}
733
734${imports};
735import java.lang.invoke.WrongMethodTypeException;
736
737public class ${test_class} extends VarHandleUnitTest {
738    ${field_declarations};
739    private static final VarHandle vh;
740    static {
741        try {
742            vh = ${lookup};
743        } catch (Exception e) {
744            throw new RuntimeException("Unexpected initialization exception", e);
745        }
746    }
747
748    @Override
749    public void doTest() throws Exception {
750       ${body}
751    }
752
753    public static void main(String[] args) {
754         new ${test_class}().run();
755    }
756}
757""")
758    with io.StringIO() as body_text:
759        compatible_types = types_that_widen_to(var_type)
760        incompatible_types = { RANDOM.choice(list(VALUE_TYPES - compatible_types)) }
761        test_types = compatible_types | incompatible_types
762        for value_type in test_types:
763            print("try {", file=body_text)
764            return_type = accessor.get_return_type(var_type)
765            if return_type:
766                print("{0} result = ({0}) ".format(return_type), end="", file=body_text)
767            print("vh.{0}(this".format(accessor.method_name), end="", file=body_text)
768            num_args = accessor.get_number_of_var_type_arguments()
769            for i in range(0, num_args):
770                print(", SampleValues.get_{0}({1})".format(value_type.boxed_type, i), end="", file=body_text)
771            print(");", file=body_text)
772            if value_type in compatible_types:
773                print("   assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.{0}));".format(accessor.access_mode),
774                      file=body_text)
775            else:
776                print("failUnreachable();", file=body_text)
777                print("} catch (WrongMethodTypeException e) {", file=body_text)
778            print("} catch (UnsupportedOperationException e) {", file=body_text)
779            print("   assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.{0}));".format(accessor.access_mode),
780                  file=body_text)
781            print("}", file=body_text)
782        expansions['body'] = body_text.getvalue();
783        with src_file_path.open("w") as src_file:
784            print(template.safe_substitute(expansions), file=src_file)
785
786def emit_boxing_return_value_type_test(accessor, var_type, output_path):
787    test_class = "BoxingReturn" + capitalize_first(accessor.method_name) + capitalize_first(var_type.name)
788    GENERATED_TEST_CLASSES.append(test_class)
789    src_file_path = output_path / java_file_for_class(test_class)
790    var_handle_kind = FIELD_VAR_HANDLE
791    expansions = build_template_dictionary(test_class, var_handle_kind, accessor, var_type)
792    template = Template("""
793${banner}
794
795${imports};
796import java.lang.invoke.WrongMethodTypeException;
797
798public class ${test_class} extends VarHandleUnitTest {
799    ${field_declarations};
800    private static final VarHandle vh;
801    static {
802        try {
803            vh = ${lookup};
804        } catch (Exception e) {
805            throw new RuntimeException("Unexpected initialization exception", e);
806        }
807    }
808
809    @Override
810    public void doTest() throws Exception {
811       ${body}
812    }
813
814    public static void main(String[] args) {
815         new ${test_class}().run();
816    }
817}
818""")
819    with io.StringIO() as body_text:
820        return_type = accessor.get_return_type(var_type)
821        compatible_types = { return_type }
822        incompatible_types = { RANDOM.choice(list(VALUE_TYPES - compatible_types)) }
823        test_types = compatible_types | incompatible_types
824        for value_type in test_types:
825            print("try {", file=body_text)
826            print("{0} result = ({0}) ".format(value_type.boxed_type), end="", file=body_text)
827            print("vh.{0}(this".format(accessor.method_name), end="", file=body_text)
828            num_args = accessor.get_number_of_var_type_arguments()
829            for i in range(0, num_args):
830                print(", {0})".format(var_type.examples[i]), end="", file=body_text)
831            print(");", file=body_text)
832            if value_type in compatible_types:
833                print("   assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.{0}));".format(accessor.access_mode),
834                      file=body_text)
835            else:
836                print("failUnreachable();", file=body_text)
837                print("} catch (WrongMethodTypeException e) {", file=body_text)
838            print("} catch (UnsupportedOperationException e) {", file=body_text)
839            print("   assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.{0}));".format(accessor.access_mode),
840                  file=body_text)
841            print("}", file=body_text)
842        expansions['body'] = body_text.getvalue();
843        with src_file_path.open("w") as src_file:
844            print(template.safe_substitute(expansions), file=src_file)
845
846def emit_boxing_value_type_accessor_tests(output_path):
847    for var_type in VALUE_TYPES:
848        for accessor in VAR_HANDLE_ACCESSORS:
849            if accessor.get_number_of_var_type_arguments() > 0:
850                emit_boxing_value_type_accessor_test(accessor, var_type, output_path)
851            else:
852                emit_boxing_return_value_type_test(accessor, var_type, output_path)
853
854def emit_main(output_path, manual_test_classes):
855    main_file_path = output_path / "Main.java"
856    all_test_classes = GENERATED_TEST_CLASSES + manual_test_classes
857    with main_file_path.open("w") as main_file:
858        print("// " + BANNER, file=main_file)
859        print("""
860public class Main {
861    public static void main(String[] args) {
862""", file=main_file)
863        for cls in all_test_classes:
864            print("         " + cls + ".main(args);", file=main_file)
865        print("        VarHandleUnitTest.DEFAULT_COLLECTOR.printSummary();", file=main_file)
866        print("        System.exit(VarHandleUnitTest.DEFAULT_COLLECTOR.failuresOccurred() ? 1 : 0);", file=main_file)
867        print("    }\n}", file=main_file)
868
869def main(argv):
870    final_java_dir = Path(argv[1])
871    if not final_java_dir.exists() or not final_java_dir.is_dir():
872        print("{} is not a valid java dir".format(final_java_dir), file=sys.stderr)
873        sys.exit(1)
874    emit_value_type_accessor_tests(final_java_dir)
875    emit_reference_accessor_tests(final_java_dir)
876    emit_boxing_value_type_accessor_tests(final_java_dir)
877    emit_main(final_java_dir, argv[2:])
878
879if __name__ == '__main__':
880    main(sys.argv)
881