From 0d3692512e20365d61324fc507bc61892792468d Mon Sep 17 00:00:00 2001 From: kevstone Date: Thu, 8 Feb 2024 12:11:15 +0000 Subject: [PATCH] Added conversion from malloc to CONTAINER.resize() --- src/clang_convert/ast_object/ast_parser.py | 47 ++++++++++++------ .../code_object/cast_expression.py | 49 +++++++++++++++++++ .../code_object/function_call.py | 5 ++ .../cast_expression_converter.py | 19 +++++++ .../code_object_converter/conversion_funcs.py | 3 ++ .../conversion_pack/conversion_validation.py | 3 ++ .../grib_accessor_conversion_validation.py | 12 +++++ 7 files changed, 122 insertions(+), 16 deletions(-) create mode 100755 src/clang_convert/code_object/cast_expression.py create mode 100755 src/clang_convert/code_object_converter/cast_expression_converter.py diff --git a/src/clang_convert/ast_object/ast_parser.py b/src/clang_convert/ast_object/ast_parser.py index d9cc5e5b2..e5064348d 100755 --- a/src/clang_convert/ast_object/ast_parser.py +++ b/src/clang_convert/ast_object/ast_parser.py @@ -4,6 +4,7 @@ import clang.cindex import ast_object.ast_utils as ast_utils import code_object.array_access as array_access import code_object.binary_operation as binary_operation +import code_object.cast_expression as cast_expression import code_object.code_objects as code_objects import code_object.compound_statement as compound_statement import code_object.conditional_operation as conditional_operation @@ -516,19 +517,24 @@ class AstParser: return c_paren_expr def parse_CXX_UNARY_EXPR(self, node): + keyword = node.spelling if not keyword: # Some unary expressions (e.g. sizeof) give an empty keyword, but we can extract it - # from the first token + # from the first token. In this case we have no child nodes and have to extract + # the expression from the tokens as well tokens = [token.spelling for token in node.get_tokens()] keyword = tokens[0] + assert keyword == "sizeof", f"Unexpected keyword [{keyword}] - not able to parse this (yet!)" + expression_text = literal.Literal(" ".join([t for t in tokens[2:-1]])) + expression = paren_expression.ParenExpression(expression_text) + else: + children = list(node.get_children()) + assert len(children) == 1, f"Expected exactly one child for unary expression, got [{len(children)}]" + expression = self.parse_ast_node(children[0]) - children = list(node.get_children()) - assert len(children) == 1, f"Expected exactly one child for unary expression" - expression = children[0] - - expression_value = self.parse_ast_node(expression) - c_unary_expr = unary_expression.UnaryExpression(keyword, expression_value) + c_unary_expr = unary_expression.UnaryExpression(keyword, expression) + debug.line("parse_CXX_UNARY_EXPR", f"Created c_unary_expr=[{debug.as_debug_string(c_unary_expr)}]") return c_unary_expr def parse_UNARY_OPERATOR(self, node): @@ -670,16 +676,25 @@ class AstParser: def parse_CSTYLE_CAST_EXPR(self, node): - for child in node.get_children(): - if child.kind == clang.cindex.CursorKind.UNEXPOSED_EXPR: - # We ignore the cast, and just return the object - return literal.Literal(child.spelling) - - debug.line("parse_CSTYLE_CAST_EXPR", f"*** IGNORING *** child spelling=[{child.spelling}] type=[{child.type.spelling}] kind=[{child.kind}]") - cast_expression = "".join([t.spelling for t in node.get_tokens()]) - debug.line("parse_CSTYLE_CAST_EXPR", f"Commenting out cast expression [{cast_expression}]") - return as_commented_out_code(cast_expression, "Removing unecessary cast") + tokens = [token.spelling for token in node.get_tokens()] + assert tokens[0] == "(" + + # Extract the cast value by finding the first ')' [note this will throw if not found!] + cast_value_end_index = tokens.index(")") + + ccast_value = literal.Literal(f"{' '.join([t for t in tokens[1:cast_value_end_index]])}") + + children = list(node.get_children()) + child_count = len(children) + assert child_count == 1, f"Expected exactly one child node for cast expression, but got [{child_count}]" + + cexpression = self.parse_ast_node(children[0]) + + ccast_expression = cast_expression.CastExpression("C", ccast_value, cexpression) + + debug.line("parse_CSTYLE_CAST_EXPR", f"Created ccast_expression = [{debug.as_debug_string(ccast_expression)}]") + return ccast_expression def parse_ARRAY_SUBSCRIPT_EXPR(self, node): # We expect two children: the variable name and the index diff --git a/src/clang_convert/code_object/cast_expression.py b/src/clang_convert/code_object/cast_expression.py new file mode 100755 index 000000000..38ad6c726 --- /dev/null +++ b/src/clang_convert/code_object/cast_expression.py @@ -0,0 +1,49 @@ +import utils.debug as debug +import code_object.code_interface as code_interface +from utils.string_funcs import strip_semicolon + +# Represent a cast expression form CAST EXPRESSION +# +# CAST can be: +# 1. C-style e.g. (char*)foo; +# 2. C++ style e.g. static_cast(foo) + +cast_types = [ + "C", + "static", + "dynamic", + "reinterpret" +] + +class CastExpression(code_interface.CodeInterface): + def __init__(self, cast_type, cast_value, expression) -> None: + self._cast_type = cast_type + self._cast_value = cast_value + self._expression = expression + assert self._cast_type in cast_types, f"Invalid cast type [{self._cast_type}]" + assert isinstance(self._cast_value, code_interface.CodeInterface), f"Cast value must be a CodeInterface class" + assert isinstance(self._expression, code_interface.CodeInterface), f"Expression must be a CodeInterface class" + + @property + def cast_type(self): + return self._cast_type + + @property + def cast_value(self): + return self._cast_value + + @property + def expression(self): + return self._expression + + def as_lines(self): + + if self._cast_type == "C": + cast_expression_string = f"({self._cast_value.as_string()}){strip_semicolon(self._expression.as_string())}" + else: + cast_expression_string = f"{self._cast_type}_cast<{self._cast_value.as_string()}>({strip_semicolon(self._expression.as_string())})" + + if not cast_expression_string.endswith(";"): + cast_expression_string += ";" + + return [cast_expression_string] diff --git a/src/clang_convert/code_object/function_call.py b/src/clang_convert/code_object/function_call.py index cc4970682..f5590cbc1 100755 --- a/src/clang_convert/code_object/function_call.py +++ b/src/clang_convert/code_object/function_call.py @@ -21,6 +21,11 @@ class FunctionCall(code_interface.CodeInterface): def args(self): return self._args + # return the arguments as a comma-separated list + @property + def arg_string(self): + return ",".join([strip_semicolon(a.as_string()) for a in self._args]) + def add_arg(self, arg_entry): assert isinstance(arg_entry, code_interface.CodeInterface), f"arg_entry must be a CodeInterface class, supplied=[{arg_entry}]" self._args.append(arg_entry) diff --git a/src/clang_convert/code_object_converter/cast_expression_converter.py b/src/clang_convert/code_object_converter/cast_expression_converter.py new file mode 100755 index 000000000..7f0ed9025 --- /dev/null +++ b/src/clang_convert/code_object_converter/cast_expression_converter.py @@ -0,0 +1,19 @@ + +import utils.debug as debug +import code_object.cast_expression as cast_expression +import code_object_converter.code_interface_converter as code_interface_converter +import code_object_converter.conversion_funcs as conversion_funcs + +class CastExpressionConverter(code_interface_converter.CodeInterfaceConverter): + def __init__(self, ccode_object) -> None: + super().__init__(ccode_object) + assert isinstance(ccode_object, cast_expression.CastExpression), f"Expected CastExpression, got type=[{type(ccode_object)}]" + + def create_cpp_code_object(self, conversion_pack): + # By default, just switch to static_cast + cpp_cast_type = "static" + cpp_cast_value = conversion_funcs.convert_ccode_object(self._ccode_object.cast_value, conversion_pack) + cpp_expression = conversion_funcs.convert_ccode_object(self._ccode_object.expression, conversion_pack) + + cppcast_expression = cast_expression.CastExpression(cpp_cast_type, cpp_cast_value, cpp_expression) + return conversion_pack.conversion_validation.validate_cast_expression(self._ccode_object, cppcast_expression) diff --git a/src/clang_convert/code_object_converter/conversion_funcs.py b/src/clang_convert/code_object_converter/conversion_funcs.py index f361d9eee..f969f97a3 100755 --- a/src/clang_convert/code_object_converter/conversion_funcs.py +++ b/src/clang_convert/code_object_converter/conversion_funcs.py @@ -2,6 +2,7 @@ import code_object.array_access as arr_access import code_object.arg as arg import code_object.binary_operation as binary_operation +import code_object.cast_expression as cast_expression import code_object.code_objects as code_objects import code_object.compound_statement as compound_statement import code_object.conditional_operation as conditional_operation @@ -36,6 +37,7 @@ import code_object.while_statement as while_statement import code_object_converter.array_access_converter as array_access_converter import code_object_converter.arg_converter as arg_converter import code_object_converter.binary_operation_converter as binary_operation_converter +import code_object_converter.cast_expression_converter as cast_expression_converter import code_object_converter.code_objects_converter as code_objects_converter import code_object_converter.compound_statement_converter as compound_statement_converter import code_object_converter.conditional_operation_converter as conditional_operation_converter @@ -76,6 +78,7 @@ CodeInterfaceConverterClasses = { arr_access.ArrayAccess : array_access_converter.ArrayAccessConverter, arg.Arg : arg_converter.ArgConverter, binary_operation.BinaryOperation : binary_operation_converter.BinaryOperationConverter, + cast_expression.CastExpression : cast_expression_converter.CastExpressionConverter, code_objects.CodeObjects : code_objects_converter.CodeObjectsConverter, compound_statement.CompoundStatement : compound_statement_converter.CompoundStatementConverter, conditional_operation.ConditionalOperation : conditional_operation_converter.ConditionalOperationConverter, diff --git a/src/clang_convert/code_object_converter/conversion_pack/conversion_validation.py b/src/clang_convert/code_object_converter/conversion_pack/conversion_validation.py index f82d5accd..5bf9bcfb2 100755 --- a/src/clang_convert/code_object_converter/conversion_pack/conversion_validation.py +++ b/src/clang_convert/code_object_converter/conversion_pack/conversion_validation.py @@ -27,6 +27,9 @@ class ConversionValidation: def validate_binary_operation(self, cbinary_operation, cppbinary_operation): return cppbinary_operation + def validate_cast_expression(self, ccast_expression, cppcast_expression): + return cppcast_expression + def validate_code_objects(self, ccode_objects, cppcode_objects): return cppcode_objects diff --git a/src/clang_convert/grib_accessor/grib_accessor_conversion_pack/grib_accessor_conversion_validation.py b/src/clang_convert/grib_accessor/grib_accessor_conversion_pack/grib_accessor_conversion_validation.py index db7724883..55c93dede 100755 --- a/src/clang_convert/grib_accessor/grib_accessor_conversion_pack/grib_accessor_conversion_validation.py +++ b/src/clang_convert/grib_accessor/grib_accessor_conversion_pack/grib_accessor_conversion_validation.py @@ -13,11 +13,13 @@ import code_object.virtual_member_function as virtual_member_function import code_object.constructor_function as constructor_function import code_object.function_call as function_call import code_object_converter.conversion_pack.arg_utils as arg_utils +import code_object.cast_expression as cast_expression from grib_accessor.grib_accessor_conversion_pack.grib_accessor_special_function_call_conversion import special_function_name_mapping from code_object.code_interface import NONE_VALUE import grib_accessor.grib_accessor_conversion_pack.grib_accessor_type_info as grib_accessor_type_info from code_object_converter.conversion_funcs import as_commented_out_code + # Pass this to the conversion_data object to be accessed by the conversion routines class GribAccessorConversionValidation(default_conversion_validation.DefaultConversionValidation): @@ -126,6 +128,16 @@ class GribAccessorConversionValidation(default_conversion_validation.DefaultConv if cppbinary_operation.right_operand.as_string().startswith("GribAccessorFlag"): updated_right_operand = literal.Literal(f"toInt({cppbinary_operation.right_operand.as_string()})") return binary_operation.BinaryOperation(cppbinary_operation.left_operand, cppbinary_operation.binary_op, updated_right_operand) + + cppright = cppbinary_operation.right_operand + if isinstance(cppright, cast_expression.CastExpression): + cpp_func_call = cppright.expression + assert isinstance(cpp_func_call, function_call.FunctionCall), f"Expected cast expression to be a FunctionCall, not [{type(cpp_func_call)}]" + if cpp_func_call.name in ["gribContextMalloc", "gribContextRealloc"]: + # For now, we'll assume we're allocating a container (may need to revisit) + cpp_alloc = literal.Literal(f"{arg_utils.extract_name(cppbinary_operation.left_operand)}.resize({cpp_func_call.arg_string});") + debug.line("validate_binary_operation", f"Changed allocation operation from=[{debug.as_debug_string(cppbinary_operation)}] to [{debug.as_debug_string(cpp_alloc)}]") + return cpp_alloc return super().validate_binary_operation(cbinary_operation, cppbinary_operation)