#!/usr/bin/python3 # # Copyright (C) 2018 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ Generate java test files for 712-varhandle-invocations """ from enum import Enum from pathlib import Path from random import Random from string import Template import io import re import sys class JavaType(object): def __init__(self, name, examples, supports_bitwise=False, supports_numeric=False): self.name=name self.examples=examples self.supports_bitwise=supports_bitwise self.supports_numeric=supports_numeric def is_value_type(self): return False def __repr__(self): return self.name def __str__(self): return self.name class ValueType(JavaType): def __init__(self, name, boxed_type, examples, ordinal=-1, width=-1, supports_bitwise=True, supports_numeric=True): JavaType.__init__(self, name, examples, supports_bitwise, supports_numeric) self.ordinal=ordinal self.width=width self.boxed_type=boxed_type def boxing_method(self): return self.boxed_type + ".valueOf" def unboxing_method(self): return self.name + "Value" def is_value_type(self): return True def __eq__(self, other): return self.ordinal == other.ordinal def __hash__(self): return self.ordinal def __le__(self, other): return self.ordinal < other.ordinal def __repr__(self): return self.name def __str__(self): return self.name BOOLEAN_TYPE = ValueType("boolean", "Boolean", [ "true", "false" ], ordinal = 0, width = 1, supports_numeric=False) BYTE_TYPE=ValueType("byte", "Byte", [ "(byte) -128", "(byte) -61", "(byte) 7", "(byte) 127", "(byte) 33" ], ordinal=1, width=1) SHORT_TYPE=ValueType("short", "Short", [ "(short) -32768", "(short) -384", "(short) 32767", "(short) 0xaa55" ], ordinal=2, width=2) CHAR_TYPE=ValueType("char", "Character", [ r"'A'", r"'#'", r"'$'", r"'Z'", r"'t'", r"'c'", r"Character.MAX_VALUE", r"Character.MIN_LOW_SURROGATE"], ordinal=3, width=2) INT_TYPE=ValueType("int", "Integer", [ "-0x01234567", "0x7f6e5d4c", "0x12345678", "0x10215220", "42" ], ordinal=4, width=4) LONG_TYPE=ValueType("long", "Long", [ "-0x0123456789abcdefl", "0x789abcdef0123456l", "0xfedcba9876543210l" ], ordinal=5, width=8) FLOAT_TYPE=ValueType("float", "Float", [ "-7.77e23f", "1.234e-17f", "3.40e36f", "-8.888e3f", "4.442e11f" ], ordinal=6, width=4, supports_bitwise=False) DOUBLE_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) VALUE_TYPES = { BOOLEAN_TYPE, BYTE_TYPE, SHORT_TYPE, CHAR_TYPE, INT_TYPE, LONG_TYPE, FLOAT_TYPE, DOUBLE_TYPE } WIDENING_CONVERSIONS = { BOOLEAN_TYPE : set(), BYTE_TYPE : { SHORT_TYPE, INT_TYPE, LONG_TYPE, FLOAT_TYPE, DOUBLE_TYPE }, SHORT_TYPE : { INT_TYPE, LONG_TYPE, FLOAT_TYPE, DOUBLE_TYPE }, CHAR_TYPE : { INT_TYPE, LONG_TYPE, FLOAT_TYPE, DOUBLE_TYPE }, INT_TYPE : { LONG_TYPE, FLOAT_TYPE, DOUBLE_TYPE }, LONG_TYPE : { FLOAT_TYPE, DOUBLE_TYPE }, FLOAT_TYPE : { DOUBLE_TYPE }, DOUBLE_TYPE : set() } def types_that_widen_to(var_type): types_that_widen = { var_type } for src_type in WIDENING_CONVERSIONS: if var_type in WIDENING_CONVERSIONS[src_type]: types_that_widen.add(src_type) return types_that_widen class VarHandleKind(object): ALL_SUPPORTED_TYPES = VALUE_TYPES VIEW_SUPPORTED_TYPES = list(filter(lambda x : x.width >= 2, ALL_SUPPORTED_TYPES)) def __init__(self, name, imports=[], declarations=[], lookup='', coordinates=[], get_value='', may_throw_read_only=False): self.name = name self.imports = imports self.declarations = declarations self.lookup = lookup self.coordinates = coordinates self.get_value_ = get_value self.may_throw_read_only = may_throw_read_only def get_name(self): return self.name def get_coordinates(self): return self.coordinates def get_field_declarations(self, dictionary): return list(map(lambda d: Template(d).safe_substitute(dictionary), self.declarations)) def get_imports(self): return self.imports def get_lookup(self, dictionary): return Template(self.lookup).safe_substitute(dictionary) def get_supported_types(self): return VarHandleKind.VIEW_SUPPORTED_TYPES if self.is_view() else VarHandleKind.ALL_SUPPORTED_TYPES def is_view(self): return "View" in self.name def get_value(self, dictionary): return Template(self.get_value_).safe_substitute(dictionary) FIELD_VAR_HANDLE = VarHandleKind("Field", [ 'java.lang.invoke.MethodHandles', 'java.lang.invoke.VarHandle' ], [ "${var_type} field = ${initial_value}" ], 'MethodHandles.lookup().findVarHandle(${test_class}.class, "field", ${var_type}.class)', [ 'this' ], 'field', may_throw_read_only = False) FINAL_FIELD_VAR_HANDLE = VarHandleKind("FinalField", [ 'java.lang.invoke.MethodHandles', 'java.lang.invoke.VarHandle' ], [ "${var_type} field = ${initial_value}" ], 'MethodHandles.lookup().findVarHandle(${test_class}.class, "field", ${var_type}.class)', [ 'this' ], 'field', may_throw_read_only = False) STATIC_FIELD_VAR_HANDLE = VarHandleKind("StaticField", [ 'java.lang.invoke.MethodHandles', 'java.lang.invoke.VarHandle' ], [ "static ${var_type} field = ${initial_value}" ], 'MethodHandles.lookup().findStaticVarHandle(${test_class}.class, "field", ${var_type}.class)', [], 'field', may_throw_read_only = False) STATIC_FINAL_FIELD_VAR_HANDLE = VarHandleKind("StaticFinalField", [ 'java.lang.invoke.MethodHandles', 'java.lang.invoke.VarHandle' ], [ "static ${var_type} field = ${initial_value}" ], 'MethodHandles.lookup().findStaticVarHandle(${test_class}.class, "field", ${var_type}.class)', [], 'field', may_throw_read_only = False) ARRAY_ELEMENT_VAR_HANDLE = VarHandleKind("ArrayElement", [ 'java.lang.invoke.MethodHandles', 'java.lang.invoke.VarHandle' ], [ "${var_type}[] array = new ${var_type}[11]", "int index = 3", "{ array[index] = ${initial_value}; }" ], 'MethodHandles.arrayElementVarHandle(${var_type}[].class)', [ 'array', 'index'], 'array[index]', may_throw_read_only = False) BYTE_ARRAY_LE_VIEW_VAR_HANDLE = VarHandleKind("ByteArrayViewLE", [ 'java.lang.invoke.MethodHandles', 'java.lang.invoke.VarHandle', 'java.nio.ByteOrder' ], [ "byte[] array = new byte[27]", "int index = 8", "{" " index = VarHandleUnitTestHelpers.alignedOffset_${var_type}(array, index);" " VarHandleUnitTestHelpers.setBytesAs_${var_type}(array, index, ${initial_value}, ByteOrder.LITTLE_ENDIAN);" "}" ], 'MethodHandles.byteArrayViewVarHandle(${var_type}[].class, ByteOrder.LITTLE_ENDIAN)', [ 'array', 'index' ], 'VarHandleUnitTestHelpers.getBytesAs_${var_type}(array, index, ByteOrder.LITTLE_ENDIAN)', may_throw_read_only = False) BYTE_ARRAY_BE_VIEW_VAR_HANDLE = VarHandleKind("ByteArrayViewBE", [ 'java.lang.invoke.MethodHandles', 'java.lang.invoke.VarHandle', 'java.nio.ByteOrder' ], [ "byte[] array = new byte[27]", "int index = 8", "{" " index = VarHandleUnitTestHelpers.alignedOffset_${var_type}(array, index);" " VarHandleUnitTestHelpers.setBytesAs_${var_type}(array, index, ${initial_value}, ByteOrder.BIG_ENDIAN);" "}" ], 'MethodHandles.byteArrayViewVarHandle(${var_type}[].class, ByteOrder.BIG_ENDIAN)', [ 'array', 'index' ], 'VarHandleUnitTestHelpers.getBytesAs_${var_type}(array, index, ByteOrder.BIG_ENDIAN)', may_throw_read_only = False) DIRECT_BYTE_BUFFER_LE_VIEW_VAR_HANDLE = VarHandleKind("DirectByteBufferViewLE", [ 'java.lang.invoke.MethodHandles', 'java.lang.invoke.VarHandle', 'java.nio.ByteBuffer', 'java.nio.ByteOrder' ], [ "ByteBuffer bb = ByteBuffer.allocateDirect(31)", "int index = 8", "{" " index = VarHandleUnitTestHelpers.alignedOffset_${var_type}(bb, index);" " VarHandleUnitTestHelpers.setBytesAs_${var_type}(bb, index, ${initial_value}, ByteOrder.LITTLE_ENDIAN);" "}" ], 'MethodHandles.byteBufferViewVarHandle(${var_type}[].class, ByteOrder.LITTLE_ENDIAN)', [ 'bb', 'index' ], 'VarHandleUnitTestHelpers.getBytesAs_${var_type}(bb, index, ByteOrder.LITTLE_ENDIAN)', may_throw_read_only = False) DIRECT_BYTE_BUFFER_BE_VIEW_VAR_HANDLE = VarHandleKind("DirectByteBufferViewBE", [ 'java.lang.invoke.MethodHandles', 'java.lang.invoke.VarHandle', 'java.nio.ByteBuffer', 'java.nio.ByteOrder' ], [ "ByteBuffer bb = ByteBuffer.allocateDirect(31)", "int index = 8", "{" " index = VarHandleUnitTestHelpers.alignedOffset_${var_type}(bb, index);" " VarHandleUnitTestHelpers.setBytesAs_${var_type}(bb, index, ${initial_value}, ByteOrder.BIG_ENDIAN);" "}" ], 'MethodHandles.byteBufferViewVarHandle(${var_type}[].class, ByteOrder.BIG_ENDIAN)', [ 'bb', 'index' ], 'VarHandleUnitTestHelpers.getBytesAs_${var_type}(bb, index, ByteOrder.BIG_ENDIAN)', may_throw_read_only = False) HEAP_BYTE_BUFFER_LE_VIEW_VAR_HANDLE = VarHandleKind("HeapByteBufferViewLE", [ 'java.lang.invoke.MethodHandles', 'java.lang.invoke.VarHandle', 'java.nio.ByteBuffer', 'java.nio.ByteOrder' ], [ "byte[] array = new byte[36]", "int offset = 8", "ByteBuffer bb = ByteBuffer.wrap(array, offset, array.length - offset)", "int index = 8", "{" " index = VarHandleUnitTestHelpers.alignedOffset_${var_type}(bb, index);" " VarHandleUnitTestHelpers.setBytesAs_${var_type}(bb, index, ${initial_value}, ByteOrder.LITTLE_ENDIAN);" "}" ], 'MethodHandles.byteBufferViewVarHandle(${var_type}[].class, ByteOrder.LITTLE_ENDIAN)', [ 'bb', 'index' ], 'VarHandleUnitTestHelpers.getBytesAs_${var_type}(bb, index, ByteOrder.LITTLE_ENDIAN)', may_throw_read_only = False) HEAP_BYTE_BUFFER_BE_VIEW_VAR_HANDLE = VarHandleKind("HeapByteBufferViewBE", [ 'java.lang.invoke.MethodHandles', 'java.lang.invoke.VarHandle', 'java.nio.ByteBuffer', 'java.nio.ByteOrder' ], [ "byte[] array = new byte[47]", "int offset = 8", "ByteBuffer bb = ByteBuffer.wrap(array, offset, array.length - offset)", "int index = 8", "{" " index = VarHandleUnitTestHelpers.alignedOffset_${var_type}(bb, index);" " VarHandleUnitTestHelpers.setBytesAs_${var_type}(bb, index, ${initial_value}, ByteOrder.BIG_ENDIAN);" "}" ], 'MethodHandles.byteBufferViewVarHandle(${var_type}[].class, ByteOrder.BIG_ENDIAN)', [ 'bb', 'index' ], 'VarHandleUnitTestHelpers.getBytesAs_${var_type}(bb, index, ByteOrder.BIG_ENDIAN)', may_throw_read_only = False) HEAP_BYTE_BUFFER_RO_LE_VIEW_VAR_HANDLE = VarHandleKind("HeapByteBufferReadOnlyViewLE", [ 'java.lang.invoke.MethodHandles', 'java.lang.invoke.VarHandle', 'java.nio.ByteBuffer', 'java.nio.ByteOrder', 'java.nio.ReadOnlyBufferException' ], [ "byte[] array = new byte[43]", "int index = 8", "ByteBuffer bb", "{" " bb = ByteBuffer.wrap(array).asReadOnlyBuffer();" " index = VarHandleUnitTestHelpers.alignedOffset_${var_type}(bb, index);" " VarHandleUnitTestHelpers.setBytesAs_${var_type}(array, index, ${initial_value}, ByteOrder.LITTLE_ENDIAN);" " bb = bb.asReadOnlyBuffer();" "}" ], 'MethodHandles.byteBufferViewVarHandle(${var_type}[].class, ByteOrder.LITTLE_ENDIAN)', [ 'bb', 'index' ], 'VarHandleUnitTestHelpers.getBytesAs_${var_type}(bb, index, ByteOrder.LITTLE_ENDIAN)', may_throw_read_only = True) HEAP_BYTE_BUFFER_RO_BE_VIEW_VAR_HANDLE = VarHandleKind("HeapByteBufferReadOnlyViewBE", [ 'java.lang.invoke.MethodHandles', 'java.lang.invoke.VarHandle', 'java.nio.ByteBuffer', 'java.nio.ByteOrder', 'java.nio.ReadOnlyBufferException' ], [ "byte[] array = new byte[29]", "int index", "ByteBuffer bb", "{" " bb = ByteBuffer.wrap(array);" " index = VarHandleUnitTestHelpers.alignedOffset_${var_type}(bb, 8);" " VarHandleUnitTestHelpers.setBytesAs_${var_type}(array, index, ${initial_value}, ByteOrder.BIG_ENDIAN);" " bb = bb.asReadOnlyBuffer();" "}" ], 'MethodHandles.byteBufferViewVarHandle(${var_type}[].class, ByteOrder.BIG_ENDIAN)', [ 'bb', 'index' ], 'VarHandleUnitTestHelpers.getBytesAs_${var_type}(bb, index, ByteOrder.BIG_ENDIAN)', may_throw_read_only = True) ALL_FIELD_VAR_HANDLE_KINDS = [ FIELD_VAR_HANDLE, FINAL_FIELD_VAR_HANDLE, STATIC_FIELD_VAR_HANDLE, STATIC_FINAL_FIELD_VAR_HANDLE ] ALL_BYTE_VIEW_VAR_HANDLE_KINDS = [ BYTE_ARRAY_LE_VIEW_VAR_HANDLE, BYTE_ARRAY_BE_VIEW_VAR_HANDLE, DIRECT_BYTE_BUFFER_LE_VIEW_VAR_HANDLE, DIRECT_BYTE_BUFFER_BE_VIEW_VAR_HANDLE, HEAP_BYTE_BUFFER_LE_VIEW_VAR_HANDLE, HEAP_BYTE_BUFFER_BE_VIEW_VAR_HANDLE, HEAP_BYTE_BUFFER_RO_LE_VIEW_VAR_HANDLE, HEAP_BYTE_BUFFER_RO_BE_VIEW_VAR_HANDLE ] ALL_VAR_HANDLE_KINDS = ALL_FIELD_VAR_HANDLE_KINDS + [ ARRAY_ELEMENT_VAR_HANDLE ] + ALL_BYTE_VIEW_VAR_HANDLE_KINDS class AccessModeForm(Enum): GET = 0 SET = 1 STRONG_COMPARE_AND_SET = 2 WEAK_COMPARE_AND_SET = 3 COMPARE_AND_EXCHANGE = 4 GET_AND_SET = 5 GET_AND_UPDATE_BITWISE = 6 GET_AND_UPDATE_NUMERIC = 7 class VarHandleAccessor: def __init__(self, method_name): self.method_name = method_name self.access_mode = self.get_access_mode(method_name) self.access_mode_form = self.get_access_mode_form(method_name) def get_return_type(self, var_type): if self.access_mode_form == AccessModeForm.SET: return None elif (self.access_mode_form == AccessModeForm.STRONG_COMPARE_AND_SET or self.access_mode_form == AccessModeForm.WEAK_COMPARE_AND_SET): return BOOLEAN_TYPE else: return var_type def get_number_of_var_type_arguments(self): if self.access_mode_form == AccessModeForm.GET: return 0 elif (self.access_mode_form == AccessModeForm.SET or self.access_mode_form == AccessModeForm.GET_AND_SET or self.access_mode_form == AccessModeForm.GET_AND_UPDATE_BITWISE or self.access_mode_form == AccessModeForm.GET_AND_UPDATE_NUMERIC): return 1 elif (self.access_mode_form == AccessModeForm.STRONG_COMPARE_AND_SET or self.access_mode_form == AccessModeForm.WEAK_COMPARE_AND_SET or self.access_mode_form == AccessModeForm.COMPARE_AND_EXCHANGE): return 2 else: raise ValueError(self.access_mode_form) def is_read_only(self): return self.access_mode_form == AccessModeForm.GET def get_java_bitwise_operator(self): if "BitwiseAnd" in self.method_name: return "&" elif "BitwiseOr" in self.method_name: return "|" elif "BitwiseXor" in self.method_name: return "^" raise ValueError(self.method_name) def get_java_numeric_operator(self): if "Add" in self.method_name: return "+" raise ValueError(self.method_name) @staticmethod def get_access_mode(accessor_method): """Converts an access method name to AccessMode value. For example, getAndSet becomes GET_AND_SET""" return re.sub('([A-Z])', r'_\1', accessor_method).upper() @staticmethod def get_access_mode_form(accessor_method): prefix_mode_list = [ ('getAndAdd', AccessModeForm.GET_AND_UPDATE_NUMERIC), ('getAndBitwise', AccessModeForm.GET_AND_UPDATE_BITWISE), ('getAndSet', AccessModeForm.GET_AND_SET), ('get', AccessModeForm.GET), ('set', AccessModeForm.SET), ('compareAndSet', AccessModeForm.STRONG_COMPARE_AND_SET), ('weakCompareAndSet', AccessModeForm.WEAK_COMPARE_AND_SET), ('compareAndExchange', AccessModeForm.COMPARE_AND_EXCHANGE)] for prefix, mode in prefix_mode_list: if accessor_method.startswith(prefix): return mode raise ValueError(accessor_method) VAR_HANDLE_ACCESSORS = [ VarHandleAccessor('get'), VarHandleAccessor('set'), VarHandleAccessor('getVolatile'), VarHandleAccessor('setVolatile'), VarHandleAccessor('getAcquire'), VarHandleAccessor('setRelease'), VarHandleAccessor('getOpaque'), VarHandleAccessor('setOpaque'), VarHandleAccessor('compareAndSet'), VarHandleAccessor('compareAndExchange'), VarHandleAccessor('compareAndExchangeAcquire'), VarHandleAccessor('compareAndExchangeRelease'), VarHandleAccessor('weakCompareAndSetPlain'), VarHandleAccessor('weakCompareAndSet'), VarHandleAccessor('weakCompareAndSetAcquire'), VarHandleAccessor('weakCompareAndSetRelease'), VarHandleAccessor('getAndSet'), VarHandleAccessor('getAndSetAcquire'), VarHandleAccessor('getAndSetRelease'), VarHandleAccessor('getAndAdd'), VarHandleAccessor('getAndAddAcquire'), VarHandleAccessor('getAndAddRelease'), VarHandleAccessor('getAndBitwiseOr'), VarHandleAccessor('getAndBitwiseOrRelease'), VarHandleAccessor('getAndBitwiseOrAcquire'), VarHandleAccessor('getAndBitwiseAnd'), VarHandleAccessor('getAndBitwiseAndRelease'), VarHandleAccessor('getAndBitwiseAndAcquire'), VarHandleAccessor('getAndBitwiseXor'), VarHandleAccessor('getAndBitwiseXorRelease'), VarHandleAccessor('getAndBitwiseXorAcquire') ] # Pseudo-RNG used for arbitrary decisions RANDOM = Random(0) BANNER = '// This file is generated by util-src/generate_java.py do not directly modify!' # List of generated test classes GENERATED_TEST_CLASSES = [] def java_file_for_class(class_name): return class_name + ".java" def capitalize_first(word): return word[0].upper() + word[1:] def indent_code(code): """Applies rudimentary indentation to code""" return code def build_template_dictionary(test_class, var_handle_kind, accessor, var_type): initial_value = RANDOM.choice(var_type.examples) updated_value = RANDOM.choice(list(filter(lambda v : v != initial_value, var_type.examples))) coordinates = ", ".join(var_handle_kind.get_coordinates()) if accessor.get_number_of_var_type_arguments() != 0 and coordinates != "": coordinates += ", " dictionary = { 'accessor_method' : accessor.method_name, 'access_mode' : accessor.access_mode, 'banner' : BANNER, 'coordinates' : coordinates, 'initial_value' : initial_value, 'test_class' : test_class, 'updated_value' : updated_value, 'var_type' : var_type, } dictionary['imports'] = ";\n".join(list(map(lambda x: "import " + x, var_handle_kind.get_imports()))) dictionary['lookup'] = var_handle_kind.get_lookup(dictionary) dictionary['field_declarations'] = ";\n".join(var_handle_kind.get_field_declarations(dictionary)) dictionary['read_value'] = var_handle_kind.get_value(dictionary) return dictionary def emit_accessor_test(var_handle_kind, accessor, var_type, output_path): test_class = var_handle_kind.get_name() + capitalize_first(accessor.method_name) + capitalize_first(var_type.name) GENERATED_TEST_CLASSES.append(test_class) src_file_path = output_path / java_file_for_class(test_class) expansions = build_template_dictionary(test_class, var_handle_kind, accessor, var_type) # Compute test operation if accessor.access_mode_form == AccessModeForm.GET: test_template = Template(""" ${var_type} value = (${var_type}) vh.${accessor_method}(${coordinates}); assertEquals(${initial_value}, value);""") elif accessor.access_mode_form == AccessModeForm.SET: test_template = Template(""" vh.${accessor_method}(${coordinates}${updated_value}); assertEquals(${updated_value}, ${read_value});""") elif accessor.access_mode_form == AccessModeForm.STRONG_COMPARE_AND_SET: test_template = Template(""" assertEquals(${initial_value}, ${read_value}); // Test an update that should succeed. boolean applied = (boolean) vh.${accessor_method}(${coordinates}${initial_value}, ${updated_value}); assertEquals(${updated_value}, ${read_value}); assertTrue(applied); // Test an update that should fail. applied = (boolean) vh.${accessor_method}(${coordinates}${initial_value}, ${initial_value}); assertFalse(applied); assertEquals(${updated_value}, ${read_value});""") elif accessor.access_mode_form == AccessModeForm.WEAK_COMPARE_AND_SET: test_template = Template(""" assertEquals(${initial_value}, ${read_value}); // Test an update that should succeed. int attempts = 10000; boolean applied; do { applied = (boolean) vh.${accessor_method}(${coordinates}${initial_value}, ${updated_value}); } while (applied == false && attempts-- > 0); assertEquals(${updated_value}, ${read_value}); assertTrue(attempts > 0); // Test an update that should fail. applied = (boolean) vh.${accessor_method}(${coordinates}${initial_value}, ${initial_value}); assertFalse(applied); assertEquals(${updated_value}, ${read_value});""") elif accessor.access_mode_form == AccessModeForm.COMPARE_AND_EXCHANGE: test_template = Template(""" // This update should succeed. ${var_type} witness_value = (${var_type}) vh.${accessor_method}(${coordinates}${initial_value}, ${updated_value}); assertEquals(${initial_value}, witness_value); assertEquals(${updated_value}, ${read_value}); // This update should fail. witness_value = (${var_type}) vh.${accessor_method}(${coordinates}${initial_value}, ${initial_value}); assertEquals(${updated_value}, witness_value); assertEquals(${updated_value}, ${read_value});""") elif accessor.access_mode_form == AccessModeForm.GET_AND_SET: test_template = Template(""" ${var_type} old_value = (${var_type}) vh.${accessor_method}(${coordinates}${updated_value}); assertEquals(${initial_value}, old_value); assertEquals(${updated_value}, ${read_value});""") elif accessor.access_mode_form == AccessModeForm.GET_AND_UPDATE_BITWISE: if var_type.supports_bitwise == True: expansions['binop'] = accessor.get_java_bitwise_operator() test_template = Template(""" ${var_type} old_value = (${var_type}) vh.${accessor_method}(${coordinates}${updated_value}); assertEquals(${initial_value}, old_value); assertEquals(${initial_value} ${binop} ${updated_value}, ${read_value});""") else: test_template = Template(""" vh.${accessor_method}(${coordinates}${initial_value}, ${updated_value}); failUnreachable();""") elif accessor.access_mode_form == AccessModeForm.GET_AND_UPDATE_NUMERIC: if var_type.supports_numeric == True: expansions['binop'] = accessor.get_java_numeric_operator() test_template = Template(""" ${var_type} old_value = (${var_type}) vh.${accessor_method}(${coordinates}${updated_value}); assertEquals(${initial_value}, old_value); ${var_type} expected_value = (${var_type}) (${initial_value} ${binop} ${updated_value}); assertEquals(expected_value, ${read_value});""") else: test_template = Template(""" vh.${accessor_method}(${coordinates}${initial_value}, ${updated_value}); failUnreachable();""") else: raise ValueError(accessor.access_mode_form) if var_handle_kind.may_throw_read_only and not accessor.is_read_only(): # ByteBufferViews can be read-only and dynamically raise ReadOnlyBufferException. expansions['try_statement'] = "try {" expansions['catch_statement'] = "failUnreachable();\n} catch (ReadOnlyBufferException ex) {}" else: expansions['try_statement'] = "" expansions['catch_statement'] = "" expansions['test_body'] = test_template.safe_substitute(expansions) s = Template("""${banner} ${imports}; class ${test_class} extends VarHandleUnitTest { ${field_declarations}; static final VarHandle vh; static { try { vh = ${lookup}; } catch (Exception e) { throw new RuntimeException("Unexpected initialization exception", e); } } @Override public void doTest() throws Exception { if (!vh.isAccessModeSupported(VarHandle.AccessMode.${access_mode})) { try { ${test_body} failUnreachable(); } catch (UnsupportedOperationException ex) {} } else { ${try_statement} ${test_body} ${catch_statement} } } public static void main(String[] args) { new ${test_class}().run(); } } """).safe_substitute(expansions) with src_file_path.open("w") as src_file: print(s, file=src_file) def emit_value_type_accessor_tests(output_path): for var_handle_kind in ALL_VAR_HANDLE_KINDS: for accessor in VAR_HANDLE_ACCESSORS: for var_type in var_handle_kind.get_supported_types(): emit_accessor_test(var_handle_kind, accessor, var_type, output_path) def emit_reference_accessor_tests(output_path): ref_type = JavaType("Widget", [ "Widget.ONE", "Widget.TWO", "null" ]) for var_handle_kind in ALL_VAR_HANDLE_KINDS: if var_handle_kind.is_view(): # Views as reference type arrays are not supported. They # fail instantiation. This is tested in 710-varhandle-creation. continue for accessor in VAR_HANDLE_ACCESSORS: emit_accessor_test(var_handle_kind, accessor, ref_type, output_path) def emit_interface_accessor_tests(output_path): ref_type = JavaType("WidgetInterface", [ "Widget.ONE", "Widget.TWO", "null" ]) for var_handle_kind in ALL_VAR_HANDLE_KINDS: if var_handle_kind.is_view(): # Views as reference type arrays are not supported. They # fail instantiation. This is tested in 710-varhandle-creation. continue for accessor in VAR_HANDLE_ACCESSORS: emit_accessor_test(var_handle_kind, accessor, ref_type, output_path) def emit_boxing_value_type_accessor_test(accessor, var_type, output_path): test_class = "Boxing" + capitalize_first(accessor.method_name) + capitalize_first(var_type.name) GENERATED_TEST_CLASSES.append(test_class) src_file_path = output_path / java_file_for_class(test_class) var_handle_kind = FIELD_VAR_HANDLE expansions = build_template_dictionary(test_class, var_handle_kind, accessor, var_type) template = Template(""" ${banner} ${imports}; import java.lang.invoke.WrongMethodTypeException; public class ${test_class} extends VarHandleUnitTest { ${field_declarations}; private static final VarHandle vh; static { try { vh = ${lookup}; } catch (Exception e) { throw new RuntimeException("Unexpected initialization exception", e); } } @Override public void doTest() throws Exception { ${body} } public static void main(String[] args) { new ${test_class}().run(); } } """) with io.StringIO() as body_text: compatible_types = types_that_widen_to(var_type) incompatible_types = { RANDOM.choice(list(VALUE_TYPES - compatible_types)) } test_types = compatible_types | incompatible_types for value_type in test_types: print("try {", file=body_text) return_type = accessor.get_return_type(var_type) if return_type: print("{0} result = ({0}) ".format(return_type), end="", file=body_text) print("vh.{0}(this".format(accessor.method_name), end="", file=body_text) num_args = accessor.get_number_of_var_type_arguments() for i in range(0, num_args): print(", SampleValues.get_{0}({1})".format(value_type.boxed_type, i), end="", file=body_text) print(");", file=body_text) if value_type in compatible_types: print(" assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.{0}));".format(accessor.access_mode), file=body_text) else: print("failUnreachable();", file=body_text) print("} catch (WrongMethodTypeException e) {", file=body_text) print("} catch (UnsupportedOperationException e) {", file=body_text) print(" assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.{0}));".format(accessor.access_mode), file=body_text) print("}", file=body_text) expansions['body'] = body_text.getvalue(); with src_file_path.open("w") as src_file: print(template.safe_substitute(expansions), file=src_file) def emit_boxing_return_value_type_test(accessor, var_type, output_path): test_class = "BoxingReturn" + capitalize_first(accessor.method_name) + capitalize_first(var_type.name) GENERATED_TEST_CLASSES.append(test_class) src_file_path = output_path / java_file_for_class(test_class) var_handle_kind = FIELD_VAR_HANDLE expansions = build_template_dictionary(test_class, var_handle_kind, accessor, var_type) template = Template(""" ${banner} ${imports}; import java.lang.invoke.WrongMethodTypeException; public class ${test_class} extends VarHandleUnitTest { ${field_declarations}; private static final VarHandle vh; static { try { vh = ${lookup}; } catch (Exception e) { throw new RuntimeException("Unexpected initialization exception", e); } } @Override public void doTest() throws Exception { ${body} } public static void main(String[] args) { new ${test_class}().run(); } } """) with io.StringIO() as body_text: return_type = accessor.get_return_type(var_type) compatible_types = { return_type } incompatible_types = { RANDOM.choice(list(VALUE_TYPES - compatible_types)) } test_types = compatible_types | incompatible_types for value_type in test_types: print("try {", file=body_text) print("{0} result = ({0}) ".format(value_type.boxed_type), end="", file=body_text) print("vh.{0}(this".format(accessor.method_name), end="", file=body_text) num_args = accessor.get_number_of_var_type_arguments() for i in range(0, num_args): print(", {0})".format(var_type.examples[i]), end="", file=body_text) print(");", file=body_text) if value_type in compatible_types: print(" assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.{0}));".format(accessor.access_mode), file=body_text) else: print("failUnreachable();", file=body_text) print("} catch (WrongMethodTypeException e) {", file=body_text) print("} catch (UnsupportedOperationException e) {", file=body_text) print(" assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.{0}));".format(accessor.access_mode), file=body_text) print("}", file=body_text) expansions['body'] = body_text.getvalue(); with src_file_path.open("w") as src_file: print(template.safe_substitute(expansions), file=src_file) def emit_boxing_value_type_accessor_tests(output_path): for var_type in VALUE_TYPES: for accessor in VAR_HANDLE_ACCESSORS: if accessor.get_number_of_var_type_arguments() > 0: emit_boxing_value_type_accessor_test(accessor, var_type, output_path) else: emit_boxing_return_value_type_test(accessor, var_type, output_path) def emit_main(output_path, manual_test_classes): main_file_path = output_path / "Main.java" all_test_classes = GENERATED_TEST_CLASSES + manual_test_classes with main_file_path.open("w") as main_file: print("// " + BANNER, file=main_file) print(""" public class Main { public static void main(String[] args) { """, file=main_file) for cls in all_test_classes: print(" " + cls + ".main(args);", file=main_file) print(" VarHandleUnitTest.DEFAULT_COLLECTOR.printSummary();", file=main_file) print(" System.exit(VarHandleUnitTest.DEFAULT_COLLECTOR.failuresOccurred() ? 1 : 0);", file=main_file) print(" }\n}", file=main_file) def main(argv): final_java_dir = Path(argv[1]) if not final_java_dir.exists() or not final_java_dir.is_dir(): print("{} is not a valid java dir".format(final_java_dir), file=sys.stderr) sys.exit(1) emit_value_type_accessor_tests(final_java_dir) emit_reference_accessor_tests(final_java_dir) emit_interface_accessor_tests(final_java_dir) emit_boxing_value_type_accessor_tests(final_java_dir) emit_main(final_java_dir, argv[2:]) if __name__ == '__main__': main(sys.argv)