diff --git a/src/java_bytecode/java_bytecode_parse_tree.h b/src/java_bytecode/java_bytecode_parse_tree.h index 7be01209f47..ca98235a51a 100644 --- a/src/java_bytecode/java_bytecode_parse_tree.h +++ b/src/java_bytecode/java_bytecode_parse_tree.h @@ -247,4 +247,18 @@ class java_bytecode_parse_treet } }; +class fieldref_exprt : public exprt +{ +public: + fieldref_exprt( + const typet &type, + const irep_idt &component_name, + const irep_idt &class_name) + : exprt(ID_empty_string, type) + { + set(ID_class, class_name); + set(ID_component_name, component_name); + } +}; + #endif // CPROVER_JAVA_BYTECODE_JAVA_BYTECODE_PARSE_TREE_H diff --git a/unit/java_bytecode/java_bytecode_parser/java_bytecode_parse_lambda_method_table.cpp b/unit/java_bytecode/java_bytecode_parser/java_bytecode_parse_lambda_method_table.cpp new file mode 100644 index 00000000000..d1484f098a3 --- /dev/null +++ b/unit/java_bytecode/java_bytecode_parser/java_bytecode_parse_lambda_method_table.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 + +typedef java_bytecode_parse_treet::classt::lambda_method_handlet + lambda_method_handlet; + +SCENARIO( + "lambda_method_handle_map with static lambdas", + "[core][java_bytecode][java_bytecode_parse_lambda_method_handle]") +{ + null_message_handlert message_handler; + GIVEN("A class with a static lambda variables") + { + java_bytecode_parse_treet parse_tree; + java_bytecode_parse( + "./java_bytecode/java_bytecode_parser/lambda_examples/" + "StaticLambdas.class", + parse_tree, + message_handler); + WHEN("Parsing that class") + { + REQUIRE(parse_tree.loading_successful); + const java_bytecode_parse_treet::classt parsed_class = + parse_tree.parsed_class; + REQUIRE(parsed_class.attribute_bootstrapmethods_read); + REQUIRE(parsed_class.lambda_method_handle_map.size() == 12); + + // Simple lambdas + THEN( + "There should be an entry for the lambda that has no parameters or " + "returns and the method it references should have an appropriate " + "descriptor") + { + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, "()V"); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == "()V"); + } + + // Parameter lambdas + THEN( + "There should be an entry for the lambda that takes parameters and the " + "method it references should have an appropriate descriptor") + { + std::string descriptor = "(ILjava/lang/Object;LDummyGeneric;)V"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + THEN( + "There should be an entry for the lambda that takes array parameters " + "and the method it references should have an appropriate descriptor") + { + std::string descriptor = "([I[Ljava/lang/Object;[LDummyGeneric;)V"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + + // Return lambdas + THEN( + "There should be an entry for the lambda that returns a primitive and " + "the method it references should have an appropriate descriptor") + { + std::string descriptor = "()I"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + THEN( + "There should be an entry for the lambda that returns a reference type " + "and the method it references should have an appropriate descriptor") + { + std::string descriptor = "()Ljava/lang/Object;"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + THEN( + "There should be an entry for the lambda that returns a specialised " + "generic type and the method it references should have an appropriate " + "descriptor") + { + std::string descriptor = "()LDummyGeneric;"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + + // Array returning lambdas + THEN( + "There should be an entry for the lambda that returns an array of " + "primitives and the method it references should have an appropriate " + "descriptor") + { + std::string descriptor = "()[I"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + THEN( + "There should be an entry for the lambda that returns an array of " + "reference types and the method it references should have an " + "appropriate descriptor") + { + std::string descriptor = "()[Ljava/lang/Object;"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + THEN( + "There should be an entry for the lambda that returns an array of " + "specialised generic types and the method it references should have an " + "appropriate descriptor") + { + std::string descriptor = "()[LDummyGeneric;"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + + // Capturing lamdbas + THEN( + "There should be an entry for the lambda that returns a primitive and " + "the method it references should have an appropriate descriptor") + { + std::string descriptor = "()I"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor, 1); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + + const typet primitive_type = java_int_type(); + + fieldref_exprt fieldref{ + primitive_type, "staticPrimitive", "java::StaticLambdas"}; + + std::vector + expected_instructions{{"getstatic", {fieldref}}, {"ireturn", {}}}; + + require_parse_tree::require_instructions_match_expectation( + expected_instructions, lambda_method.instructions); + } + THEN( + "There should be an entry for the lambda that returns a reference type " + "and the method it references should have an appropriate descriptor") + { + std::string descriptor = "()Ljava/lang/Object;"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor, 1); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + + const reference_typet dummy_generic_reference_type = + java_reference_type(symbol_typet{"java::java.lang.Object"}); + + fieldref_exprt fieldref{dummy_generic_reference_type, + "staticReference", + "java::StaticLambdas"}; + + std::vector + expected_instructions{{"getstatic", {fieldref}}, {"areturn", {}}}; + + require_parse_tree::require_instructions_match_expectation( + expected_instructions, lambda_method.instructions); + } + THEN( + "There should be an entry for the lambda that returns a specialised " + "generic type and the method it references should have an appropriate " + "descriptor") + { + std::string descriptor = "()LDummyGeneric;"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor, 1); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const java_bytecode_parse_treet::methodt &lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + + const reference_typet dummy_generic_reference_type = + java_reference_type(symbol_typet{"java::DummyGeneric"}); + + fieldref_exprt fieldref{dummy_generic_reference_type, + "staticSpecalisedGeneric", + "java::StaticLambdas"}; + + std::vector + expected_instructions{{"getstatic", {fieldref}}, {"areturn", {}}}; + + require_parse_tree::require_instructions_match_expectation( + expected_instructions, lambda_method.instructions); + } + } + } +} +SCENARIO( + "lambda_method_handle_map with local lambdas", + "[core][java_bytecode][java_bytecode_parse_lambda_method_handle]") +{ + null_message_handlert message_handler; + GIVEN("A method with local lambdas") + { + java_bytecode_parse_treet parse_tree; + java_bytecode_parse( + "./java_bytecode/java_bytecode_parser/lambda_examples/" + "LocalLambdas.class", + parse_tree, + message_handler); + WHEN("Parsing that class") + { + REQUIRE(parse_tree.loading_successful); + const java_bytecode_parse_treet::classt parsed_class = + parse_tree.parsed_class; + REQUIRE(parsed_class.attribute_bootstrapmethods_read); + REQUIRE(parsed_class.lambda_method_handle_map.size() == 12); + + // Simple lambdas + THEN( + "There should be an entry for the lambda that has no parameters or " + "returns and the method it references should have an appropriate " + "descriptor") + { + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, "()V"); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == "()V"); + } + + // Parameter lambdas + THEN( + "There should be an entry for the lambda that takes parameters and the " + "method it references should have an appropriate descriptor") + { + std::string descriptor = "(ILjava/lang/Object;LDummyGeneric;)V"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + THEN( + "There should be an entry for the lambda that takes array parameters " + "and the method it references should have an appropriate descriptor") + { + std::string descriptor = "([I[Ljava/lang/Object;[LDummyGeneric;)V"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + + // Return lambdas + THEN( + "There should be an entry for the lambda that returns a primitive and " + "the method it references should have an appropriate descriptor") + { + std::string descriptor = "()I"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + THEN( + "There should be an entry for the lambda that returns a reference type " + "and the method it references should have an appropriate descriptor") + { + std::string descriptor = "()Ljava/lang/Object;"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + THEN( + "There should be an entry for the lambda that returns a specialised " + "generic type and the method it references should have an appropriate " + "descriptor") + { + std::string descriptor = "()LDummyGeneric;"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + + // Array returning lambdas + THEN( + "There should be an entry for the lambda that returns an array of " + "primitives and the method it references should have an appropriate " + "descriptor") + { + std::string descriptor = "()[I"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + THEN( + "There should be an entry for the lambda that returns an array of " + "reference types and the method it references should have an " + "appropriate descriptor") + { + std::string descriptor = "()[Ljava/lang/Object;"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + THEN( + "There should be an entry for the lambda that returns an array of " + "specialised generic types and the method it references should have an " + "appropriate descriptor") + { + std::string descriptor = "()[LDummyGeneric;"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + + // Capturing lamdbas + THEN( + "There should be an entry for the lambda that returns a primitive " + "local variable and the method it references should have an " + "appropriate descriptor") + { + std::string descriptor = "()I"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor, 1); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + // Note here the descriptor of the implementation is different - the + // implementation requries the input to be passed in + REQUIRE(id2string(lambda_method.descriptor) == "(I)I"); + + std::vector + expected_instructions{{"iload_0", {}}, {"ireturn", {}}}; + + require_parse_tree::require_instructions_match_expectation( + expected_instructions, lambda_method.instructions); + } + THEN( + "There should be an entry for the lambda that returns a reference type " + "local variable and the method it references should have an " + "appropriate descriptor") + { + // Since it is a local variable, the corresponding method takes the + // captured variable as an input + std::string descriptor = "()Ljava/lang/Object;"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor, 1); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE( + id2string(lambda_method.descriptor) == + "(Ljava/lang/Object;)Ljava/lang/Object;"); + + std::vector + expected_instructions{{"aload_0", {}}, {"areturn", {}}}; + + require_parse_tree::require_instructions_match_expectation( + expected_instructions, lambda_method.instructions); + } + THEN( + "There should be an entry for the lambda that returns a specialised " + "generic type local variable and the method it references should have " + "an appropriate descriptor") + { + // Since it is a local variable, the corresponding method takes the + // captured variable as an input + std::string descriptor = "()LDummyGeneric;"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor, 1); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const java_bytecode_parse_treet::methodt &lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE( + id2string(lambda_method.descriptor) == + "(LDummyGeneric;)LDummyGeneric;"); + + // since just returning the parameter, nothing to put on the stack + std::vector + expected_instructions{{"aload_0", {}}, {"areturn", {}}}; + + require_parse_tree::require_instructions_match_expectation( + expected_instructions, lambda_method.instructions); + } + } + } +} +SCENARIO( + "lambda_method_handle_map with member lambdas", + "[core][java_bytecode][java_bytecode_parse_lambda_method_handle]") +{ + null_message_handlert message_handler; + GIVEN("A class that has lambdas as member variables") + { + java_bytecode_parse_treet parse_tree; + java_bytecode_parse( + "./java_bytecode/java_bytecode_parser/lambda_examples/" + "MemberLambdas.class", + parse_tree, + message_handler); + WHEN("Parsing that class") + { + REQUIRE(parse_tree.loading_successful); + const java_bytecode_parse_treet::classt parsed_class = + parse_tree.parsed_class; + REQUIRE(parsed_class.attribute_bootstrapmethods_read); + REQUIRE(parsed_class.lambda_method_handle_map.size() == 12); + + // Simple lambdas + THEN( + "There should be an entry for the lambda that has no parameters or " + "returns and the method it references should have an appropriate " + "descriptor") + { + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, "()V"); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == "()V"); + } + + // Parameter lambdas + THEN( + "There should be an entry for the lambda that takes parameters and the " + "method it references should have an appropriate descriptor") + { + std::string descriptor = "(ILjava/lang/Object;LDummyGeneric;)V"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + THEN( + "There should be an entry for the lambda that takes array parameters " + "and the method it references should have an appropriate descriptor") + { + std::string descriptor = "([I[Ljava/lang/Object;[LDummyGeneric;)V"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + + // Return lambdas + THEN( + "There should be an entry for the lambda that returns a primitive and " + "the method it references should have an appropriate descriptor") + { + std::string descriptor = "()I"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + THEN( + "There should be an entry for the lambda that returns a reference type " + "and the method it references should have an appropriate descriptor") + { + std::string descriptor = "()Ljava/lang/Object;"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + THEN( + "There should be an entry for the lambda that returns a specialised " + "generic type and the method it references should have an appropriate " + "descriptor") + { + std::string descriptor = "()LDummyGeneric;"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + + // Array returning lambdas + THEN( + "There should be an entry for the lambda that returns an array of " + "primitives and the method it references should have an appropriate " + "descriptor") + { + std::string descriptor = "()[I"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + THEN( + "There should be an entry for the lambda that returns an array of " + "reference types and the method it references should have an " + "appropriate descriptor") + { + std::string descriptor = "()[Ljava/lang/Object;"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + THEN( + "There should be an entry for the lambda that returns an array of " + "specialised generic types and the method it references should have an " + "appropriate descriptor") + { + std::string descriptor = "()[LDummyGeneric;"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + + // Capturing lamdbas + THEN( + "There should be an entry for the lambda that returns a primitive " + "local variable and the method it references should have an " + "appropriate descriptor") + { + std::string descriptor = "()I"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor, 1); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + // Note here the descriptor of the implementation is different - the + // implementation requries the input to be passed in + REQUIRE(id2string(lambda_method.descriptor) == "()I"); + REQUIRE_FALSE(lambda_method.is_static); + + const fieldref_exprt primitive_fieldref{ + java_int_type(), "memberPrimitive", "java::MemberLambdas"}; + + std::vector + expected_instructions{{"aload_0", {}}, // load this of stack + {"getfield", {primitive_fieldref}}, + {"ireturn", {}}}; + + require_parse_tree::require_instructions_match_expectation( + expected_instructions, lambda_method.instructions); + } + THEN( + "There should be an entry for the lambda that returns a reference type " + "local variable and the method it references should have an " + "appropriate descriptor") + { + // Since it is a local variable, the corresponding method takes the + // captured variable as an input + std::string descriptor = "()Ljava/lang/Object;"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor, 1); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == "()Ljava/lang/Object;"); + REQUIRE_FALSE(lambda_method.is_static); + + const reference_typet dummy_generic_reference_type = + java_reference_type(symbol_typet{"java::java.lang.Object"}); + + const fieldref_exprt reference_fieldref{dummy_generic_reference_type, + "memberReference", + "java::MemberLambdas"}; + + std::vector + expected_instructions{{"aload_0", {}}, // load this of stack + {"getfield", {reference_fieldref}}, + {"areturn", {}}}; + + require_parse_tree::require_instructions_match_expectation( + expected_instructions, lambda_method.instructions); + } + THEN( + "There should be an entry for the lambda that returns a specialised " + "generic type local variable and the method it references should have " + "an appropriate descriptor") + { + // Since it is a local variable, the corresponding method takes the + // captured variable as an input + std::string descriptor = "()LDummyGeneric;"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor, 1); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const java_bytecode_parse_treet::methodt &lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == "()LDummyGeneric;"); + REQUIRE_FALSE(lambda_method.is_static); + + const reference_typet dummy_generic_reference_type = + java_reference_type(symbol_typet{"java::DummyGeneric"}); + + const fieldref_exprt generic_reference_fieldref{ + dummy_generic_reference_type, + "memberSpecalisedGeneric", + "java::MemberLambdas"}; + + // since just returning the parameter, nothing to put on the stack + std::vector + expected_instructions{{"aload_0", {}}, // load this of stack + {"getfield", {generic_reference_fieldref}}, + {"areturn", {}}}; + + require_parse_tree::require_instructions_match_expectation( + expected_instructions, lambda_method.instructions); + } + } + } +} +SCENARIO( + "lambda_method_handle_map with member lambdas capturing outer class " + "variables", + "[core][java_bytecode][java_bytecode_parse_lambda_method_handle]") +{ + null_message_handlert message_handler; + GIVEN( + "An inner class with member variables as lambdas that capture outer " + "variables") + { + java_bytecode_parse_treet parse_tree; + java_bytecode_parse( + "./java_bytecode/java_bytecode_parser/lambda_examples/" + "OuterMemberLambdas$Inner.class", + parse_tree, + message_handler); + WHEN("Parsing that class") + { + REQUIRE(parse_tree.loading_successful); + const java_bytecode_parse_treet::classt parsed_class = + parse_tree.parsed_class; + REQUIRE(parsed_class.attribute_bootstrapmethods_read); + REQUIRE(parsed_class.lambda_method_handle_map.size() == 3); + + // Field ref for getting the outer class + const reference_typet outer_class_reference_type = + java_reference_type(symbol_typet{"java::OuterMemberLambdas"}); + const fieldref_exprt outer_fieldref{ + outer_class_reference_type, "this$0", "java::OuterMemberLambdas$Inner"}; + + THEN( + "There should be an entry for the lambda that returns a primitive " + "local variable and the method it references should have an " + "appropriate descriptor") + { + std::string descriptor = "()I"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + // Note here the descriptor of the implementation is different - the + // implementation requries the input to be passed in + REQUIRE(id2string(lambda_method.descriptor) == "()I"); + REQUIRE_FALSE(lambda_method.is_static); + + const fieldref_exprt primitive_fieldref{ + java_int_type(), "memberPrimitive", "java::OuterMemberLambdas"}; + + std::vector + expected_instructions{{"aload_0", {}}, // load this of stack + {"getfield", {outer_fieldref}}, + {"getfield", {primitive_fieldref}}, + {"ireturn", {}}}; + + require_parse_tree::require_instructions_match_expectation( + expected_instructions, lambda_method.instructions); + } + THEN( + "There should be an entry for the lambda that returns a reference type " + "local variable and the method it references should have an " + "appropriate descriptor") + { + // Since it is a local variable, the corresponding method takes the + // captured variable as an input + std::string descriptor = "()Ljava/lang/Object;"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == "()Ljava/lang/Object;"); + REQUIRE_FALSE(lambda_method.is_static); + + const reference_typet dummy_generic_reference_type = + java_reference_type(symbol_typet{"java::java.lang.Object"}); + + const fieldref_exprt reference_fieldref{dummy_generic_reference_type, + "memberReference", + "java::OuterMemberLambdas"}; + + std::vector + expected_instructions{{"aload_0", {}}, // load this of stack + {"getfield", {outer_fieldref}}, + {"getfield", {reference_fieldref}}, + {"areturn", {}}}; + + require_parse_tree::require_instructions_match_expectation( + expected_instructions, lambda_method.instructions); + } + THEN( + "There should be an entry for the lambda that returns a specialised " + "generic type local variable and the method it references should have " + "an appropriate descriptor") + { + // Since it is a local variable, the corresponding method takes the + // captured variable as an input + std::string descriptor = "()LDummyGeneric;"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const java_bytecode_parse_treet::methodt &lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == "()LDummyGeneric;"); + REQUIRE_FALSE(lambda_method.is_static); + + const reference_typet dummy_generic_reference_type = + java_reference_type(symbol_typet{"java::DummyGeneric"}); + + const fieldref_exprt generic_reference_fieldref{ + dummy_generic_reference_type, + "memberSpecalisedGeneric", + "java::OuterMemberLambdas"}; + + // since just returning the parameter, nothing to put on the stack + std::vector + expected_instructions{{"aload_0", {}}, // load this of stack + {"getfield", {outer_fieldref}}, + {"getfield", {generic_reference_fieldref}}, + {"areturn", {}}}; + + require_parse_tree::require_instructions_match_expectation( + expected_instructions, lambda_method.instructions); + } + } + } +} diff --git a/unit/java_bytecode/java_bytecode_parser/lambda_examples/ArrayParameterLambda.class b/unit/java_bytecode/java_bytecode_parser/lambda_examples/ArrayParameterLambda.class new file mode 100644 index 00000000000..56886405324 Binary files /dev/null and b/unit/java_bytecode/java_bytecode_parser/lambda_examples/ArrayParameterLambda.class differ diff --git a/unit/java_bytecode/java_bytecode_parser/lambda_examples/DummyGeneric.class b/unit/java_bytecode/java_bytecode_parser/lambda_examples/DummyGeneric.class new file mode 100644 index 00000000000..afbff9f82d1 Binary files /dev/null and b/unit/java_bytecode/java_bytecode_parser/lambda_examples/DummyGeneric.class differ diff --git a/unit/java_bytecode/java_bytecode_parser/lambda_examples/LambdaInterfaces.java b/unit/java_bytecode/java_bytecode_parser/lambda_examples/LambdaInterfaces.java new file mode 100644 index 00000000000..388d8230df6 --- /dev/null +++ b/unit/java_bytecode/java_bytecode_parser/lambda_examples/LambdaInterfaces.java @@ -0,0 +1,39 @@ +interface SimpleLambda { + public void Execute(); +} + +interface ParameterLambda { + public void Execute(int primitive, Object reference, DummyGeneric specalisedGeneric); +} + +interface ArrayParameterLambda { + public void Execute(int[] primitive, Object[] reference, DummyGeneric[] specalisedGeneric); +} + +interface ReturningLambdaPrimitive { + public int Execute(); +} + +interface ReturningLambdaReference { + public Object Execute(); +} + +interface ReturningLambdaSpecalisedGeneric { + public DummyGeneric Execute(); +} + +interface ReturningLambdaPrimitiveArray { + public int[] Execute(); +} + +interface ReturningLambdaReferenceArray { + public Object[] Execute(); +} + +interface ReturningLambdaSpecalisedGenericArray { + public DummyGeneric[] Execute(); +} + +class DummyGeneric { + T field; +} diff --git a/unit/java_bytecode/java_bytecode_parser/lambda_examples/LocalLambdas.class b/unit/java_bytecode/java_bytecode_parser/lambda_examples/LocalLambdas.class new file mode 100644 index 00000000000..28f9f1d7990 Binary files /dev/null and b/unit/java_bytecode/java_bytecode_parser/lambda_examples/LocalLambdas.class differ diff --git a/unit/java_bytecode/java_bytecode_parser/lambda_examples/LocalLambdas.java b/unit/java_bytecode/java_bytecode_parser/lambda_examples/LocalLambdas.java new file mode 100644 index 00000000000..181662018ee --- /dev/null +++ b/unit/java_bytecode/java_bytecode_parser/lambda_examples/LocalLambdas.java @@ -0,0 +1,59 @@ +public class LocalLambdas { + public static void test() { + int localPrimitive = 5; + Object localReference = null; + DummyGeneric localSpecalisedGeneric = null; + + // Declare some local lambdas + SimpleLambda simpleLambda = () -> { /*NOP*/ }; + + ParameterLambda paramLambda = + (int primitive, Object reference, DummyGeneric specalisedGeneric) -> {}; + ArrayParameterLambda arrayParamLambda = + (int[] primitive, Object[] reference, DummyGeneric[] specalisedGeneric) -> {}; + + ReturningLambdaPrimitive returnPrimitiveLambda = () -> { + return 1; + }; + ReturningLambdaReference returnReferenceLambda = () -> { + return null; + }; + ReturningLambdaSpecalisedGeneric returningSpecalisedGenericLambda = () -> { + return null; + }; + + ReturningLambdaPrimitiveArray returnPrimitiveArrayLambda = () -> { + return null; + }; + ReturningLambdaReferenceArray returnReferenceArrayLambda = () -> { + return null; + }; + ReturningLambdaSpecalisedGenericArray returningSpecalisedGenericArrayLambda = () -> { + return null; + }; + + ReturningLambdaPrimitive returnPrimitiveLambdaCapture = () -> { + return localPrimitive; + }; + ReturningLambdaReference returnReferenceLambdaCapture = () -> { + return localReference; + }; + ReturningLambdaSpecalisedGeneric returningSpecalisedGenericLambdaCapture = () -> { + return localSpecalisedGeneric; + }; + + simpleLambda.Execute(); + paramLambda.Execute(4, null, null); + arrayParamLambda.Execute(null, null, null); + returnPrimitiveLambda.Execute(); + returnReferenceLambda.Execute(); + returningSpecalisedGenericLambda.Execute(); + returnPrimitiveArrayLambda.Execute(); + returnReferenceArrayLambda.Execute(); + returningSpecalisedGenericArrayLambda.Execute(); + + returnPrimitiveLambdaCapture.Execute(); + returnReferenceLambdaCapture.Execute(); + returningSpecalisedGenericLambdaCapture.Execute(); + } +} diff --git a/unit/java_bytecode/java_bytecode_parser/lambda_examples/MemberLambdas.class b/unit/java_bytecode/java_bytecode_parser/lambda_examples/MemberLambdas.class new file mode 100644 index 00000000000..04984e52720 Binary files /dev/null and b/unit/java_bytecode/java_bytecode_parser/lambda_examples/MemberLambdas.class differ diff --git a/unit/java_bytecode/java_bytecode_parser/lambda_examples/MemberLambdas.java b/unit/java_bytecode/java_bytecode_parser/lambda_examples/MemberLambdas.java new file mode 100644 index 00000000000..fa7a99e358c --- /dev/null +++ b/unit/java_bytecode/java_bytecode_parser/lambda_examples/MemberLambdas.java @@ -0,0 +1,38 @@ +public class MemberLambdas { + + int memberPrimitive; + Object memberReference; + DummyGeneric memberSpecalisedGeneric; + + SimpleLambda simpleLambda = () -> { /*NOP*/ }; + ParameterLambda paramLambda = (int primitive, Object reference, DummyGeneric specalisedGeneric) -> {}; + ArrayParameterLambda arrayParamLambda = (int[] primitive, Object[] reference, DummyGeneric[] specalisedGeneric) -> {}; + ReturningLambdaPrimitive returnPrimitiveLambda = () -> { return 1; }; + ReturningLambdaReference returnReferenceLambda = () -> { return null; }; + ReturningLambdaSpecalisedGeneric returningSpecalisedGenericLambda = () -> { return null; }; + ReturningLambdaPrimitiveArray returnPrimitiveArrayLambda = () -> { return null; }; + ReturningLambdaReferenceArray returnReferenceArrayLambda = () -> { return null; }; + ReturningLambdaSpecalisedGenericArray returningSpecalisedGenericArrayLambda = () -> { return null; }; + + ReturningLambdaPrimitive returnPrimitiveLambdaCapture = () -> { return memberPrimitive; }; + ReturningLambdaReference returnReferenceLambdaCapture = () -> { return memberReference; }; + ReturningLambdaSpecalisedGeneric returningSpecalisedGenericLambdaCapture = () -> { return memberSpecalisedGeneric; }; + + + public void testMethod() { + simpleLambda.Execute(); + paramLambda.Execute(4, null, null); + arrayParamLambda.Execute(null, null, null); + returnPrimitiveLambda.Execute(); + returnReferenceLambda.Execute(); + returningSpecalisedGenericLambda.Execute(); + returnPrimitiveArrayLambda.Execute(); + returnReferenceArrayLambda.Execute(); + returningSpecalisedGenericArrayLambda.Execute(); + + returnPrimitiveLambdaCapture.Execute(); + returnReferenceLambdaCapture.Execute(); + returningSpecalisedGenericLambdaCapture.Execute(); + } +} + diff --git a/unit/java_bytecode/java_bytecode_parser/lambda_examples/OuterMemberLambdas$Inner.class b/unit/java_bytecode/java_bytecode_parser/lambda_examples/OuterMemberLambdas$Inner.class new file mode 100644 index 00000000000..19b99a98190 Binary files /dev/null and b/unit/java_bytecode/java_bytecode_parser/lambda_examples/OuterMemberLambdas$Inner.class differ diff --git a/unit/java_bytecode/java_bytecode_parser/lambda_examples/OuterMemberLambdas.class b/unit/java_bytecode/java_bytecode_parser/lambda_examples/OuterMemberLambdas.class new file mode 100644 index 00000000000..da380075695 Binary files /dev/null and b/unit/java_bytecode/java_bytecode_parser/lambda_examples/OuterMemberLambdas.class differ diff --git a/unit/java_bytecode/java_bytecode_parser/lambda_examples/OuterMemberLambdas.java b/unit/java_bytecode/java_bytecode_parser/lambda_examples/OuterMemberLambdas.java new file mode 100644 index 00000000000..e1255c0ae20 --- /dev/null +++ b/unit/java_bytecode/java_bytecode_parser/lambda_examples/OuterMemberLambdas.java @@ -0,0 +1,22 @@ +public class OuterMemberLambdas { + + int memberPrimitive; + Object memberReference; + DummyGeneric memberSpecalisedGeneric; + + public class Inner { + + ReturningLambdaPrimitive returnPrimitiveLambdaCapture = () -> { return memberPrimitive; }; + ReturningLambdaReference returnReferenceLambdaCapture = () -> { return memberReference; }; + ReturningLambdaSpecalisedGeneric returningSpecalisedGenericLambdaCapture = () -> { return memberSpecalisedGeneric; }; + + + public void testMethod() { + + returnPrimitiveLambdaCapture.Execute(); + returnReferenceLambdaCapture.Execute(); + returningSpecalisedGenericLambdaCapture.Execute(); + } + } +} + diff --git a/unit/java_bytecode/java_bytecode_parser/lambda_examples/ParameterLambda.class b/unit/java_bytecode/java_bytecode_parser/lambda_examples/ParameterLambda.class new file mode 100644 index 00000000000..9d52eef3aa7 Binary files /dev/null and b/unit/java_bytecode/java_bytecode_parser/lambda_examples/ParameterLambda.class differ diff --git a/unit/java_bytecode/java_bytecode_parser/lambda_examples/ReturningLambdaPrimitive.class b/unit/java_bytecode/java_bytecode_parser/lambda_examples/ReturningLambdaPrimitive.class new file mode 100644 index 00000000000..81073aa0155 Binary files /dev/null and b/unit/java_bytecode/java_bytecode_parser/lambda_examples/ReturningLambdaPrimitive.class differ diff --git a/unit/java_bytecode/java_bytecode_parser/lambda_examples/ReturningLambdaPrimitiveArray.class b/unit/java_bytecode/java_bytecode_parser/lambda_examples/ReturningLambdaPrimitiveArray.class new file mode 100644 index 00000000000..f9f231bc326 Binary files /dev/null and b/unit/java_bytecode/java_bytecode_parser/lambda_examples/ReturningLambdaPrimitiveArray.class differ diff --git a/unit/java_bytecode/java_bytecode_parser/lambda_examples/ReturningLambdaReference.class b/unit/java_bytecode/java_bytecode_parser/lambda_examples/ReturningLambdaReference.class new file mode 100644 index 00000000000..549084ee1a6 Binary files /dev/null and b/unit/java_bytecode/java_bytecode_parser/lambda_examples/ReturningLambdaReference.class differ diff --git a/unit/java_bytecode/java_bytecode_parser/lambda_examples/ReturningLambdaReferenceArray.class b/unit/java_bytecode/java_bytecode_parser/lambda_examples/ReturningLambdaReferenceArray.class new file mode 100644 index 00000000000..423190ffd3f Binary files /dev/null and b/unit/java_bytecode/java_bytecode_parser/lambda_examples/ReturningLambdaReferenceArray.class differ diff --git a/unit/java_bytecode/java_bytecode_parser/lambda_examples/ReturningLambdaSpecalisedGeneric.class b/unit/java_bytecode/java_bytecode_parser/lambda_examples/ReturningLambdaSpecalisedGeneric.class new file mode 100644 index 00000000000..3a82b5ddbda Binary files /dev/null and b/unit/java_bytecode/java_bytecode_parser/lambda_examples/ReturningLambdaSpecalisedGeneric.class differ diff --git a/unit/java_bytecode/java_bytecode_parser/lambda_examples/ReturningLambdaSpecalisedGenericArray.class b/unit/java_bytecode/java_bytecode_parser/lambda_examples/ReturningLambdaSpecalisedGenericArray.class new file mode 100644 index 00000000000..8e9183ed10b Binary files /dev/null and b/unit/java_bytecode/java_bytecode_parser/lambda_examples/ReturningLambdaSpecalisedGenericArray.class differ diff --git a/unit/java_bytecode/java_bytecode_parser/lambda_examples/SimpleLambda.class b/unit/java_bytecode/java_bytecode_parser/lambda_examples/SimpleLambda.class new file mode 100644 index 00000000000..ff1dae8ba4b Binary files /dev/null and b/unit/java_bytecode/java_bytecode_parser/lambda_examples/SimpleLambda.class differ diff --git a/unit/java_bytecode/java_bytecode_parser/lambda_examples/StaticLambdas.class b/unit/java_bytecode/java_bytecode_parser/lambda_examples/StaticLambdas.class new file mode 100644 index 00000000000..a5d959f5781 Binary files /dev/null and b/unit/java_bytecode/java_bytecode_parser/lambda_examples/StaticLambdas.class differ diff --git a/unit/java_bytecode/java_bytecode_parser/lambda_examples/StaticLambdas.java b/unit/java_bytecode/java_bytecode_parser/lambda_examples/StaticLambdas.java new file mode 100644 index 00000000000..697b2209f47 --- /dev/null +++ b/unit/java_bytecode/java_bytecode_parser/lambda_examples/StaticLambdas.java @@ -0,0 +1,37 @@ +public class StaticLambdas { + + static int staticPrimitive; + static Object staticReference; + static DummyGeneric staticSpecalisedGeneric; + + static SimpleLambda simpleLambda = () -> { /*NOP*/ }; + static ParameterLambda paramLambda = (int primitive, Object reference, DummyGeneric specalisedGeneric) -> {}; + static ArrayParameterLambda arrayParamLambda = (int[] primitive, Object[] reference, DummyGeneric[] specalisedGeneric) -> {}; + static ReturningLambdaPrimitive returnPrimitiveLambda = () -> { return 1; }; + static ReturningLambdaReference returnReferenceLambda = () -> { return null; }; + static ReturningLambdaSpecalisedGeneric returningSpecalisedGenericLambda = () -> { return null; }; + static ReturningLambdaPrimitiveArray returnPrimitiveArrayLambda = () -> { return null; }; + static ReturningLambdaReferenceArray returnReferenceArrayLambda = () -> { return null; }; + static ReturningLambdaSpecalisedGenericArray returningSpecalisedGenericArrayLambda = () -> { return null; }; + + static ReturningLambdaPrimitive returnPrimitiveLambdaCapture = () -> { return staticPrimitive; }; + static ReturningLambdaReference returnReferenceLambdaCapture = () -> { return staticReference; }; + static ReturningLambdaSpecalisedGeneric returningSpecalisedGenericLambdaCapture = () -> { return staticSpecalisedGeneric; }; + + + public static void testMethod() { + simpleLambda.Execute(); + paramLambda.Execute(4, null, null); + arrayParamLambda.Execute(null, null, null); + returnPrimitiveLambda.Execute(); + returnReferenceLambda.Execute(); + returningSpecalisedGenericLambda.Execute(); + returnPrimitiveArrayLambda.Execute(); + returnReferenceArrayLambda.Execute(); + returningSpecalisedGenericArrayLambda.Execute(); + + returnPrimitiveLambdaCapture.Execute(); + returnReferenceLambdaCapture.Execute(); + returningSpecalisedGenericLambdaCapture.Execute(); + } +} diff --git a/unit/testing-utils/require_parse_tree.cpp b/unit/testing-utils/require_parse_tree.cpp new file mode 100644 index 00000000000..70922d4b55a --- /dev/null +++ b/unit/testing-utils/require_parse_tree.cpp @@ -0,0 +1,117 @@ +/*******************************************************************\ + + Module: Unit test utilities + + Author: DiffBlue Limited. All rights reserved. + +\*******************************************************************/ + +#include "require_parse_tree.h" + +/// Find in the parsed class a specific entry within the +/// lambda_method_handle_map with a matching descriptor. Will fail if no +/// matching lambda entry found. +/// \param parsed_class: the class to inspect +/// \param descriptor: the descriptor the lambda method should have +/// \param entry_index: the number to skip (i.e. if multiple entries with the +/// same descriptor +/// \return +require_parse_tree::lambda_method_handlet +require_parse_tree::require_lambda_entry_for_descriptor( + const java_bytecode_parse_treet::classt &parsed_class, + const std::string &descriptor, + const size_t entry_index) +{ + REQUIRE(entry_index < parsed_class.lambda_method_handle_map.size()); + typedef java_bytecode_parse_treet::classt::lambda_method_handle_mapt:: + value_type lambda_method_entryt; + + size_t matches_found = 0; + + const auto matching_lambda_entry = std::find_if( + parsed_class.lambda_method_handle_map.begin(), + parsed_class.lambda_method_handle_map.end(), + [&descriptor, &matches_found, &entry_index]( + const lambda_method_entryt &entry) { + if(entry.second.method_type == descriptor) + { + ++matches_found; + return matches_found == entry_index + 1; + } + return false; + }); + + INFO("Looking for descriptor: " << descriptor); + std::ostringstream found_entries; + for(const auto entry : parsed_class.lambda_method_handle_map) + { + found_entries << id2string(entry.first.first) << ": " + << id2string(entry.second.method_type) << std::endl; + } + INFO("Found descriptors:\n" << found_entries.str()); + + REQUIRE(matching_lambda_entry != parsed_class.lambda_method_handle_map.end()); + + return matching_lambda_entry->second; +} + +/// Finds a specific method in the parsed class with a matching name. +/// \param parsed_class: The class +/// \param method_name: The name of the method to look for +/// \return The methodt structure with the corresponding name +const require_parse_tree::methodt require_parse_tree::require_method( + const java_bytecode_parse_treet::classt &parsed_class, + const irep_idt &method_name) +{ + const auto method = std::find_if( + parsed_class.methods.begin(), + parsed_class.methods.end(), + [&method_name](const java_bytecode_parse_treet::methodt &method) { + return method.name == method_name; + }); + + INFO("Looking for method: " << method_name); + std::ostringstream found_methods; + for(const auto entry : parsed_class.methods) + { + found_methods << id2string(entry.name) << std::endl; + } + INFO("Found methods:\n" << found_methods.str()); + + REQUIRE(method != parsed_class.methods.end()); + + return *method; +} + +/// Verify whether a given methods instructions match an expectation +/// \param expected_instructions: The expected instructions for a given method +/// \param instructions: The instructions of a method +void require_parse_tree::require_instructions_match_expectation( + const expected_instructionst &expected_instructions, + const java_bytecode_parse_treet::methodt::instructionst instructions) +{ + REQUIRE(instructions.size() == expected_instructions.size()); + auto actual_instruction_it = instructions.begin(); + for(const auto expected_instruction : expected_instructions) + { + expected_instruction.require_instructions_equal(*actual_instruction_it); + ++actual_instruction_it; + } +} + +/// Check whether a given instruction matches an expectation of the instruction +/// \param actual_instruction: The instruction to check +void require_parse_tree::expected_instructiont::require_instructions_equal( + java_bytecode_parse_treet::instructiont actual_instruction) const +{ + REQUIRE(actual_instruction.statement == instruction_mnemoic); + REQUIRE(actual_instruction.args.size() == instruction_arguments.size()); + auto actual_arg_it = actual_instruction.args.begin(); + for(const exprt &expected_arg : actual_instruction.args) + { + INFO("Expected argument" << expected_arg.pretty()); + INFO("Actual argument" << actual_arg_it->pretty()); + REQUIRE(*actual_arg_it == expected_arg); + ++actual_arg_it; + } +} diff --git a/unit/testing-utils/require_parse_tree.h b/unit/testing-utils/require_parse_tree.h new file mode 100644 index 00000000000..35b3ab195d2 --- /dev/null +++ b/unit/testing-utils/require_parse_tree.h @@ -0,0 +1,60 @@ +/*******************************************************************\ + + Module: Unit test utilities + + Author: DiffBlue Limited. All rights reserved. + +\*******************************************************************/ + +/// \file +/// Utilties for inspecting java_parse_treet + +#ifndef CPROVER_TESTING_UTILS_REQUIRE_PARSE_TREE_H +#define CPROVER_TESTING_UTILS_REQUIRE_PARSE_TREE_H + +#include +#include "catch.hpp" + +// NOLINTNEXTLINE(readability/namespace) +namespace require_parse_tree +{ +typedef java_bytecode_parse_treet::classt::lambda_method_handlet + lambda_method_handlet; + +lambda_method_handlet require_lambda_entry_for_descriptor( + const java_bytecode_parse_treet::classt &parsed_class, + const std::string &descriptor, + const size_t entry_index = 0); + +typedef java_bytecode_parse_treet::methodt methodt; + +const methodt require_method( + const java_bytecode_parse_treet::classt &parsed_class, + const irep_idt &method_name); + +struct expected_instructiont +{ + expected_instructiont( + const irep_idt &instruction_mnemoic, + const std::vector &instruction_arguments) + : instruction_mnemoic(instruction_mnemoic), + instruction_arguments(instruction_arguments) + { + } + + void require_instructions_equal( + java_bytecode_parse_treet::instructiont actual_instruction) const; + +private: + const irep_idt instruction_mnemoic; + const std::vector instruction_arguments; +}; + +typedef std::vector expected_instructionst; + +void require_instructions_match_expectation( + const expected_instructionst &expected_instructions, + const java_bytecode_parse_treet::methodt::instructionst instructions); +} + +#endif //CPROVER_TESTING_UTILS_REQUIRE_PARSE_TREE_H