blob: 763fb201e939a467516fb37d5c08b9f7cca995b5 [file] [log] [blame]
#!/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)