diff --git a/src/java_bytecode/java_types.cpp b/src/java_bytecode/java_types.cpp index 752c3c22dd8..92b2ab27dcb 100644 --- a/src/java_bytecode/java_types.cpp +++ b/src/java_bytecode/java_types.cpp @@ -22,6 +22,16 @@ Author: Daniel Kroening, kroening@kroening.com #include #endif +std::vector parse_list_types( + const std::string src, + const std::string class_name_prefix, + const char opening_bracket, + const char closing_bracket); + +size_t find_closing_semi_colon_for_reference_type( + const std::string src, + size_t starting_point = 0); + typet java_int_type() { return signedbv_typet(32); @@ -167,6 +177,240 @@ exprt java_bytecode_promotion(const exprt &expr) return typecast_exprt(expr, new_type); } +/// Take a list of generic arguments and parse them into the generic type. +/// \param generic_type [out]: The existing generic type to add the information +/// to +/// \param parameters: The string representing the generic arguments for a +/// signature. For example (including wrapping angle +/// brackets). +/// \param class_name_prefix: The name of the class to use to prefix any found +/// generic types +void add_generic_type_information( + java_generic_typet &generic_type, + const std::string ¶meters, + const std::string &class_name_prefix) +{ + PRECONDITION(parameters.size() >= 2); + PRECONDITION(parameters[0] == '<'); + PRECONDITION(parameters[parameters.size() - 1] == '>'); + + // parse contained types, can be either type variables, starting with T + // or instantiated types + std::vector params = + parse_list_types(parameters, class_name_prefix, '<', '>'); + + CHECK_RETURN(!params.empty()); // We should have at least one generic param + + // take these types - they should either be java_generic_parameters, in which + // case they can be directly added to the generic_type + // otherwise they should be wrapped in a java_generic_inst_parametert + + std::transform( + params.begin(), + params.end(), + std::back_inserter(generic_type.generic_type_variables()), + [](const typet &type) -> java_generic_parametert { + if(is_java_generic_parameter(type)) + { + return to_java_generic_parameter(type); + } + else + { + INVARIANT( + is_reference(type), "All generic parameters should be references"); + return java_generic_inst_parametert(to_symbol_type(type.subtype())); + } + }); +} + +/// Take a signature string and remove everything in angle brackets allowing +/// for the type to be parsed normally. +/// \param src: The input string +/// \return The input string with everything between angle brackets removed +std::string erase_type_arguments(const std::string &src) +{ + std::string class_name = src; + std::size_t f_pos = class_name.find('<', 1); + + while(f_pos != std::string::npos) + { + std::size_t e_pos = find_closing_delimiter(class_name, f_pos, '<', '>'); + if(e_pos == std::string::npos) + { + throw unsupported_java_class_signature_exceptiont( + "Failed to find generic signature closing delimiter (or recursive " + "generic): " + + src); + } + + // erase generic information between brackets + class_name.erase(f_pos, e_pos - f_pos + 1); + + // Search the remainder of the string for generic signature + f_pos = class_name.find('<', e_pos + 1); + } + return class_name; +} + +/// Returns the full class name, skipping over the generics. +/// \param src: a type descriptor or signature +/// 1. Signature: Lcom/package/OuterClass.Inner; +/// 2. Descriptor: Lcom.pacakge.OuterClass$Inner; +/// \return The full name of the class like com.package.OuterClass.Inner (for +/// both examples). +std::string gather_full_class_name(const std::string &src) +{ + PRECONDITION(src.size() >= 2); + PRECONDITION(src[0] == 'L'); + PRECONDITION(src[src.size() - 1] == ';'); + + std::string class_name = src.substr(1, src.size() - 2); + + class_name = erase_type_arguments(class_name); + + std::replace(class_name.begin(), class_name.end(), '.', '$'); + std::replace(class_name.begin(), class_name.end(), '/', '.'); + return class_name; +} + +/// Given a substring of a descriptor or signature that contains one or more +/// types parse out the individual types. This is used for parsing the +/// parameters of a function or the generic arguments contained within angle +/// brackets. +/// \param src: The input string that is wrapped in either ( ) or < > +/// \param class_name_prefix: The name of the class to use to prefix any found +/// generic types +/// \param opening_bracket: For checking string is passed in as expected, the +/// opening bracket (i.e. '(' or '<'). +/// \param closing_bracket: For checking string is passed in as expected, the +/// closing bracket (i.e. ')' or '>'). +/// \return A vector of types that the string represents +std::vector parse_list_types( + const std::string src, + const std::string class_name_prefix, + const char opening_bracket, + const char closing_bracket) +{ + PRECONDITION(src.size() >= 2); + PRECONDITION(src[0] == opening_bracket); + PRECONDITION(src[src.size() - 1] == closing_bracket); + + // Loop over the types in the given string, parsing each one in turn + // and adding to the type_list + std::vector type_list; + for(std::size_t i = 1; i < src.size() - 1; i++) + { + size_t start = i; + while(i < src.size()) + { + // parameter is an object type or instantiated generic type + if(src[i] == 'L') + { + i = find_closing_semi_colon_for_reference_type(src, i); + break; + } + + // parameter is an array + else if(src[i] == '[') + i++; + + // parameter is a type variable + else if(src[i] == 'T') + i = src.find(';', i); // ends on ; + + // type is an atomic type (just one character) + else + { + break; + } + } + + std::string sub_str = src.substr(start, i - start + 1); + const typet &new_type = java_type_from_string(sub_str, class_name_prefix); + INVARIANT(new_type != nil_typet(), "Failed to parse type"); + + type_list.push_back(new_type); + } + return type_list; +} + +/// For parsing a class type reference +/// \param src: The input string +/// Either a signature: "Lpackage/class.innerclass; +/// Or a descriptor: "Lpackage.class$inner; +/// \param class_name_prefix: The name of the class to use to prefix any found +/// generic types +/// \return The reference type if parsed correctly, no value if parsing fails. +reference_typet +build_class_name(const std::string &src, const std::string &class_name_prefix) +{ + PRECONDITION(src[0] == 'L'); + + // All reference types must end on a ; + PRECONDITION(src[src.size() - 1] == ';'); + + std::string container_class = gather_full_class_name(src); + std::string identifier = "java::" + container_class; + symbol_typet symbol_type(identifier); + symbol_type.set(ID_C_base_name, container_class); + + std::size_t f_pos = src.find('<', 1); + if(f_pos != std::string::npos) + { + java_generic_typet result(symbol_type); + // get generic type information + do + { + std::size_t e_pos = find_closing_delimiter(src, f_pos, '<', '>'); + if(e_pos == std::string::npos) + throw unsupported_java_class_signature_exceptiont( + "Parsing type with unmatched generic bracket: " + src); + + add_generic_type_information( + result, src.substr(f_pos, e_pos - f_pos + 1), class_name_prefix); + + // Look for the next generic type info (if it exists) + f_pos = src.find('<', e_pos + 1); + } while(f_pos != std::string::npos); + return result; + } + + return java_reference_type(symbol_type); +} + +/// Finds the closing semi-colon ending a ClassTypeSignature that starts at +/// \p starting_point. +/// \param src: The input string to work on. +/// \param starting_point: The string position where the opening 'L' we want to +/// find the closing ';' for. +/// \return The string position corresponding to the matching ';'. For example: +/// LA;, we would return 2. For LA; we would return 7. +/// See unit/java_bytecode/java_util_tests.cpp for more examples. +size_t find_closing_semi_colon_for_reference_type( + const std::string src, + size_t starting_point) +{ + PRECONDITION(src[starting_point] == 'L'); + size_t next_semi_colon = src.find(';', starting_point); + INVARIANT( + next_semi_colon != std::string::npos, + "There must be a semi-colon somewhere in the input"); + size_t next_angle_bracket = src.find('<', starting_point); + + while(next_angle_bracket < next_semi_colon) + { + size_t end_bracket = + find_closing_delimiter(src, next_angle_bracket, '<', '>'); + INVARIANT( + end_bracket != std::string::npos, "Must find matching angle bracket"); + + next_semi_colon = src.find(';', end_bracket + 1); + next_angle_bracket = src.find('<', end_bracket + 1); + } + + return next_semi_colon; +} + /// Transforms a string representation of a Java type into an internal type /// representation thereof. /// @@ -240,49 +484,15 @@ typet java_type_from_string( std::string(src, e_pos+1, std::string::npos), class_name_prefix); - for(std::size_t i=1; i' - i=find_closing_delimiter(src, generic_open, '<', '>')+1; - else - i=src.find(';', i); // ends on ; - break; - } - - // parameter is an array - else if(src[i]=='[') - i++; - - // parameter is a type variable - else if(src[i]=='T') - i=src.find(';', i); // ends on ; - - // type is an atomic type (just one character) - else - break; - } - - std::string sub_str=src.substr(start, i-start+1); - param.type()=java_type_from_string(sub_str, class_name_prefix); - - if(param.type().id()==ID_symbol) - param.type()=java_reference_type(param.type()); - - result.parameters().push_back(param); - } + std::vector param_types = + parse_list_types(src.substr(0, e_pos + 1), class_name_prefix, '(', ')'); + + // create parameters for each type + std::transform( + param_types.begin(), + param_types.end(), + std::back_inserter(result.parameters()), + [](const typet &type) { return code_typet::parametert(type); }); return result; } @@ -327,108 +537,7 @@ typet java_type_from_string( } case 'L': { - // ends on ; - if(src[src.size()-1]!=';') - return nil_typet(); - - std::size_t f_pos=src.find('<', 1); - // get generic type information - if(f_pos!=std::string::npos) - { - std::size_t e_pos=find_closing_delimiter(src, f_pos, '<', '>'); - if(e_pos==std::string::npos) - throw unsupported_java_class_signature_exceptiont( - "recursive generic"); - - // construct container type - std::string generic_container_class=src.substr(1, f_pos-1); - - for(unsigned i=0; i type_variablest; java_generic_parametert( const irep_idt &_type_var_name, @@ -108,6 +107,8 @@ class java_generic_parametert:public reference_typet return type_variables().front(); } +private: + typedef std::vector type_variablest; const type_variablest &type_variables() const { return (const type_variablest &)(find(ID_type_variables).get_sub()); diff --git a/src/java_bytecode/java_utils.cpp b/src/java_bytecode/java_utils.cpp index 8f366c324e3..e3b20a51224 100644 --- a/src/java_bytecode/java_utils.cpp +++ b/src/java_bytecode/java_utils.cpp @@ -214,9 +214,9 @@ size_t find_closing_delimiter( while(c_pos<=end_pos) { - if(src[c_pos]=='<') + if(src[c_pos] == open_char) depth++; - else if(src[c_pos]=='>') + else if(src[c_pos] == close_char) { if(depth==0) return c_pos; diff --git a/unit/java_bytecode/java_bytecode_parse_generics/GenericClass$InnerClass.class b/unit/java_bytecode/java_bytecode_parse_generics/GenericClass$InnerClass.class new file mode 100644 index 00000000000..5a263fcbd4b Binary files /dev/null and b/unit/java_bytecode/java_bytecode_parse_generics/GenericClass$InnerClass.class differ diff --git a/unit/java_bytecode/java_bytecode_parse_generics/GenericClass.class b/unit/java_bytecode/java_bytecode_parse_generics/GenericClass.class new file mode 100644 index 00000000000..3b60626bea6 Binary files /dev/null and b/unit/java_bytecode/java_bytecode_parse_generics/GenericClass.class differ diff --git a/unit/java_bytecode/java_bytecode_parse_generics/GenericClass.java b/unit/java_bytecode/java_bytecode_parse_generics/GenericClass.java new file mode 100644 index 00000000000..8c13ed4f0db --- /dev/null +++ b/unit/java_bytecode/java_bytecode_parse_generics/GenericClass.java @@ -0,0 +1,109 @@ +public class GenericClass +{ + class InnerClass + { + } + + class GenericInnerClass + { + V field; + + class DoublyNestedInnerClass + { + + } + + class DoublyNestedInnerGenericClass + { + T field; + } + } + + class SameGenericParamInnerClass + { + T field; + } + + InnerClass field; + GenericInnerClass field2; + GenericInnerClass field3; + + GenericInnerClass.DoublyNestedInnerClass field4; + GenericInnerClass.DoublyNestedInnerClass field5; + + GenericInnerClass.DoublyNestedInnerGenericClass field6; + GenericInnerClass.DoublyNestedInnerGenericClass field7; + + void method(InnerClass input) + { + + } + + void method2(InnerClass input, InnerClass input2) + { + + } + + + void method3(GenericInnerClass input) + { + + } + + void method4(GenericInnerClass input) + { + + } + + void method5(GenericInnerClass.DoublyNestedInnerClass input) + { + + } + + void method6(GenericInnerClass.DoublyNestedInnerClass input) + { + + } + + void method7(GenericInnerClass.DoublyNestedInnerGenericClass input) + { + + } + + void method8(GenericInnerClass.DoublyNestedInnerGenericClass input) + { + + } + + InnerClass ret_method1() + { + return null; + } + + GenericInnerClass ret_method2() + { + return null; + } + + GenericInnerClass ret_method3() + { + return null; + } + + GenericInnerClass.DoublyNestedInnerClass ret_method4() + { + return null; + } + GenericInnerClass.DoublyNestedInnerClass ret_method5() + { + return null; + } + GenericInnerClass.DoublyNestedInnerGenericClass ret_method6() + { + return null; + } + GenericInnerClass.DoublyNestedInnerGenericClass ret_method7() + { + return null; + } +} diff --git a/unit/java_bytecode/java_bytecode_parse_generics/parse_generic_inner_class.cpp b/unit/java_bytecode/java_bytecode_parse_generics/parse_generic_inner_class.cpp new file mode 100644 index 00000000000..acb09972a88 --- /dev/null +++ b/unit/java_bytecode/java_bytecode_parse_generics/parse_generic_inner_class.cpp @@ -0,0 +1,935 @@ +/*******************************************************************\ + + Module: Unit tests for parsing generic classes + + Author: DiffBlue Limited. All rights reserved. + +\*******************************************************************/ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include + +SCENARIO( + "Parse fields of inner classes on a generic class", + "[core][java_bytecode][java_bytecode_parse_generics]") +{ + const symbol_tablet &new_symbol_table = load_java_class( + "GenericClass", "./java_bytecode/java_bytecode_parse_generics"); + + std::string class_prefix = "java::GenericClass"; + THEN("There should be a symbol for GenericClass with correct components") + { + REQUIRE(new_symbol_table.has_symbol(class_prefix)); + const symbolt &class_symbol = new_symbol_table.lookup_ref(class_prefix); + + const class_typet &class_type = + require_symbol::require_complete_class(class_symbol); + + THEN("The field component should be a pointer to GenericClass$InnerClass") + { + const struct_typet::componentt &field_component = + require_type::require_component(class_type, "field"); + + require_type::require_pointer( + field_component.type(), symbol_typet("java::GenericClass$InnerClass")); + + THEN("The pointer should be generic") + { + REQUIRE(is_java_generic_type(field_component.type())); + const auto &generic_variables = + to_java_generic_type(field_component.type()).generic_type_variables(); + REQUIRE(generic_variables.size() == 1); + const java_generic_parametert &generic_param = generic_variables[0]; + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + } + + THEN("The field component should be a pointer to GenericClass$InnerClass") + { + const struct_typet::componentt &field_component = + require_type::require_component(class_type, "field2"); + + require_type::require_pointer( + field_component.type(), + symbol_typet("java::GenericClass$GenericInnerClass")); + + THEN("The pointer should be generic") + { + REQUIRE(is_java_generic_type(field_component.type())); + const auto &generic_variables = + to_java_generic_type(field_component.type()).generic_type_variables(); + REQUIRE(generic_variables.size() == 2); + { + const java_generic_parametert &generic_param = generic_variables[0]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + { + const java_generic_parametert &generic_param = generic_variables[1]; + REQUIRE(is_java_generic_inst_parameter(generic_param)); + REQUIRE( + generic_param == + java_generic_inst_parametert(symbol_typet("java::Foo"))); + } + } + } + + THEN( + "The field component should be a pointer to " + "GenericClass$GenericInnerClass$DoublyNestedInnerClass") + { + const struct_typet::componentt &field_component = + require_type::require_component(class_type, "field4"); + + require_type::require_pointer( + field_component.type(), + symbol_typet( + "java::GenericClass$GenericInnerClass$DoublyNestedInnerClass")); + + THEN("The pointer should be generic") + { + REQUIRE(is_java_generic_type(field_component.type())); + const auto &generic_variables = + to_java_generic_type(field_component.type()).generic_type_variables(); + REQUIRE(generic_variables.size() == 2); + { + const java_generic_parametert &generic_param = generic_variables[0]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + { + const java_generic_parametert &generic_param = generic_variables[1]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param == + java_generic_inst_parametert(symbol_typet("java::Foo"))); + } + } + } + + THEN( + "The field component should be a pointer to " + "GenericClass$GenericInnerClass$DoublyNestedInnerClass") + { + const struct_typet::componentt &field_component = + require_type::require_component(class_type, "field5"); + + require_type::require_pointer( + field_component.type(), + symbol_typet( + "java::GenericClass$GenericInnerClass$DoublyNestedInnerClass")); + + THEN("The pointer should be generic") + { + REQUIRE(is_java_generic_type(field_component.type())); + const auto &generic_variables = + to_java_generic_type(field_component.type()).generic_type_variables(); + REQUIRE(generic_variables.size() == 2); + { + const java_generic_parametert &generic_param = generic_variables[0]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + { + const java_generic_parametert &generic_param = generic_variables[1]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + } + } + + THEN( + "The field component should be a pointer to " + "GenericClass$GenericInnerClass$DoublyNestedInnerGenericClass") + { + const struct_typet::componentt &field_component = + require_type::require_component(class_type, "field6"); + + require_type::require_pointer( + field_component.type(), + symbol_typet( + "java::GenericClass$GenericInnerClass$" + "DoublyNestedInnerGenericClass")); + + THEN("The pointer should be generic") + { + REQUIRE(is_java_generic_type(field_component.type())); + const auto &generic_variables = + to_java_generic_type(field_component.type()).generic_type_variables(); + REQUIRE(generic_variables.size() == 3); + { + const java_generic_parametert &generic_param = generic_variables[0]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + { + const java_generic_parametert &generic_param = generic_variables[1]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param == + java_generic_inst_parametert(symbol_typet("java::Foo"))); + } + { + const java_generic_parametert &generic_param = generic_variables[2]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param == + java_generic_inst_parametert(symbol_typet("java::Foo"))); + } + } + } + + THEN( + "The field component should be a pointer to " + "GenericClass$GenericInnerClass$DoublyNestedInnerGenericClass") + { + const struct_typet::componentt &field_component = + require_type::require_component(class_type, "field7"); + + require_type::require_pointer( + field_component.type(), + symbol_typet( + "java::GenericClass$GenericInnerClass$" + "DoublyNestedInnerGenericClass")); + + THEN("The pointer should be generic") + { + REQUIRE(is_java_generic_type(field_component.type())); + const auto &generic_variables = + to_java_generic_type(field_component.type()).generic_type_variables(); + REQUIRE(generic_variables.size() == 3); + { + const java_generic_parametert &generic_param = generic_variables[0]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + { + const java_generic_parametert &generic_param = generic_variables[1]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + { + const java_generic_parametert &generic_param = generic_variables[2]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + } + } + } +} + +SCENARIO( + "Parse methods of generic classes using inner parameters", + "[core][java_bytecode][java_bytecode_parse_generics][caskjd]") +{ + const symbol_tablet &new_symbol_table = load_java_class( + "GenericClass", "./java_bytecode/java_bytecode_parse_generics"); + + std::string class_prefix = "java::GenericClass"; + + THEN("Method 1 should take a pointer to GenericClass$InnerClass") + { + const std::string func_name = ".method"; + const std::string func_descriptor = ":(LGenericClass$InnerClass;)V"; + const std::string process_func_name = + class_prefix + func_name + func_descriptor; + + REQUIRE(new_symbol_table.has_symbol(process_func_name)); + const symbolt &function_symbol = + new_symbol_table.lookup_ref(process_func_name); + + const code_typet &function_call = + require_type::require_code(function_symbol.type); + + const auto param_type = + require_type::require_parameter(function_call, "input"); + require_type::require_pointer( + param_type.type(), symbol_typet("java::GenericClass$InnerClass")); + + THEN("The pointer should be generic") + { + REQUIRE(is_java_generic_type(param_type.type())); + const auto &generic_variables = + to_java_generic_type(param_type.type()).generic_type_variables(); + REQUIRE(generic_variables.size() == 1); + const java_generic_parametert &generic_param = generic_variables[0]; + REQUIRE( + generic_param.type_variable() == symbol_typet("java::GenericClass::T")); + } + } + THEN("Method 2 should take two pointers to GenericClass$InnerClass") + { + const std::string func_name = ".method2"; + const std::string func_descriptor = + ":(LGenericClass$InnerClass;LGenericClass$InnerClass;)V"; + const std::string process_func_name = + class_prefix + func_name + func_descriptor; + + REQUIRE(new_symbol_table.has_symbol(process_func_name)); + const symbolt &function_symbol = + new_symbol_table.lookup_ref(process_func_name); + + const code_typet &function_call = + require_type::require_code(function_symbol.type); + + // Check param input + { + const auto param_type = + require_type::require_parameter(function_call, "input"); + require_type::require_pointer( + param_type.type(), symbol_typet("java::GenericClass$InnerClass")); + + THEN("The pointer should be generic") + { + REQUIRE(is_java_generic_type(param_type.type())); + const auto &generic_variables = + to_java_generic_type(param_type.type()).generic_type_variables(); + REQUIRE(generic_variables.size() == 1); + const java_generic_parametert &generic_param = generic_variables[0]; + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + } + + // Check param input2 + { + const auto param_type2 = + require_type::require_parameter(function_call, "input2"); + require_type::require_pointer( + param_type2.type(), symbol_typet("java::GenericClass$InnerClass")); + + THEN("The pointer should be generic") + { + REQUIRE(is_java_generic_type(param_type2.type())); + const auto &generic_variables = + to_java_generic_type(param_type2.type()).generic_type_variables(); + REQUIRE(generic_variables.size() == 1); + const java_generic_parametert &generic_param = generic_variables[0]; + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + } + } + THEN("Method 3 should take a pointer to GenericClass$GenericInnerClass") + { + const std::string func_name = ".method3"; + const std::string func_descriptor = ":(LGenericClass$GenericInnerClass;)V"; + const std::string process_func_name = + class_prefix + func_name + func_descriptor; + + REQUIRE(new_symbol_table.has_symbol(process_func_name)); + const symbolt &function_symbol = + new_symbol_table.lookup_ref(process_func_name); + + const code_typet &function_call = + require_type::require_code(function_symbol.type); + + const auto param_type = + require_type::require_parameter(function_call, "input"); + require_type::require_pointer( + param_type.type(), symbol_typet("java::GenericClass$GenericInnerClass")); + + THEN("The pointer should be generic") + { + REQUIRE(is_java_generic_type(param_type.type())); + const auto &generic_variables = + to_java_generic_type(param_type.type()).generic_type_variables(); + REQUIRE(generic_variables.size() == 2); + { + const java_generic_parametert &generic_param = generic_variables[0]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + { + const java_generic_parametert &generic_param = generic_variables[1]; + REQUIRE(is_java_generic_inst_parameter(generic_param)); + REQUIRE( + generic_param == + java_generic_inst_parametert(symbol_typet("java::Foo"))); + } + } + } + THEN("Method 4 should take a pointer to GenericClass$GenericInnerClass") + { + const std::string func_name = ".method4"; + const std::string func_descriptor = ":(LGenericClass$GenericInnerClass;)V"; + const std::string process_func_name = + class_prefix + func_name + func_descriptor; + + REQUIRE(new_symbol_table.has_symbol(process_func_name)); + const symbolt &function_symbol = + new_symbol_table.lookup_ref(process_func_name); + + const code_typet &function_call = + require_type::require_code(function_symbol.type); + + const auto param_type = + require_type::require_parameter(function_call, "input"); + require_type::require_pointer( + param_type.type(), symbol_typet("java::GenericClass$GenericInnerClass")); + + THEN("The pointer should be generic") + { + REQUIRE(is_java_generic_type(param_type.type())); + const auto &generic_variables = + to_java_generic_type(param_type.type()).generic_type_variables(); + REQUIRE(generic_variables.size() == 2); + { + const java_generic_parametert &generic_param = generic_variables[0]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + { + const java_generic_parametert &generic_param = generic_variables[1]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + } + } + THEN( + "Method 5 should take a pointer to " + "GenericClass$GenericInnerClass$DoublyNestedInnerClass") + { + const std::string func_name = ".method5"; + const std::string func_descriptor = + ":(LGenericClass$GenericInnerClass$DoublyNestedInnerClass;)V"; + const std::string process_func_name = + class_prefix + func_name + func_descriptor; + + REQUIRE(new_symbol_table.has_symbol(process_func_name)); + const symbolt &function_symbol = + new_symbol_table.lookup_ref(process_func_name); + + const code_typet &function_call = + require_type::require_code(function_symbol.type); + + const auto param_type = + require_type::require_parameter(function_call, "input"); + require_type::require_pointer( + param_type.type(), + symbol_typet( + "java::GenericClass$GenericInnerClass$DoublyNestedInnerClass")); + + THEN("The pointer should be generic") + { + REQUIRE(is_java_generic_type(param_type.type())); + const auto &generic_variables = + to_java_generic_type(param_type.type()).generic_type_variables(); + REQUIRE(generic_variables.size() == 2); + { + const java_generic_parametert &generic_param = generic_variables[0]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + { + const java_generic_parametert &generic_param = generic_variables[1]; + REQUIRE(is_java_generic_inst_parameter(generic_param)); + REQUIRE( + generic_param == + java_generic_inst_parametert(symbol_typet("java::Foo"))); + } + } + } + THEN( + "Method 6 should take a pointer to " + "GenericClass$GenericInnerClass$DoublyNestedInnerClass") + { + const std::string func_name = ".method6"; + const std::string func_descriptor = + ":(LGenericClass$GenericInnerClass$DoublyNestedInnerClass;)V"; + const std::string process_func_name = + class_prefix + func_name + func_descriptor; + + REQUIRE(new_symbol_table.has_symbol(process_func_name)); + const symbolt &function_symbol = + new_symbol_table.lookup_ref(process_func_name); + + const code_typet &function_call = + require_type::require_code(function_symbol.type); + + const auto param_type = + require_type::require_parameter(function_call, "input"); + require_type::require_pointer( + param_type.type(), + symbol_typet( + "java::GenericClass$GenericInnerClass$DoublyNestedInnerClass")); + + THEN("The pointer should be generic") + { + REQUIRE(is_java_generic_type(param_type.type())); + const auto &generic_variables = + to_java_generic_type(param_type.type()).generic_type_variables(); + REQUIRE(generic_variables.size() == 2); + { + const java_generic_parametert &generic_param = generic_variables[0]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + { + const java_generic_parametert &generic_param = generic_variables[1]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + } + } + THEN( + "Method 7 should take a pointer to " + "GenericClass$GenericInnerClass$DoublyNestedInnerGenericClass") + { + const std::string func_name = ".method7"; + const std::string func_descriptor = + ":(LGenericClass$GenericInnerClass$DoublyNestedInnerGenericClass;)V"; + const std::string process_func_name = + class_prefix + func_name + func_descriptor; + + REQUIRE(new_symbol_table.has_symbol(process_func_name)); + const symbolt &function_symbol = + new_symbol_table.lookup_ref(process_func_name); + + const code_typet &function_call = + require_type::require_code(function_symbol.type); + + const auto param_type = + require_type::require_parameter(function_call, "input"); + require_type::require_pointer( + param_type.type(), + symbol_typet( + "java::GenericClass$GenericInnerClass$" + "DoublyNestedInnerGenericClass")); + + THEN("The pointer should be generic") + { + REQUIRE(is_java_generic_type(param_type.type())); + const auto &generic_variables = + to_java_generic_type(param_type.type()).generic_type_variables(); + REQUIRE(generic_variables.size() == 3); + { + const java_generic_parametert &generic_param = generic_variables[0]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + { + const java_generic_parametert &generic_param = generic_variables[1]; + REQUIRE(is_java_generic_inst_parameter(generic_param)); + REQUIRE( + generic_param == + java_generic_inst_parametert(symbol_typet("java::Foo"))); + } + { + const java_generic_parametert &generic_param = generic_variables[2]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param == + java_generic_inst_parametert(symbol_typet("java::Foo"))); + } + } + } + THEN( + "Method 8 should take a pointer to " + "GenericClass$GenericInnerClass$DoublyNestedInnerGenericClass") + { + const std::string func_name = ".method8"; + const std::string func_descriptor = + ":(LGenericClass$GenericInnerClass$DoublyNestedInnerGenericClass;)V"; + const std::string process_func_name = + class_prefix + func_name + func_descriptor; + + REQUIRE(new_symbol_table.has_symbol(process_func_name)); + const symbolt &function_symbol = + new_symbol_table.lookup_ref(process_func_name); + + const code_typet &function_call = + require_type::require_code(function_symbol.type); + + const auto param_type = + require_type::require_parameter(function_call, "input"); + require_type::require_pointer( + param_type.type(), + symbol_typet( + "java::GenericClass$GenericInnerClass$" + "DoublyNestedInnerGenericClass")); + + THEN("The pointer should be generic") + { + REQUIRE(is_java_generic_type(param_type.type())); + const auto &generic_variables = + to_java_generic_type(param_type.type()).generic_type_variables(); + REQUIRE(generic_variables.size() == 3); + { + const java_generic_parametert &generic_param = generic_variables[0]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + { + const java_generic_parametert &generic_param = generic_variables[1]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + { + const java_generic_parametert &generic_param = generic_variables[2]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + } + } + THEN("Ret Method 1 should return a GenericClass$InnerClass") + { + const std::string func_name = ".ret_method1"; + const std::string func_descriptor = ":()LGenericClass$InnerClass;"; + const std::string process_func_name = + class_prefix + func_name + func_descriptor; + + REQUIRE(new_symbol_table.has_symbol(process_func_name)); + const symbolt &function_symbol = + new_symbol_table.lookup_ref(process_func_name); + + const code_typet &function_call = + require_type::require_code(function_symbol.type); + + require_type::require_pointer( + function_call.return_type(), + symbol_typet("java::GenericClass$InnerClass")); + + THEN("The pointer should be generic") + { + REQUIRE(is_java_generic_type(function_call.return_type())); + const auto &generic_variables = + to_java_generic_type(function_call.return_type()) + .generic_type_variables(); + REQUIRE(generic_variables.size() == 1); + const java_generic_parametert &generic_param = generic_variables[0]; + REQUIRE( + generic_param.type_variable() == symbol_typet("java::GenericClass::T")); + } + } + THEN("Ret method 2 should return a GenericClass$GenericInnerClass") + { + const std::string func_name = ".ret_method2"; + const std::string func_descriptor = ":()LGenericClass$GenericInnerClass;"; + const std::string process_func_name = + class_prefix + func_name + func_descriptor; + + REQUIRE(new_symbol_table.has_symbol(process_func_name)); + const symbolt &function_symbol = + new_symbol_table.lookup_ref(process_func_name); + + const code_typet &function_call = + require_type::require_code(function_symbol.type); + + require_type::require_pointer( + function_call.return_type(), + symbol_typet("java::GenericClass$GenericInnerClass")); + + THEN("The pointer should be generic") + { + REQUIRE(is_java_generic_type(function_call.return_type())); + const auto &generic_variables = + to_java_generic_type(function_call.return_type()) + .generic_type_variables(); + REQUIRE(generic_variables.size() == 2); + { + const java_generic_parametert &generic_param = generic_variables[0]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + { + const java_generic_parametert &generic_param = generic_variables[1]; + REQUIRE(is_java_generic_inst_parameter(generic_param)); + REQUIRE( + generic_param == + java_generic_inst_parametert(symbol_typet("java::Foo"))); + } + } + } + THEN("Ret method 3 should return a GenericClass$GenericInnerClass") + { + const std::string func_name = ".ret_method3"; + const std::string func_descriptor = ":()LGenericClass$GenericInnerClass;"; + const std::string process_func_name = + class_prefix + func_name + func_descriptor; + + REQUIRE(new_symbol_table.has_symbol(process_func_name)); + const symbolt &function_symbol = + new_symbol_table.lookup_ref(process_func_name); + + const code_typet &function_call = + require_type::require_code(function_symbol.type); + + require_type::require_pointer( + function_call.return_type(), + symbol_typet("java::GenericClass$GenericInnerClass")); + + THEN("The pointer should be generic") + { + REQUIRE(is_java_generic_type(function_call.return_type())); + const auto &generic_variables = + to_java_generic_type(function_call.return_type()) + .generic_type_variables(); + REQUIRE(generic_variables.size() == 2); + { + const java_generic_parametert &generic_param = generic_variables[0]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + { + const java_generic_parametert &generic_param = generic_variables[1]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + } + } + THEN( + "Ret method 4 should return a " + "GenericClass$GenericInnerClass$DoublyNestedInnerClass") + { + const std::string func_name = ".ret_method4"; + const std::string func_descriptor = + ":()LGenericClass$GenericInnerClass$DoublyNestedInnerClass;"; + const std::string process_func_name = + class_prefix + func_name + func_descriptor; + + REQUIRE(new_symbol_table.has_symbol(process_func_name)); + const symbolt &function_symbol = + new_symbol_table.lookup_ref(process_func_name); + + const code_typet &function_call = + require_type::require_code(function_symbol.type); + + require_type::require_pointer( + function_call.return_type(), + symbol_typet( + "java::GenericClass$GenericInnerClass$DoublyNestedInnerClass")); + + THEN("The pointer should be generic") + { + REQUIRE(is_java_generic_type(function_call.return_type())); + const auto &generic_variables = + to_java_generic_type(function_call.return_type()) + .generic_type_variables(); + REQUIRE(generic_variables.size() == 2); + { + const java_generic_parametert &generic_param = generic_variables[0]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + { + const java_generic_parametert &generic_param = generic_variables[1]; + REQUIRE(is_java_generic_inst_parameter(generic_param)); + REQUIRE( + generic_param == + java_generic_inst_parametert(symbol_typet("java::Foo"))); + } + } + } + THEN( + "Ret method 5 should return a " + "GenericClass$GenericInnerClass$DoublyNestedInnerClass") + { + const std::string func_name = ".ret_method5"; + const std::string func_descriptor = + ":()LGenericClass$GenericInnerClass$DoublyNestedInnerClass;"; + const std::string process_func_name = + class_prefix + func_name + func_descriptor; + + REQUIRE(new_symbol_table.has_symbol(process_func_name)); + const symbolt &function_symbol = + new_symbol_table.lookup_ref(process_func_name); + + const code_typet &function_call = + require_type::require_code(function_symbol.type); + + require_type::require_pointer( + function_call.return_type(), + symbol_typet( + "java::GenericClass$GenericInnerClass$DoublyNestedInnerClass")); + + THEN("The pointer should be generic") + { + REQUIRE(is_java_generic_type(function_call.return_type())); + const auto &generic_variables = + to_java_generic_type(function_call.return_type()) + .generic_type_variables(); + REQUIRE(generic_variables.size() == 2); + { + const java_generic_parametert &generic_param = generic_variables[0]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + { + const java_generic_parametert &generic_param = generic_variables[1]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + } + } + THEN( + "Ret method 6 should return a " + "GenericClass$GenericInnerClass$DoublyNestedInnerGenericClass") + { + const std::string func_name = ".ret_method6"; + const std::string func_descriptor = + ":()LGenericClass$GenericInnerClass$DoublyNestedInnerGenericClass;"; + const std::string process_func_name = + class_prefix + func_name + func_descriptor; + + REQUIRE(new_symbol_table.has_symbol(process_func_name)); + const symbolt &function_symbol = + new_symbol_table.lookup_ref(process_func_name); + + const code_typet &function_call = + require_type::require_code(function_symbol.type); + + require_type::require_pointer( + function_call.return_type(), + symbol_typet( + "java::GenericClass$GenericInnerClass$" + "DoublyNestedInnerGenericClass")); + + THEN("The pointer should be generic") + { + REQUIRE(is_java_generic_type(function_call.return_type())); + const auto &generic_variables = + to_java_generic_type(function_call.return_type()) + .generic_type_variables(); + REQUIRE(generic_variables.size() == 3); + { + const java_generic_parametert &generic_param = generic_variables[0]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + { + const java_generic_parametert &generic_param = generic_variables[1]; + REQUIRE(is_java_generic_inst_parameter(generic_param)); + REQUIRE( + generic_param == + java_generic_inst_parametert(symbol_typet("java::Foo"))); + } + { + const java_generic_parametert &generic_param = generic_variables[2]; + REQUIRE(is_java_generic_inst_parameter(generic_param)); + REQUIRE( + generic_param == + java_generic_inst_parametert(symbol_typet("java::Foo"))); + } + } + } + THEN( + "Ret method 7 should return a " + "GenericClass$GenericInnerClass$DoublyNestedInnerGenericClass") + { + const std::string func_name = ".ret_method7"; + const std::string func_descriptor = + ":()LGenericClass$GenericInnerClass$DoublyNestedInnerGenericClass;"; + const std::string process_func_name = + class_prefix + func_name + func_descriptor; + + REQUIRE(new_symbol_table.has_symbol(process_func_name)); + const symbolt &function_symbol = + new_symbol_table.lookup_ref(process_func_name); + + const code_typet &function_call = + require_type::require_code(function_symbol.type); + + require_type::require_pointer( + function_call.return_type(), + symbol_typet( + "java::GenericClass$GenericInnerClass$" + "DoublyNestedInnerGenericClass")); + + THEN("The pointer should be generic") + { + REQUIRE(is_java_generic_type(function_call.return_type())); + const auto &generic_variables = + to_java_generic_type(function_call.return_type()) + .generic_type_variables(); + REQUIRE(generic_variables.size() == 3); + { + const java_generic_parametert &generic_param = generic_variables[0]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + { + const java_generic_parametert &generic_param = generic_variables[1]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + { + const java_generic_parametert &generic_param = generic_variables[2]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + } + } +} diff --git a/unit/java_bytecode/java_utils_test.cpp b/unit/java_bytecode/java_utils_test.cpp index 068a3984aca..b568b8c6d01 100644 --- a/unit/java_bytecode/java_utils_test.cpp +++ b/unit/java_bytecode/java_utils_test.cpp @@ -13,6 +13,7 @@ #include +#include #include SCENARIO("Test that the generic signature delimiter lookup works reliably", @@ -62,4 +63,281 @@ SCENARIO("Test that the generic signature delimiter lookup works reliably", REQUIRE(find_closing_delimiter(generic_sigs[6], 9, '<', '>')==17); } } + GIVEN("Some bracketed functions") + { + std::vector bracket_sigs{ + // Valid inputs + "(Entry)", + "Something(Else)", + "(Nested(Bracket))", + // Invalid inputs + "(", + "(Integer>", + }; + WHEN("We check if the closing tag is recognised correctly") + { + // TEST VALID CASES + + // (Entry) + REQUIRE(find_closing_delimiter(bracket_sigs[0], 0, '(', ')') == 6); + // Something(Else) + REQUIRE(find_closing_delimiter(bracket_sigs[1], 9, '(', ')') == 14); + // (Nested(Bracket)) + REQUIRE(find_closing_delimiter(bracket_sigs[2], 0, '(', ')') == 16); + REQUIRE(find_closing_delimiter(bracket_sigs[2], 7, '(', ')') == 15); + + // TEST INVALID CASES + + // ( + REQUIRE( + find_closing_delimiter(bracket_sigs[3], 0, '(', ')') == + std::string::npos); + // (Integer> + REQUIRE( + find_closing_delimiter(bracket_sigs[4], 0, '(', ')') == + std::string::npos); + } + } +} + +SCENARIO("gather_full_class_name") +{ + GIVEN("Descriptor: class") + { + std::string descriptor = "LClassName;"; + THEN("Should get ClassName back") + { + const std::string &class_name = gather_full_class_name(descriptor); + REQUIRE(class_name == "ClassName"); + } + } + GIVEN("Descriptor: A packaged class") + { + std::string descriptor = "Ljava/lang/Object;"; + THEN("Should get java.lang.Object back") + { + const std::string &class_name = gather_full_class_name(descriptor); + REQUIRE(class_name == "java.lang.Object"); + } + } + GIVEN("Descriptor: A inner class") + { + std::string descriptor = "LOuter$Inner;"; + THEN("Should get Outer$Inner") + { + const std::string &class_name = gather_full_class_name(descriptor); + REQUIRE(class_name == "Outer$Inner"); + } + } + GIVEN("Descriptor: a doubly nested inner class") + { + std::string descriptor = "LOuter$Inner$Inner2;"; + THEN("Should get Outer$Inner$Inner2") + { + const std::string &class_name = gather_full_class_name(descriptor); + REQUIRE(class_name == "Outer$Inner$Inner2"); + } + } + + GIVEN("Signature: An generic class") + { + std::string signature = "LClassName;"; + THEN("Should get ClassName back") + { + const std::string &class_name = gather_full_class_name(signature); + REQUIRE(class_name == "ClassName"); + } + } + GIVEN("Signature: An inner class in a generic class") + { + std::string signature = "LClassName.Inner;"; + THEN("Should get ClassName$Inner back") + { + const std::string &class_name = gather_full_class_name(signature); + REQUIRE(class_name == "ClassName$Inner"); + } + } + GIVEN("Signature: An generic inner class in a generic class") + { + std::string signature = "LClassName.Inner;"; + THEN("Should get ClassName$Inner back") + { + const std::string &class_name = gather_full_class_name(signature); + REQUIRE(class_name == "ClassName$Inner"); + } + } + GIVEN("Signature: A generic inner class in a non generic class") + { + std::string signature = "LClassName.Inner;"; + THEN("Should get ClassName$Inner back") + { + const std::string &class_name = gather_full_class_name(signature); + REQUIRE(class_name == "ClassName$Inner"); + } + } + GIVEN( + "Signature: A generic inner class in a non generic class in a non " + "generic " + "class") + { + std::string signature = "LClassName.Inner.Inner2;"; + THEN("Should get ClassName$Inner$Inner2 back") + { + const std::string &class_name = gather_full_class_name(signature); + REQUIRE(class_name == "ClassName$Inner$Inner2"); + } + } + GIVEN( + "Signature: A generic inner class in a generic class in a non generic " + "class") + { + std::string signature = "LClassName.Inner.Inner2;"; + THEN("Should get ClassName$Inner$Inner2 back") + { + const std::string &class_name = gather_full_class_name(signature); + REQUIRE(class_name == "ClassName$Inner$Inner2"); + } + } + GIVEN( + "Signature: A generic inner class in a generic class in a generic " + "class") + { + std::string signature = "LClassName.Inner.Inner2;"; + THEN("Should get ClassName$Inner$Inner2 back") + { + const std::string &class_name = gather_full_class_name(signature); + REQUIRE(class_name == "ClassName$Inner$Inner2"); + } + } + GIVEN( + "Signature: A non-generic inner class in a generic class in a non " + "generic " + "class") + { + std::string signature = "LClassName.Inner.Inner2;"; + THEN("Should get ClassName$Inner$Inner2 back") + { + const std::string &class_name = gather_full_class_name(signature); + REQUIRE(class_name == "ClassName$Inner$Inner2"); + } + } + GIVEN( + "Signature: A non-generic inner class in a generic class in a generic " + "class") + { + std::string signature = "LClassName.Inner.Inner2;"; + THEN("Should get ClassName$Inner$Inner2 back") + { + const std::string &class_name = gather_full_class_name(signature); + REQUIRE(class_name == "ClassName$Inner$Inner2"); + } + } + GIVEN( + "Signature: A non-generic inner class in a non-generic class in a " + "generic " + "class") + { + std::string signature = "LClassName.Inner.Inner2;"; + THEN("Should get ClassName$Inner$Inner2 back") + { + const std::string &class_name = gather_full_class_name(signature); + REQUIRE(class_name == "ClassName$Inner$Inner2"); + } + } + GIVEN( + "Signature: A generic inner class in a non-generic class in a generic " + "class") + { + std::string signature = "LClassName.Inner.Inner2;"; + THEN("Should get ClassName$Inner$Inner2 back") + { + const std::string &class_name = gather_full_class_name(signature); + REQUIRE(class_name == "ClassName$Inner$Inner2"); + } + } +} + +SCENARIO("find_closing_semi_colon_for_reference_type", "[core][java_util_test]") +{ + GIVEN("A simple reference type") + { + std::string descriptor = "LA;"; + // | + // 012 + REQUIRE(find_closing_semi_colon_for_reference_type(descriptor, 0) == 2); + } + GIVEN("A generic reference type") + { + std::string descriptor = "LA;"; + // | + // 01234567 + REQUIRE(find_closing_semi_colon_for_reference_type(descriptor, 0) == 7); + } + GIVEN("A generic reference type with multiple generic params") + { + std::string descriptor = "LA;"; + // | + // 01234567890 + REQUIRE(find_closing_semi_colon_for_reference_type(descriptor, 0) == 10); + } + GIVEN("A descriptor with multiple reference type") + { + std::string descriptor = "LA;LB;"; + // | + // 012 + REQUIRE(find_closing_semi_colon_for_reference_type(descriptor, 0) == 2); + } + GIVEN("A descriptor with multiple reference types parsing the second") + { + std::string descriptor = "LA;LB;"; + // | | + // 012345 + REQUIRE(find_closing_semi_colon_for_reference_type(descriptor, 3) == 5); + } + GIVEN("A descriptor inner class") + { + std::string descriptor = "LA$B;"; + // | + // 01234 + REQUIRE(find_closing_semi_colon_for_reference_type(descriptor, 0) == 4); + } + GIVEN("A signature inner class") + { + std::string descriptor = "LA.B;"; + // | + // 01234 + REQUIRE(find_closing_semi_colon_for_reference_type(descriptor, 0) == 4); + } + GIVEN("A inner class of a generic class") + { + std::string descriptor = "LA.B;"; + // | + // 0123456789 + REQUIRE(find_closing_semi_colon_for_reference_type(descriptor, 0) == 9); + } + GIVEN("A inner class of a instantiated generic class") + { + std::string descriptor = "LA.B;"; + // | + // 012345678901 + REQUIRE(find_closing_semi_colon_for_reference_type(descriptor, 0) == 11); + } + GIVEN( + "A signature with multiple references and an inner class of a generic " + "class") + { + std::string descriptor = "LA.B;LA.B;"; + // | + // 0123456789 + REQUIRE(find_closing_semi_colon_for_reference_type(descriptor, 0) == 9); + } + GIVEN( + "A signature with multiple references and an inner class of a generic " + "class") + { + std::string descriptor = "LA.B;LA.B;"; + // | | + // 01234567890123456789 + REQUIRE(find_closing_semi_colon_for_reference_type(descriptor, 10) == 19); + } } diff --git a/unit/testing-utils/Makefile b/unit/testing-utils/Makefile index db9dfb33ec4..b0978465a3f 100644 --- a/unit/testing-utils/Makefile +++ b/unit/testing-utils/Makefile @@ -2,6 +2,7 @@ SRC = \ c_to_expr.cpp \ load_java_class.cpp \ require_expr.cpp \ + require_type.cpp \ # Empty last line (please keep above list sorted!) INCLUDES = -I .. -I . -I ../../src diff --git a/unit/testing-utils/require_type.cpp b/unit/testing-utils/require_type.cpp new file mode 100644 index 00000000000..882f39005c4 --- /dev/null +++ b/unit/testing-utils/require_type.cpp @@ -0,0 +1,80 @@ +/*******************************************************************\ + + Module: Unit test utilities + + Author: DiffBlue Limited. All rights reserved. + +\*******************************************************************/ + +#include "require_type.h" + +#include +#include + +/// Checks a type is a pointer type optionally with a specific subtype +/// \param type: The type to check +/// \param subtype: An optional subtype. If provided, checks the subtype of the +/// pointer is this. +/// \return A cast to pointer_typet version of type +pointer_typet require_type::require_pointer( + const typet &type, + const optionalt &subtype) +{ + REQUIRE(type.id() == ID_pointer); + const pointer_typet &pointer = to_pointer_type(type); + + if(subtype) + { + // TODO: use base_type_eq + REQUIRE(pointer.subtype() == subtype.value()); + } + return pointer; +} + +/// Checks a struct like type has a component with a specific name +/// \param struct_type: The structure that should have the component +/// \param component_name: The name of the component +/// \return The component with the specified name +struct_union_typet::componentt require_type::require_component( + const struct_typet &struct_type, + const irep_idt &component_name) +{ + const auto &componet = std::find_if( + struct_type.components().begin(), + struct_type.components().end(), + [&component_name](const struct_union_typet::componentt &component) { + return component.get_name() == component_name; + }); + + REQUIRE(componet != struct_type.components().end()); + return *componet; +} + +/// Checks a type is a code_type (i.e. a function) +/// \param type: The type to check +/// \return The cast version of the type code_type +code_typet require_type::require_code(const typet &type) +{ + REQUIRE(type.id() == ID_code); + return to_code_type(type); +} + +/// Verify that a function has a parameter of a specific name. +/// \param function_type: The type of the function +/// \param param_name: The name of the parameter +/// \return: A reference to the parameter structure corresponding to this +/// parameter name. +code_typet::parametert require_type::require_parameter( + const code_typet &function_type, + const irep_idt ¶m_name) +{ + const auto param = std::find_if( + function_type.parameters().begin(), + function_type.parameters().end(), + [¶m_name](const code_typet::parametert param) { + return param.get_base_name() == param_name; + }); + + REQUIRE(param != function_type.parameters().end()); + return *param; +} diff --git a/unit/testing-utils/require_type.h b/unit/testing-utils/require_type.h new file mode 100644 index 00000000000..488abb5da44 --- /dev/null +++ b/unit/testing-utils/require_type.h @@ -0,0 +1,36 @@ +/*******************************************************************\ + + Module: Unit test utilities + + Author: DiffBlue Limited. All rights reserved. + +\*******************************************************************/ + +/// \file +/// Helper functions for requiring specific types +/// If the type is of the wrong type, throw a CATCH exception +/// Also checks associated properties and returns a casted version of the +/// expression. + +#ifndef CPROVER_TESTING_UTILS_REQUIRE_TYPE_H +#define CPROVER_TESTING_UTILS_REQUIRE_TYPE_H + +#include +#include + +// NOLINTNEXTLINE(readability/namespace) +namespace require_type +{ +pointer_typet +require_pointer(const typet &type, const optionalt &subtype); + +struct_typet::componentt require_component( + const struct_typet &struct_type, + const irep_idt &component_name); + +code_typet require_code(const typet &type); +code_typet::parametert +require_parameter(const code_typet &function_type, const irep_idt ¶m_name); +} + +#endif