From 5d6ebfd745d0f14c4264c90551a3447870e0f741 Mon Sep 17 00:00:00 2001 From: Thomas Kiley Date: Tue, 17 Oct 2017 16:41:53 +0100 Subject: [PATCH 1/2] Revert "test isn't ready yet" --- unit/Makefile | 1 + .../parse_generic_class.cpp | 329 ++++++++++++++++++ 2 files changed, 330 insertions(+) create mode 100644 unit/java_bytecode/java_bytecode_parse_generics/parse_generic_class.cpp diff --git a/unit/Makefile b/unit/Makefile index c89872fc1d3..379918fa56f 100644 --- a/unit/Makefile +++ b/unit/Makefile @@ -17,6 +17,7 @@ SRC += unit_tests.cpp \ analyses/does_remove_const/does_type_preserve_const_correctness.cpp \ analyses/does_remove_const/is_type_at_least_as_const_as.cpp \ java_bytecode/java_bytecode_convert_class/convert_abstract_class.cpp \ + java_bytecode/java_bytecode_parse_generics/parse_generic_class.cpp \ miniBDD_new.cpp \ java_bytecode/java_string_library_preprocess/convert_exprt_to_string_exprt.cpp \ java_bytecode/java_utils_test.cpp \ diff --git a/unit/java_bytecode/java_bytecode_parse_generics/parse_generic_class.cpp b/unit/java_bytecode/java_bytecode_parse_generics/parse_generic_class.cpp new file mode 100644 index 00000000000..d14553d2599 --- /dev/null +++ b/unit/java_bytecode/java_bytecode_parse_generics/parse_generic_class.cpp @@ -0,0 +1,329 @@ +/*******************************************************************\ + + Module: Unit tests for parsing generic classes + + Author: DiffBlue Limited. All rights reserved. + +\*******************************************************************/ + +#include + +#include +#include + +#include +#include +#include +#include +#include + +SCENARIO( + "java_bytecode_parse_generics", + "[core][java_bytecode][java_bytecode_parse_generics]") +{ + const symbol_tablet &new_symbol_table= + load_java_class("generics", "./java_bytecode/java_bytecode_parse_generics"); + + GIVEN("Some class files with Generics") + { + WHEN("Parsing a class with type variable") + { + REQUIRE(new_symbol_table.has_symbol("java::generics$element")); + THEN("The symbol type should be generic") + { + const symbolt &class_symbol= + new_symbol_table.lookup("java::generics$element").value().get(); + const typet &symbol_type=class_symbol.type; + + REQUIRE(symbol_type.id()==ID_struct); + class_typet class_type=to_class_type(symbol_type); + REQUIRE(class_type.is_class()); + java_class_typet java_class_type=to_java_class_type(class_type); + REQUIRE(is_java_generics_class_type(java_class_type)); + java_generics_class_typet java_generics_class_type= + to_java_generics_class_type(java_class_type); + + const struct_union_typet::componentt &elem= + java_generics_class_type.get_component("elem"); + const typet &elem_type=java_class_type.component_type("elem"); + + REQUIRE(is_java_generic_parameter(elem_type)); + + REQUIRE(java_generics_class_type.generic_types().size()==1); + THEN("Type variable is named 'E'") + { + typet &type_var=java_generics_class_type.generic_types().front(); + REQUIRE(is_java_generic_parameter(type_var)); + java_generic_parametert generic_type_var= + to_java_generic_parameter(type_var); + REQUIRE( + generic_type_var.type_variable().get_identifier()== + "java::generics$element::E"); + typet &sub_type=generic_type_var.subtype(); + REQUIRE(sub_type.id()==ID_symbol); + symbol_typet &bound_type=to_symbol_type(sub_type); + REQUIRE(bound_type.get_identifier()=="java::java.lang.Object"); + } + } + } + } + + GIVEN("Some class files with generic type variable") + { + WHEN("Parsing a class with bounded type variable") + { + REQUIRE(new_symbol_table.has_symbol("java::generics$bound_element")); + THEN("The symbol type should be generic") + { + const symbolt &class_symbol= + new_symbol_table.lookup("java::generics$bound_element").value().get(); + const typet &symbol_type=class_symbol.type; + + REQUIRE(symbol_type.id()==ID_struct); + class_typet class_type=to_class_type(symbol_type); + REQUIRE(class_type.is_class()); + java_class_typet java_class_type=to_java_class_type(class_type); + REQUIRE(is_java_generics_class_type(java_class_type)); + java_generics_class_typet java_generics_class_type= + to_java_generics_class_type(java_class_type); + REQUIRE(java_generics_class_type.generic_types().size()==1); + typet &type_var=java_generics_class_type.generic_types().front(); + REQUIRE(is_java_generic_parameter(type_var)); + java_generic_parametert generic_type_var= + to_java_generic_parameter(type_var); + + REQUIRE( + generic_type_var.type_variable().get_identifier()== + "java::generics$bound_element::NUM"); + REQUIRE( + java_generics_class_type_var(0, java_generics_class_type)== + "java::generics$bound_element::NUM"); + THEN("Bound must be Number") + { + typet &sub_type=generic_type_var.subtype(); + REQUIRE(sub_type.id()==ID_symbol); + symbol_typet &bound_type=to_symbol_type(sub_type); + REQUIRE(bound_type.get_identifier()=="java::java.lang.Number"); + REQUIRE( + to_symbol_type( + java_generics_class_type_bound(0, java_generics_class_type)) + .get_identifier()=="java::java.lang.Number"); + } + + const struct_union_typet::componentt &elem= + java_generics_class_type.get_component("elem"); + const typet &elem_type=java_class_type.component_type("elem"); + + REQUIRE(is_java_generic_parameter(elem_type)); + } + } + } + + GIVEN("Some class files with generic type variable") + { + WHEN("Parsing a class with bounded type variable") + { + REQUIRE(new_symbol_table.has_symbol("java::generics")); + + THEN("The generic fields should be annotated with concrete types") + { + const symbolt &class_symbol=new_symbol_table.lookup("java::generics") + .value().get(); + const typet &symbol_type=class_symbol.type; + + REQUIRE(symbol_type.id()==ID_struct); + class_typet class_type=to_class_type(symbol_type); + REQUIRE(class_type.is_class()); + java_class_typet java_class_type=to_java_class_type(class_type); + REQUIRE(!is_java_generics_class_type(java_class_type)); + + const struct_union_typet::componentt &belem= + java_class_type.get_component("belem"); + const typet &belem_type=java_class_type.component_type("belem"); + + REQUIRE(belem_type!=nil_typet()); + REQUIRE(is_java_generic_type(belem_type)); + THEN("Field has instantiated type variable") + { + const java_generic_typet &container= + to_java_generic_type(belem_type); + + const std::vector &generic_types= + container.generic_type_variables(); + REQUIRE(generic_types.size()==1); + + const typet& inst_type=java_generic_get_inst_type(0, container); + + REQUIRE(inst_type.id()==ID_pointer); + const typet &inst_type_symbol=inst_type.subtype(); + REQUIRE(inst_type_symbol.id()==ID_symbol); + REQUIRE( + to_symbol_type(inst_type_symbol).get_identifier()== + "java::java.lang.Integer"); + } + } + } + } + + GIVEN("Some class files with Generics") + { + WHEN("Methods with generic signatures") + { + REQUIRE( + new_symbol_table + .has_symbol("java::generics$bound_element.f:()Ljava/lang/Number;")); + +// TODO: methods should have generic return type (the tests needs to be +// extended), reintroduce when the issue of signature/descriptor for methods is +// resolved +// THEN("The method should have generic return type") +// { +// const symbolt &method_symbol= +// new_symbol_table +// .lookup("java::generics$bound_element.f:()Ljava/lang/Number;") +// .value().get(); +// const typet &symbol_type=method_symbol.type; +// +// REQUIRE(symbol_type.id()==ID_code); +// +// const code_typet &code=to_code_type(symbol_type); +// } + + REQUIRE( + new_symbol_table + .has_symbol("java::generics$bound_element.g:(Ljava/lang/Number;)V")); + +// TODO: methods are not recognized as generic, reintroduce when +// the issue of signature/descriptor for methods is resolved +// THEN("The method should have a generic parameter.") +// { +// const symbolt &method_symbol= +// new_symbol_table +// .lookup("java::generics$bound_element.g:(Ljava/lang/Number;)V"); +// const typet &symbol_type=method_symbol.type; +// +// REQUIRE(symbol_type.id()==ID_code); +// +// const code_typet &code=to_code_type(symbol_type); +// +// bool found=false; +// for(const auto &p : code.parameters()) +// { +// if(p.get_identifier()== +// "java::generics$bound_element.g:(Ljava/lang/Number;)V::e") +// { +// found=true; +// const typet &t=p.type(); +// REQUIRE(is_java_generic_parameter(p.type())); +// const java_generic_parametert &gen_type= +// to_java_generic_parameter(p.type()); +// const symbol_typet &type_var=gen_type.type_variable(); +// REQUIRE(type_var.get_identifier()== +// "java::generics$bound_element::NUM"); +// break; +// } +// } +// REQUIRE(found); +// } + } + } + GIVEN("A class with multiple bounds") + { + THEN("The bounds should be encoded") + { + REQUIRE( + new_symbol_table.has_symbol("java::generics$double_bound_element")); + THEN("The symbol should have a generic parameter") + { + const symbolt &class_symbol= + new_symbol_table.lookup("java::generics$double_bound_element") + .value().get(); + const typet &symbol_type=class_symbol.type; + + REQUIRE(symbol_type.id()==ID_struct); + class_typet class_type=to_class_type(symbol_type); + REQUIRE(class_type.is_class()); + java_class_typet java_class_type=to_java_class_type(class_type); + REQUIRE_FALSE(is_java_generics_class_type(java_class_type)); + + // TODO (tkiley): Extend this unit test when bounds are correctly + // parsed. +#if 0 + java_generics_class_typet java_generics_class_type= + to_java_generics_class_type(java_class_type); + REQUIRE(java_generics_class_type.generic_types().size()==1); + typet &type_var=java_generics_class_type.generic_types().front(); + REQUIRE(is_java_generic_parameter(type_var)); + java_generic_parametert generic_type_var= + to_java_generic_parameter(type_var); + + REQUIRE( + generic_type_var.type_variable().get_identifier()== + "java::generics$double_bound_element::T"); + REQUIRE( + java_generics_class_type_var(0, java_generics_class_type)== + "java::generics$double_bound_element::T"); + THEN("Bound must be Number and dummyInterface") + { + + } +#endif + } + } + } + GIVEN("A class with multiple generic parameters") + { + THEN("Both generic parameters should be encoded") + { + const symbolt &class_symbol= + new_symbol_table.lookup("java::generics$two_elements").value().get(); + const typet &symbol_type=class_symbol.type; + + REQUIRE(symbol_type.id()==ID_struct); + class_typet class_type=to_class_type(symbol_type); + REQUIRE(class_type.is_class()); + java_class_typet java_class_type=to_java_class_type(class_type); + REQUIRE(is_java_generics_class_type(java_class_type)); + + java_generics_class_typet java_generics_class_type= + to_java_generics_class_type(java_class_type); + REQUIRE(java_generics_class_type.generic_types().size()==2); + + auto generic_param_iterator= + java_generics_class_type.generic_types().cbegin(); + + // The first parameter should be called K + { + const typet &first_param=*generic_param_iterator; + REQUIRE(is_java_generic_parameter(first_param)); + java_generic_parametert generic_type_var= + to_java_generic_parameter(first_param); + + REQUIRE( + generic_type_var.type_variable().get_identifier()== + "java::generics$two_elements::K"); + REQUIRE( + java_generics_class_type_var(0, java_generics_class_type)== + "java::generics$two_elements::K"); + } + + ++generic_param_iterator; + + + // The second parameter should be called V + { + const typet &second_param=*generic_param_iterator; + REQUIRE(is_java_generic_parameter(second_param)); + java_generic_parametert generic_type_var= + to_java_generic_parameter(second_param); + + REQUIRE( + generic_type_var.type_variable().get_identifier()== + "java::generics$two_elements::V"); + REQUIRE( + java_generics_class_type_var(1, java_generics_class_type)== + "java::generics$two_elements::V"); + } + } + } +} From 69d67abc57533e72443d4baf53d039283185d18e Mon Sep 17 00:00:00 2001 From: Thomas Kiley Date: Tue, 17 Oct 2017 16:57:18 +0100 Subject: [PATCH 2/2] Modified unit test to compile Updated the includes to point to new unit test utilties location Updated usages of symbol_table to respect new interface --- .../parse_generic_class.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/unit/java_bytecode/java_bytecode_parse_generics/parse_generic_class.cpp b/unit/java_bytecode/java_bytecode_parse_generics/parse_generic_class.cpp index d14553d2599..f49c9022887 100644 --- a/unit/java_bytecode/java_bytecode_parse_generics/parse_generic_class.cpp +++ b/unit/java_bytecode/java_bytecode_parse_generics/parse_generic_class.cpp @@ -6,7 +6,8 @@ \*******************************************************************/ -#include +#include +#include #include #include @@ -15,7 +16,6 @@ #include #include #include -#include SCENARIO( "java_bytecode_parse_generics", @@ -32,7 +32,7 @@ SCENARIO( THEN("The symbol type should be generic") { const symbolt &class_symbol= - new_symbol_table.lookup("java::generics$element").value().get(); + new_symbol_table.lookup_ref("java::generics$element"); const typet &symbol_type=class_symbol.type; REQUIRE(symbol_type.id()==ID_struct); @@ -76,7 +76,7 @@ SCENARIO( THEN("The symbol type should be generic") { const symbolt &class_symbol= - new_symbol_table.lookup("java::generics$bound_element").value().get(); + new_symbol_table.lookup_ref("java::generics$bound_element"); const typet &symbol_type=class_symbol.type; REQUIRE(symbol_type.id()==ID_struct); @@ -127,8 +127,8 @@ SCENARIO( THEN("The generic fields should be annotated with concrete types") { - const symbolt &class_symbol=new_symbol_table.lookup("java::generics") - .value().get(); + const symbolt &class_symbol= + new_symbol_table.lookup_ref("java::generics"); const typet &symbol_type=class_symbol.type; REQUIRE(symbol_type.id()==ID_struct); @@ -236,8 +236,7 @@ SCENARIO( THEN("The symbol should have a generic parameter") { const symbolt &class_symbol= - new_symbol_table.lookup("java::generics$double_bound_element") - .value().get(); + new_symbol_table.lookup_ref("java::generics$double_bound_element"); const typet &symbol_type=class_symbol.type; REQUIRE(symbol_type.id()==ID_struct); @@ -276,7 +275,7 @@ SCENARIO( THEN("Both generic parameters should be encoded") { const symbolt &class_symbol= - new_symbol_table.lookup("java::generics$two_elements").value().get(); + new_symbol_table.lookup_ref("java::generics$two_elements"); const typet &symbol_type=class_symbol.type; REQUIRE(symbol_type.id()==ID_struct);