| #!/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) |
| |
| # For indexable types we need to check out-of-bounds access at negative index. |
| # We always generate the check, but comment it out for non-indexable types. |
| dictionary['coordinates_negative_index'] = coordinates.replace('index', '-16') |
| dictionary['indexable_only'] = "//" if not re.search('Array|ByteBuffer', var_handle_kind.name) else "" |
| |
| 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); |
| // Check for out of bounds access (for indexable types only). |
| ${indexable_only} try { |
| ${indexable_only} value = (${var_type}) vh.${accessor_method}(${coordinates_negative_index}); |
| ${indexable_only} failUnreachable(); |
| ${indexable_only} } catch (IndexOutOfBoundsException ex) {}""") |
| elif accessor.access_mode_form == AccessModeForm.SET: |
| test_template = Template(""" |
| vh.${accessor_method}(${coordinates}${updated_value}); |
| assertEquals(${updated_value}, ${read_value}); |
| // Check for out of bounds access (for indexable types only). |
| ${indexable_only} try { |
| ${indexable_only} vh.${accessor_method}(${coordinates_negative_index}${updated_value}); |
| ${indexable_only} failUnreachable(); |
| ${indexable_only} } catch (IndexOutOfBoundsException ex) {}""") |
| 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}); |
| // Check for out of bounds access (for indexable types only). |
| ${indexable_only} try { |
| ${indexable_only} applied = (boolean) vh.${accessor_method}(${coordinates_negative_index}${updated_value}, ${updated_value}); |
| ${indexable_only} failUnreachable(); |
| ${indexable_only} } catch (IndexOutOfBoundsException ex) {}""") |
| 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}); |
| // Check for out of bounds access (for indexable types only). |
| ${indexable_only} try { |
| ${indexable_only} applied = (boolean) vh.${accessor_method}(${coordinates_negative_index}${updated_value}, ${updated_value}); |
| ${indexable_only} failUnreachable(); |
| ${indexable_only} } catch (IndexOutOfBoundsException ex) {}""") |
| 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}); |
| // Check for out of bounds access (for indexable types only). |
| ${indexable_only} try { |
| ${indexable_only} witness_value = (${var_type}) vh.${accessor_method}(${coordinates_negative_index}${updated_value}, ${updated_value}); |
| ${indexable_only} failUnreachable(); |
| ${indexable_only} } catch (IndexOutOfBoundsException ex) {}""") |
| 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}); |
| // Check for out of bounds access (for indexable types only). |
| ${indexable_only} try { |
| ${indexable_only} old_value = (${var_type}) vh.${accessor_method}(${coordinates_negative_index}${updated_value}); |
| ${indexable_only} failUnreachable(); |
| ${indexable_only} } catch (IndexOutOfBoundsException ex) {}""") |
| 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}); |
| // Check for out of bounds access (for indexable types only). |
| ${indexable_only} try { |
| ${indexable_only} old_value = (${var_type}) vh.${accessor_method}(${coordinates_negative_index}${updated_value}); |
| ${indexable_only} failUnreachable(); |
| ${indexable_only} } catch (IndexOutOfBoundsException ex) {}""") |
| else: |
| test_template = Template(""" |
| vh.${accessor_method}(${coordinates}${initial_value}, ${updated_value}); |
| failUnreachable(); |
| // Check for out of bounds access (for indexable types only). |
| ${indexable_only} try { |
| ${indexable_only} vh.${accessor_method}(${coordinates_negative_index}${updated_value}, ${updated_value}); |
| ${indexable_only} failUnreachable(); |
| ${indexable_only} } catch (IndexOutOfBoundsException ex) {}""") |
| 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}); |
| // Check for out of bounds access (for indexable types only). |
| ${indexable_only} try { |
| ${indexable_only} old_value = (${var_type}) vh.${accessor_method}(${coordinates_negative_index}${updated_value}); |
| ${indexable_only} failUnreachable(); |
| ${indexable_only} } catch (IndexOutOfBoundsException ex) {}""") |
| else: |
| test_template = Template(""" |
| vh.${accessor_method}(${coordinates}${initial_value}, ${updated_value}); |
| failUnreachable(); |
| // Check for out of bounds access (for indexable types only). |
| ${indexable_only} try { |
| ${indexable_only} vh.${accessor_method}(${coordinates_negative_index}${updated_value}, ${updated_value}); |
| ${indexable_only} failUnreachable(); |
| ${indexable_only} } catch (IndexOutOfBoundsException ex) {}""") |
| 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) |