You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
892 lines
44 KiB
892 lines
44 KiB
#!/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)
|