From 770eb2a5b56fcf27073bdd6e1b99ba34dc884cdd Mon Sep 17 00:00:00 2001 From: thk123 Date: Thu, 15 Mar 2018 18:10:14 +0000 Subject: [PATCH 01/30] Remove methods without a implementation or usage --- src/java_bytecode/java_bytecode_convert_method_class.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/java_bytecode/java_bytecode_convert_method_class.h b/src/java_bytecode/java_bytecode_convert_method_class.h index aa3a008faaf..91995fcc0cf 100644 --- a/src/java_bytecode/java_bytecode_convert_method_class.h +++ b/src/java_bytecode/java_bytecode_convert_method_class.h @@ -250,8 +250,6 @@ class java_bytecode_convert_methodt:public messaget const bytecode_infot &get_bytecode_info(const irep_idt &statement); - bool class_needs_clinit(const irep_idt &classname); - exprt get_or_create_clinit_wrapper(const irep_idt &classname); codet get_clinit_call(const irep_idt &classname); bool is_method_inherited( From 39282a6ca1470575f1d8d10fca84d6f2539bb436 Mon Sep 17 00:00:00 2001 From: thk123 Date: Mon, 19 Mar 2018 11:26:38 +0000 Subject: [PATCH 02/30] Add debug information for working directory The most common error when running unit tests is forgetting to set the working direcotory. This causes problems when loading java classes so to minimize this we print the working directory in the catch output. --- unit/testing-utils/load_java_class.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/unit/testing-utils/load_java_class.cpp b/unit/testing-utils/load_java_class.cpp index c8da44814cc..f198008dff4 100644 --- a/unit/testing-utils/load_java_class.cpp +++ b/unit/testing-utils/load_java_class.cpp @@ -17,6 +17,7 @@ #include #include +#include /// Go through the process of loading, type-checking and finalising loading a /// specific class file to build the symbol table. The functions are converted @@ -128,6 +129,12 @@ symbol_tablet load_java_class( const typet &class_type=class_symbol.type; REQUIRE(class_type.id()==ID_struct); + // Log the working directory to help people identify the common error + // of wrong working directory (should be the `unit` directory when running + // the unit tests). + std::string path = get_current_working_directory(); + INFO("Working directory: " << path); + // if this fails it indicates the class was not loaded // Check your working directory and the class path is correctly configured // as this often indicates that one of these is wrong. From 6df8d6b8d563091b212ec2f23ceba2f7affa725a Mon Sep 17 00:00:00 2001 From: thk123 Date: Mon, 19 Mar 2018 11:47:38 +0000 Subject: [PATCH 03/30] Extended require_goto_statements to provide meaningful errors --- .../testing-utils/require_goto_statements.cpp | 36 +++++++++++++------ 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/unit/testing-utils/require_goto_statements.cpp b/unit/testing-utils/require_goto_statements.cpp index 7a2974bb827..3df493ab9f9 100644 --- a/unit/testing-utils/require_goto_statements.cpp +++ b/unit/testing-utils/require_goto_statements.cpp @@ -153,28 +153,42 @@ require_goto_statements::find_pointer_assignments( const std::vector &instructions) { pointer_assignment_locationt locations; + bool found_assignment = false; + std::vector all_symbols; for(const codet &statement : instructions) { if(statement.get_statement() == ID_assign) { const code_assignt &code_assign = to_code_assign(statement); - if( - code_assign.lhs().id() == ID_symbol && - to_symbol_expr(code_assign.lhs()).get_identifier() == pointer_name) + if(code_assign.lhs().id() == ID_symbol) { - if( - code_assign.rhs() == - null_pointer_exprt(to_pointer_type(code_assign.lhs().type()))) + const symbol_exprt &symbol_expr = to_symbol_expr(code_assign.lhs()); + all_symbols.push_back(symbol_expr.get_identifier()); + if(symbol_expr.get_identifier() == pointer_name) { - locations.null_assignment = code_assign; - } - else - { - locations.non_null_assignments.push_back(code_assign); + if( + code_assign.rhs() == + null_pointer_exprt(to_pointer_type(code_assign.lhs().type()))) + { + locations.null_assignment = code_assign; + } + else + { + locations.non_null_assignments.push_back(code_assign); + } + found_assignment = true; } } } } + INFO("Looking for symbol: " << pointer_name); + std::ostringstream found_symbols; + for(const auto entry : all_symbols) + { + found_symbols << entry << std::endl; + } + INFO("Symbols: " << found_symbols.str()); + REQUIRE(found_assignment); return locations; } From 46fa1764d84a1936b98669ae4e5f77a25ae59fd7 Mon Sep 17 00:00:00 2001 From: thk123 Date: Mon, 19 Mar 2018 12:27:24 +0000 Subject: [PATCH 04/30] Extending require utilities to be used in test --- unit/testing-utils/require_expr.cpp | 36 +++++++++++++++++++++++++++-- unit/testing-utils/require_expr.h | 9 ++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/unit/testing-utils/require_expr.cpp b/unit/testing-utils/require_expr.cpp index b4ad9d470c6..c338293f3d2 100644 --- a/unit/testing-utils/require_expr.cpp +++ b/unit/testing-utils/require_expr.cpp @@ -16,6 +16,7 @@ #include #include +#include /// Verify a given exprt is an index_exprt with a a constant value equal to the /// expected value @@ -69,8 +70,39 @@ member_exprt require_expr::require_member( symbol_exprt require_expr::require_symbol( const exprt &expr, const irep_idt &symbol_name) { - REQUIRE(expr.id()==ID_symbol); - const symbol_exprt &symbol_expr=to_symbol_expr(expr); + const symbol_exprt &symbol_expr = require_symbol(expr); REQUIRE(symbol_expr.get_identifier()==symbol_name); return symbol_expr; } + +/// Verify a given exprt is a symbol_exprt. +/// \param expr: The expression. +/// \return The expr cast to a symbol_exprt +symbol_exprt require_expr::require_symbol(const exprt &expr) +{ + REQUIRE(expr.id() == ID_symbol); + return to_symbol_expr(expr); +} + +/// Verify a given exprt is a typecast_expr. +/// \param expr: The expression. +/// \return The expr cast to a typecast_exprt +typecast_exprt require_expr::require_typecast(const exprt &expr) +{ + REQUIRE(expr.id() == ID_typecast); + return to_typecast_expr(expr); +} + +/// Verify a given exprt is a side_effect_exprt with appropriate statement. +/// \param expr: The expression. +/// \param symbol_name: The intended identifier of statement +/// \return The expr cast to a side_effect_exprt +side_effect_exprt require_expr::require_side_effect_expr( + const exprt &expr, + const irep_idt &side_effect_statement) +{ + REQUIRE(expr.id() == ID_side_effect); + const side_effect_exprt &side_effect_expr = to_side_effect_expr(expr); + REQUIRE(side_effect_expr.get_statement() == side_effect_statement); + return side_effect_expr; +} diff --git a/unit/testing-utils/require_expr.h b/unit/testing-utils/require_expr.h index 6e545e90a3a..b663578a94c 100644 --- a/unit/testing-utils/require_expr.h +++ b/unit/testing-utils/require_expr.h @@ -16,6 +16,7 @@ #define CPROVER_TESTING_UTILS_REQUIRE_EXPR_H #include +#include // NOLINTNEXTLINE(readability/namespace) namespace require_expr @@ -28,6 +29,14 @@ namespace require_expr symbol_exprt require_symbol( const exprt &expr, const irep_idt &symbol_name); + + symbol_exprt require_symbol(const exprt &expr); + + typecast_exprt require_typecast(const exprt &expr); + + side_effect_exprt require_side_effect_expr( + const exprt &expr, + const irep_idt &side_effect_statement); } #endif // CPROVER_TESTING_UTILS_REQUIRE_EXPR_H From 2348d10f737cf68e8437cbbaebacd77dcf69d8ab Mon Sep 17 00:00:00 2001 From: thk123 Date: Mon, 19 Mar 2018 14:08:28 +0000 Subject: [PATCH 05/30] Adding unit test for checking local lambda conversion Verifies the structure mimics the construction of an anonymous class that implements the interface --- unit/Makefile | 1 + .../convert_invoke_dynamic.cpp | 93 +++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp diff --git a/unit/Makefile b/unit/Makefile index c3872f7a98b..1157bb72db0 100644 --- a/unit/Makefile +++ b/unit/Makefile @@ -21,6 +21,7 @@ SRC += unit_tests.cpp \ goto-programs/class_hierarchy_graph.cpp \ goto-programs/remove_virtual_functions_without_fallback.cpp \ java_bytecode/java_bytecode_convert_class/convert_abstract_class.cpp \ + java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp \ java_bytecode/java_bytecode_parse_generics/parse_generic_class.cpp \ java_bytecode/java_object_factory/gen_nondet_string_init.cpp \ java_bytecode/java_bytecode_parse_lambdas/java_bytecode_parse_lambda_method_table.cpp \ diff --git a/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp b/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp new file mode 100644 index 00000000000..4cda50514f9 --- /dev/null +++ b/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp @@ -0,0 +1,93 @@ +/*******************************************************************\ + + Module: Unit tests for converting invokedynamic instructions into codet + + Author: Diffblue Ltd. + +\*******************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +SCENARIO( + "Converting invokedynamic with a local lambda", + "[core]" + "[lamdba][java_bytecode][java_bytecode_convert_method][!mayfail]") +{ + // NOLINTNEXTLINE(whitespace/braces) + run_test_with_compilers([](const std::string &compiler) { + GIVEN( + "A class with a static lambda variables from " + compiler + " compiler.") + { + symbol_tablet symbol_table = load_java_class( + "LocalLambdas", + "./java_bytecode/java_bytecode_parser/lambda_examples/" + compiler + + "_classes/", + "LocalLambdas.test"); + + WHEN("Inspecting the assignments of the entry function") + { + const std::vector &instructions = + require_goto_statements::get_all_statements( + symbol_table.lookup_ref("java::LocalLambdas.test:()V").value); + + THEN("The local variable should be assigned a non-null pointer") + { + // TODO(tkiley): we don't want 11 here + // TODO(tkiley): This is the actual lambda which doesn't currently work + const auto lambda_assignment = + require_goto_statements::find_pointer_assignments( + "java::LocalLambdas.test:()V::11::simpleLambda", instructions); + + REQUIRE(lambda_assignment.non_null_assignments.size() == 1); + REQUIRE_FALSE(lambda_assignment.null_assignment.has_value()); + + const typecast_exprt &rhs_value = require_expr::require_typecast( + lambda_assignment.non_null_assignments[0].rhs()); + + const symbol_exprt &rhs_symbol = + require_expr::require_symbol(rhs_value.op0()); + + const irep_idt &tmp_object_symbol = rhs_symbol.get_identifier(); + + const auto tmp_object_assignments = + require_goto_statements::find_pointer_assignments( + tmp_object_symbol, instructions); + + REQUIRE(tmp_object_assignments.non_null_assignments.size() == 1); + REQUIRE_FALSE(tmp_object_assignments.null_assignment.has_value()); + + const side_effect_exprt &side_effect_expr = + require_expr::require_side_effect_expr( + tmp_object_assignments.non_null_assignments[0].rhs(), + ID_java_new); + + const pointer_typet &lambda_temp_type = + require_type::require_pointer(side_effect_expr.type(), {}); + + const symbol_typet &lambda_implementor_type = + require_type::require_symbol(lambda_temp_type.subtype()); + + const irep_idt &tmp_class_identifier = + lambda_implementor_type.get_identifier(); + + const symbolt &lambda_implementor_type_symbol = + require_symbol::require_symbol_exists( + symbol_table, tmp_class_identifier); + + REQUIRE(lambda_implementor_type_symbol.is_type); + const class_typet &tmp_lambda_class_type = + require_type::require_complete_class( + lambda_implementor_type_symbol.type); + + REQUIRE(tmp_lambda_class_type.has_base("java::SimpleLambda")); + } + } + } + }); +} From ee2179c4f09eaf0c7708c05a27d68dd646ed7e57 Mon Sep 17 00:00:00 2001 From: thk123 Date: Mon, 19 Mar 2018 15:12:48 +0000 Subject: [PATCH 06/30] Introduce checks the the function body for Execute calls the correct lambda --- .../convert_invoke_dynamic.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp b/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp index 4cda50514f9..f6d40c954f5 100644 --- a/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp +++ b/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp @@ -86,6 +86,23 @@ SCENARIO( lambda_implementor_type_symbol.type); REQUIRE(tmp_lambda_class_type.has_base("java::SimpleLambda")); + + THEN("The function in the class should call the lambda method") + { + const irep_idt method_identifier = + id2string(tmp_class_identifier) + ".Execute:()V"; + const symbolt &method_symbol = + require_symbol::require_symbol_exists( + symbol_table, method_identifier); + + REQUIRE(method_symbol.is_function()); + + const std::vector &assignments = + require_goto_statements::get_all_statements(method_symbol.value); + + require_goto_statements::require_function_call( + assignments, "java::LocalLambdas.lambda$test$0:()V"); + } } } } From bea730d3845b35334ce5c3fc9497e1cb92ee71b4 Mon Sep 17 00:00:00 2001 From: thk123 Date: Mon, 19 Mar 2018 15:49:15 +0000 Subject: [PATCH 07/30] Adding utility for verifying a set of statements contains a function call --- .../testing-utils/require_goto_statements.cpp | 36 +++++++++++++++++++ unit/testing-utils/require_goto_statements.h | 23 ++++++++++++ 2 files changed, 59 insertions(+) diff --git a/unit/testing-utils/require_goto_statements.cpp b/unit/testing-utils/require_goto_statements.cpp index 3df493ab9f9..e20757d1b2b 100644 --- a/unit/testing-utils/require_goto_statements.cpp +++ b/unit/testing-utils/require_goto_statements.cpp @@ -409,3 +409,39 @@ require_goto_statements::require_entry_point_argument_assignment( .get_identifier(); return argument_tmp_name; } + +/// Verify that a collection of statements contains a function call to a +/// function whose symbol identifier matches the provided identifier +/// \param statements: The collection of statements to inspect +/// \param function_call_identifier: The symbol identifier of the function +/// that should have been called +/// \return The first code_function_callt to the relevant function or throws a +/// no_matching_function_callt if no call is found +code_function_callt require_goto_statements::require_function_call( + const std::vector &statements, + const irep_idt &function_call_identifier) +{ + // TODO: Would appreciate some review comments on whether it makes the most sense: + // - return a vector of matching code_function_calls + // - return an optionalt with the first matching call + // - return a code_function_callt throwing an exception if none found + for(const codet &statement : statements) + { + if(statement.get_statement() == ID_function_call) + { + const code_function_callt &function_call = + to_code_function_call(statement); + + if(function_call.function().id() == ID_symbol) + { + if( + to_symbol_expr(function_call.function()).get_identifier() == + function_call_identifier) + { + return function_call; + } + } + } + } + throw no_matching_function_callt(function_call_identifier); +} diff --git a/unit/testing-utils/require_goto_statements.h b/unit/testing-utils/require_goto_statements.h index f2767b73b55..cf20b2b5974 100644 --- a/unit/testing-utils/require_goto_statements.h +++ b/unit/testing-utils/require_goto_statements.h @@ -47,6 +47,25 @@ class no_decl_found_exceptiont : public std::exception std::string _varname; }; +class no_matching_function_callt : public std::exception +{ +public: + explicit no_matching_function_callt(const irep_idt &function_identifier) + : function_identifier(function_identifier) + { + } + + virtual const char *what() const throw() + { + std::ostringstream stringStream; + stringStream << "Failed to find function call for: " << function_identifier; + return stringStream.str().c_str(); + } + +private: + irep_idt function_identifier; +}; + pointer_assignment_locationt find_struct_component_assignments( const std::vector &statements, const irep_idt &structure_name, @@ -85,6 +104,10 @@ const irep_idt &require_struct_array_component_assignment( const irep_idt &require_entry_point_argument_assignment( const irep_idt &argument_name, const std::vector &entry_point_statements); + +code_function_callt require_function_call( + const std::vector &statements, + const irep_idt &function_call_identifier); } #endif // CPROVER_TESTING_UTILS_REQUIRE_GOTO_STATEMENTS_H From 7b0cee179f26fc92b5d4d08f24a6f881c069be77 Mon Sep 17 00:00:00 2001 From: thk123 Date: Mon, 19 Mar 2018 17:33:52 +0000 Subject: [PATCH 08/30] Refactored test method to allow reuse --- .../convert_invoke_dynamic.cpp | 151 ++++++++++-------- 1 file changed, 86 insertions(+), 65 deletions(-) diff --git a/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp b/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp index f6d40c954f5..a07f4f0072d 100644 --- a/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp +++ b/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp @@ -14,6 +14,81 @@ #include #include +struct lambda_assignment_test_datat +{ + irep_idt lambda_variable_id; + irep_idt lambda_interface; + std::string lambda_interface_method_descriptor; + irep_idt lambda_function_id; +}; + +void validate_lamdba_assignement( + const symbol_tablet &symbol_table, + const std::vector &instructions, + const lambda_assignment_test_datat &test_data) +{ + const auto lambda_assignment = + require_goto_statements::find_pointer_assignments( + test_data.lambda_variable_id, instructions); + + REQUIRE(lambda_assignment.non_null_assignments.size() == 1); + REQUIRE_FALSE(lambda_assignment.null_assignment.has_value()); + + const typecast_exprt &rhs_value = require_expr::require_typecast( + lambda_assignment.non_null_assignments[0].rhs()); + + const symbol_exprt &rhs_symbol = + require_expr::require_symbol(rhs_value.op0()); + + const irep_idt &tmp_object_symbol = rhs_symbol.get_identifier(); + + const auto tmp_object_assignments = + require_goto_statements::find_pointer_assignments( + tmp_object_symbol, instructions); + + REQUIRE(tmp_object_assignments.non_null_assignments.size() == 1); + REQUIRE_FALSE(tmp_object_assignments.null_assignment.has_value()); + + const side_effect_exprt &side_effect_expr = + require_expr::require_side_effect_expr( + tmp_object_assignments.non_null_assignments[0].rhs(), ID_java_new); + + const pointer_typet &lambda_temp_type = + require_type::require_pointer(side_effect_expr.type(), {}); + + const symbol_typet &lambda_implementor_type = + require_type::require_symbol(lambda_temp_type.subtype()); + + const irep_idt &tmp_class_identifier = + lambda_implementor_type.get_identifier(); + + const symbolt &lambda_implementor_type_symbol = + require_symbol::require_symbol_exists(symbol_table, tmp_class_identifier); + + REQUIRE(lambda_implementor_type_symbol.is_type); + const class_typet &tmp_lambda_class_type = + require_type::require_complete_class(lambda_implementor_type_symbol.type); + + REQUIRE(tmp_lambda_class_type.has_base(test_data.lambda_interface)); + + THEN("The function in the class should call the lambda method") + { + const irep_idt method_identifier = + id2string(tmp_class_identifier) + + test_data.lambda_interface_method_descriptor; + const symbolt &method_symbol = + require_symbol::require_symbol_exists(symbol_table, method_identifier); + + REQUIRE(method_symbol.is_function()); + + const std::vector &assignments = + require_goto_statements::get_all_statements(method_symbol.value); + + require_goto_statements::require_function_call( + assignments, test_data.lambda_function_id); + } +} + SCENARIO( "Converting invokedynamic with a local lambda", "[core]" @@ -36,73 +111,19 @@ SCENARIO( require_goto_statements::get_all_statements( symbol_table.lookup_ref("java::LocalLambdas.test:()V").value); - THEN("The local variable should be assigned a non-null pointer") - { - // TODO(tkiley): we don't want 11 here - // TODO(tkiley): This is the actual lambda which doesn't currently work - const auto lambda_assignment = - require_goto_statements::find_pointer_assignments( - "java::LocalLambdas.test:()V::11::simpleLambda", instructions); - - REQUIRE(lambda_assignment.non_null_assignments.size() == 1); - REQUIRE_FALSE(lambda_assignment.null_assignment.has_value()); - - const typecast_exprt &rhs_value = require_expr::require_typecast( - lambda_assignment.non_null_assignments[0].rhs()); - - const symbol_exprt &rhs_symbol = - require_expr::require_symbol(rhs_value.op0()); - - const irep_idt &tmp_object_symbol = rhs_symbol.get_identifier(); - - const auto tmp_object_assignments = - require_goto_statements::find_pointer_assignments( - tmp_object_symbol, instructions); - - REQUIRE(tmp_object_assignments.non_null_assignments.size() == 1); - REQUIRE_FALSE(tmp_object_assignments.null_assignment.has_value()); - - const side_effect_exprt &side_effect_expr = - require_expr::require_side_effect_expr( - tmp_object_assignments.non_null_assignments[0].rhs(), - ID_java_new); + const std::string function_prefix = "java::LocalLambdas.test:()V"; - const pointer_typet &lambda_temp_type = - require_type::require_pointer(side_effect_expr.type(), {}); - - const symbol_typet &lambda_implementor_type = - require_type::require_symbol(lambda_temp_type.subtype()); - - const irep_idt &tmp_class_identifier = - lambda_implementor_type.get_identifier(); - - const symbolt &lambda_implementor_type_symbol = - require_symbol::require_symbol_exists( - symbol_table, tmp_class_identifier); - - REQUIRE(lambda_implementor_type_symbol.is_type); - const class_typet &tmp_lambda_class_type = - require_type::require_complete_class( - lambda_implementor_type_symbol.type); - - REQUIRE(tmp_lambda_class_type.has_base("java::SimpleLambda")); - - THEN("The function in the class should call the lambda method") - { - const irep_idt method_identifier = - id2string(tmp_class_identifier) + ".Execute:()V"; - const symbolt &method_symbol = - require_symbol::require_symbol_exists( - symbol_table, method_identifier); - - REQUIRE(method_symbol.is_function()); - - const std::vector &assignments = - require_goto_statements::get_all_statements(method_symbol.value); + THEN( + "The local variable should be assigned a temp object implementing " + "SimpleLambda") + { + lambda_assignment_test_datat test_data; + test_data.lambda_variable_id = function_prefix + "::11::simpleLambda"; - require_goto_statements::require_function_call( - assignments, "java::LocalLambdas.lambda$test$0:()V"); - } + test_data.lambda_interface = "java::SimpleLambda"; + test_data.lambda_interface_method_descriptor = ".Execute:()V"; + test_data.lambda_function_id = "java::LocalLambdas.pretendLambda:()V"; + validate_lamdba_assignement(symbol_table, instructions, test_data); } } } From 5610aca228ce3ed9950d22d2b5dd09c708f67388 Mon Sep 17 00:00:00 2001 From: thk123 Date: Mon, 19 Mar 2018 17:36:06 +0000 Subject: [PATCH 09/30] Added unit test for lambda assigned --- .../convert_invoke_dynamic.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp b/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp index a07f4f0072d..9b6ac38bccc 100644 --- a/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp +++ b/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp @@ -125,6 +125,21 @@ SCENARIO( test_data.lambda_function_id = "java::LocalLambdas.pretendLambda:()V"; validate_lamdba_assignement(symbol_table, instructions, test_data); } + THEN( + "The local variable should be assigned a non-null pointer to a " + "parameter interface implementor") + { + lambda_assignment_test_datat test_data; + test_data.lambda_variable_id = function_prefix + "::35::paramLambda"; + + test_data.lambda_interface = "java::ParameterLambda"; + test_data.lambda_interface_method_descriptor = + ".Execute:(ILjava/lang/Object;LDummyGeneric;)V"; + test_data.lambda_function_id = + "java::LocalLambdas.lambda$test$1:(ILjava/lang/" + "Object;LDummyGeneric;)V"; + validate_lamdba_assignement(symbol_table, instructions, test_data); + } } } }); From 99c21ed4446a55740810e56b52970cb0268f500a Mon Sep 17 00:00:00 2001 From: thk123 Date: Tue, 20 Mar 2018 12:08:40 +0000 Subject: [PATCH 10/30] Extended find pointer assignment to take a regex --- .../testing-utils/require_goto_statements.cpp | 19 +++++++++++++++++-- unit/testing-utils/require_goto_statements.h | 6 ++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/unit/testing-utils/require_goto_statements.cpp b/unit/testing-utils/require_goto_statements.cpp index e20757d1b2b..4c752a42e78 100644 --- a/unit/testing-utils/require_goto_statements.cpp +++ b/unit/testing-utils/require_goto_statements.cpp @@ -151,6 +151,19 @@ require_goto_statements::pointer_assignment_locationt require_goto_statements::find_pointer_assignments( const irep_idt &pointer_name, const std::vector &instructions) +{ + INFO("Looking for symbol: " << pointer_name); + std::regex special_chars{R"([-[\]{}()*+?.,\^$|#\s])"}; + std::string sanitized = + std::regex_replace(id2string(pointer_name), special_chars, R"(\$&)"); + return find_pointer_assignments( + std::regex("^" + sanitized + "$"), instructions); +} + +require_goto_statements::pointer_assignment_locationt +require_goto_statements::find_pointer_assignments( + const std::regex &pointer_name_match, + const std::vector &instructions) { pointer_assignment_locationt locations; bool found_assignment = false; @@ -164,7 +177,9 @@ require_goto_statements::find_pointer_assignments( { const symbol_exprt &symbol_expr = to_symbol_expr(code_assign.lhs()); all_symbols.push_back(symbol_expr.get_identifier()); - if(symbol_expr.get_identifier() == pointer_name) + if( + std::regex_search( + id2string(symbol_expr.get_identifier()), pointer_name_match)) { if( code_assign.rhs() == @@ -181,7 +196,7 @@ require_goto_statements::find_pointer_assignments( } } } - INFO("Looking for symbol: " << pointer_name); + std::ostringstream found_symbols; for(const auto entry : all_symbols) { diff --git a/unit/testing-utils/require_goto_statements.h b/unit/testing-utils/require_goto_statements.h index cf20b2b5974..c8100336a5c 100644 --- a/unit/testing-utils/require_goto_statements.h +++ b/unit/testing-utils/require_goto_statements.h @@ -16,6 +16,8 @@ #include #include +#include + #ifndef CPROVER_TESTING_UTILS_REQUIRE_GOTO_STATEMENTS_H #define CPROVER_TESTING_UTILS_REQUIRE_GOTO_STATEMENTS_H @@ -81,6 +83,10 @@ pointer_assignment_locationt find_pointer_assignments( const irep_idt &pointer_name, const std::vector &instructions); +pointer_assignment_locationt find_pointer_assignments( + const std::regex &pointer_name_match, + const std::vector &instructions); + const code_declt &require_declaration_of_name( const irep_idt &variable_name, const std::vector &entry_point_instructions); From d171f64b468ba3937126a4b3c1066a84ec9bef0b Mon Sep 17 00:00:00 2001 From: thk123 Date: Tue, 20 Mar 2018 12:19:26 +0000 Subject: [PATCH 11/30] Swap finding variable values to use regex --- .../convert_invoke_dynamic.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp b/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp index 9b6ac38bccc..67eadec1f28 100644 --- a/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp +++ b/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp @@ -16,7 +16,7 @@ struct lambda_assignment_test_datat { - irep_idt lambda_variable_id; + std::regex lambda_variable_id; irep_idt lambda_interface; std::string lambda_interface_method_descriptor; irep_idt lambda_function_id; @@ -111,14 +111,16 @@ SCENARIO( require_goto_statements::get_all_statements( symbol_table.lookup_ref("java::LocalLambdas.test:()V").value); - const std::string function_prefix = "java::LocalLambdas.test:()V"; + const std::string function_prefix_regex_str = + "java::LocalLambdas\\.test:\\(\\)V"; THEN( "The local variable should be assigned a temp object implementing " "SimpleLambda") { lambda_assignment_test_datat test_data; - test_data.lambda_variable_id = function_prefix + "::11::simpleLambda"; + test_data.lambda_variable_id = + std::regex(function_prefix_regex_str + "::\\d+::simpleLambda$"); test_data.lambda_interface = "java::SimpleLambda"; test_data.lambda_interface_method_descriptor = ".Execute:()V"; @@ -130,7 +132,8 @@ SCENARIO( "parameter interface implementor") { lambda_assignment_test_datat test_data; - test_data.lambda_variable_id = function_prefix + "::35::paramLambda"; + test_data.lambda_variable_id = + std::regex(function_prefix_regex_str + "::\\d+::paramLambda$"); test_data.lambda_interface = "java::ParameterLambda"; test_data.lambda_interface_method_descriptor = From d2ed92b8650bd83709bb2ed9ac3b4004998d275f Mon Sep 17 00:00:00 2001 From: thk123 Date: Tue, 20 Mar 2018 12:28:14 +0000 Subject: [PATCH 12/30] Adding test for lambda taking array parameters --- .../convert_invoke_dynamic.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp b/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp index 67eadec1f28..fbf75f608b1 100644 --- a/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp +++ b/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp @@ -143,6 +143,23 @@ SCENARIO( "Object;LDummyGeneric;)V"; validate_lamdba_assignement(symbol_table, instructions, test_data); } + THEN( + "The local variable should be assigned a non-null pointer to a " + "array parameter interface implementor") + { + lambda_assignment_test_datat test_data; + + test_data.lambda_variable_id = + std::regex(function_prefix_regex_str + "::\\d+::arrayParamLambda$"); + + test_data.lambda_interface = "java::ArrayParameterLambda"; + test_data.lambda_interface_method_descriptor = + ".Execute:([I[Ljava/lang/Object;[LDummyGeneric;)V"; + test_data.lambda_function_id = + "java::LocalLambdas.lambda$test$2:" + "([I[Ljava/lang/Object;[LDummyGeneric;)V"; + validate_lamdba_assignement(symbol_table, instructions, test_data); + } } } }); From db6756e5cf5b43f892f1dbbb06e624f0ddc11e71 Mon Sep 17 00:00:00 2001 From: thk123 Date: Tue, 20 Mar 2018 14:28:20 +0000 Subject: [PATCH 13/30] Adding checks for parameters of the called function --- .../convert_invoke_dynamic.cpp | 47 ++++++++++++++++++- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp b/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp index fbf75f608b1..d796430ee73 100644 --- a/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp +++ b/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp @@ -13,6 +13,7 @@ #include #include #include +#include struct lambda_assignment_test_datat { @@ -20,6 +21,8 @@ struct lambda_assignment_test_datat irep_idt lambda_interface; std::string lambda_interface_method_descriptor; irep_idt lambda_function_id; + + std::vector expected_params; }; void validate_lamdba_assignement( @@ -84,8 +87,29 @@ void validate_lamdba_assignement( const std::vector &assignments = require_goto_statements::get_all_statements(method_symbol.value); - require_goto_statements::require_function_call( - assignments, test_data.lambda_function_id); + code_function_callt function_call = + require_goto_statements::require_function_call( + assignments, test_data.lambda_function_id); + + std::string variable_prefix = id2string(method_identifier) + "::"; + // replace all symbol exprs with a prefixed symbol expr + std::vector expected_args = test_data.expected_params; + for(exprt &arg : expected_args) + { + for(auto it = arg.depth_begin(); it != arg.depth_end(); ++it) + { + if(it->id() == ID_symbol) + { + symbol_exprt &symbol_expr = to_symbol_expr(it.mutate()); + const irep_idt simple_id = symbol_expr.get_identifier(); + symbol_expr.set_identifier(variable_prefix + id2string(simple_id)); + } + } + } + + REQUIRE_THAT( + function_call.arguments(), + Catch::Matchers::Vector::EqualsMatcher{expected_args}); } } @@ -125,6 +149,7 @@ SCENARIO( test_data.lambda_interface = "java::SimpleLambda"; test_data.lambda_interface_method_descriptor = ".Execute:()V"; test_data.lambda_function_id = "java::LocalLambdas.pretendLambda:()V"; + test_data.expected_params = {}; validate_lamdba_assignement(symbol_table, instructions, test_data); } THEN( @@ -141,6 +166,15 @@ SCENARIO( test_data.lambda_function_id = "java::LocalLambdas.lambda$test$1:(ILjava/lang/" "Object;LDummyGeneric;)V"; + + symbol_exprt integer_param{"primitive", java_int_type()}; + symbol_exprt ref_param{"reference", + java_type_from_string("Ljava/lang/Object;")}; + symbol_exprt generic_param{ + "specalisedGeneric", + java_type_from_string("LDummyGeneric;")}; + test_data.expected_params = {integer_param, ref_param, generic_param}; + validate_lamdba_assignement(symbol_table, instructions, test_data); } THEN( @@ -158,6 +192,15 @@ SCENARIO( test_data.lambda_function_id = "java::LocalLambdas.lambda$test$2:" "([I[Ljava/lang/Object;[LDummyGeneric;)V"; + + symbol_exprt integer_param{"primitive", java_type_from_string("[I")}; + symbol_exprt ref_param{"reference", + java_type_from_string("[Ljava/lang/Object;")}; + symbol_exprt generic_param{ + "specalisedGeneric", + java_type_from_string("[LDummyGeneric;")}; + test_data.expected_params = {integer_param, ref_param, generic_param}; + validate_lamdba_assignement(symbol_table, instructions, test_data); } } From f3ddee6783dffb7a5d9f5d7e5903ed10f4e8a62d Mon Sep 17 00:00:00 2001 From: thk123 Date: Tue, 20 Mar 2018 14:40:24 +0000 Subject: [PATCH 14/30] Adding tests to verify the return of the lambda wrapper method --- .../convert_invoke_dynamic.cpp | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp b/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp index d796430ee73..12d61f51352 100644 --- a/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp +++ b/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp @@ -23,6 +23,7 @@ struct lambda_assignment_test_datat irep_idt lambda_function_id; std::vector expected_params; + bool should_return_value; }; void validate_lamdba_assignement( @@ -110,6 +111,15 @@ void validate_lamdba_assignement( REQUIRE_THAT( function_call.arguments(), Catch::Matchers::Vector::EqualsMatcher{expected_args}); + + if(test_data.should_return_value) + { + require_expr::require_symbol(function_call.lhs()); + } + else + { + REQUIRE(function_call.lhs().is_nil()); + } } } @@ -150,6 +160,7 @@ SCENARIO( test_data.lambda_interface_method_descriptor = ".Execute:()V"; test_data.lambda_function_id = "java::LocalLambdas.pretendLambda:()V"; test_data.expected_params = {}; + test_data.should_return_value = false; validate_lamdba_assignement(symbol_table, instructions, test_data); } THEN( @@ -174,6 +185,7 @@ SCENARIO( "specalisedGeneric", java_type_from_string("LDummyGeneric;")}; test_data.expected_params = {integer_param, ref_param, generic_param}; + test_data.should_return_value = false; validate_lamdba_assignement(symbol_table, instructions, test_data); } @@ -200,7 +212,24 @@ SCENARIO( "specalisedGeneric", java_type_from_string("[LDummyGeneric;")}; test_data.expected_params = {integer_param, ref_param, generic_param}; + test_data.should_return_value = false; + + validate_lamdba_assignement(symbol_table, instructions, test_data); + } + THEN( + "The local variable should be assigned a temp object implementing " + "ReturningLambdaPrimitive") + { + lambda_assignment_test_datat test_data; + + test_data.lambda_variable_id = std::regex( + function_prefix_regex_str + "::\\d+::returnPrimitiveLambda"); + test_data.lambda_interface = "java::ReturningLambdaPrimitive"; + test_data.lambda_interface_method_descriptor = ".Execute:()I"; + test_data.lambda_function_id = "java::LocalLambdas.lambda$test$3:()I"; + test_data.expected_params = {}; + test_data.should_return_value = true; validate_lamdba_assignement(symbol_table, instructions, test_data); } } From 3e0e12e0cde7d416bcd21e73a51a6bd6965705bd Mon Sep 17 00:00:00 2001 From: thk123 Date: Tue, 20 Mar 2018 14:44:07 +0000 Subject: [PATCH 15/30] Adding tests for the other two returning lambdas that don't capture --- .../convert_invoke_dynamic.cpp | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp b/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp index 12d61f51352..e96d04c8e0e 100644 --- a/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp +++ b/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp @@ -232,6 +232,48 @@ SCENARIO( test_data.should_return_value = true; validate_lamdba_assignement(symbol_table, instructions, test_data); } + THEN( + "The local variable should be assigned a temp object implementing " + "ReturningLambdaReference") + { + lambda_assignment_test_datat test_data; + + test_data.lambda_variable_id = std::regex( + function_prefix_regex_str + "::\\d+::returnReferenceLambda"); + + test_data.lambda_interface = "java::ReturningLambdaReference"; + + test_data.lambda_interface_method_descriptor = + ".Execute:()Ljava/lang/Object;"; + + //"java::LocalLambdas.lambda$test$0:()V" + test_data.lambda_function_id = + "java::LocalLambdas.lambda$test$4:()Ljava/lang/Object;"; + test_data.expected_params = {}; + test_data.should_return_value = true; + validate_lamdba_assignement(symbol_table, instructions, test_data); + } + THEN( + "The local variable should be assigned a temp object implementing " + "ReturningLambdaSpecalisedGeneric") + { + lambda_assignment_test_datat test_data; + + test_data.lambda_variable_id = std::regex( + function_prefix_regex_str + + "::\\d+::returningSpecalisedGenericLambda"); + + test_data.lambda_interface = "java::ReturningLambdaSpecalisedGeneric"; + + test_data.lambda_interface_method_descriptor = + ".Execute:()LDummyGeneric;"; + test_data.lambda_function_id = + "java::LocalLambdas.lambda$test$5:()LDummyGeneric;"; + test_data.expected_params = {}; + test_data.should_return_value = true; + validate_lamdba_assignement(symbol_table, instructions, test_data); + } + // TODO[TG-2482]: Tests for local lambdas that capture variables } } }); From 8999cf69f2c8a420971cd6e1d1345b9612801bec Mon Sep 17 00:00:00 2001 From: thk123 Date: Wed, 21 Mar 2018 11:06:53 +0000 Subject: [PATCH 16/30] Added utiltity for getting this member components Add documentation to find pointer assignments for a component --- .../testing-utils/require_goto_statements.cpp | 48 +++++++++++++++++++ unit/testing-utils/require_goto_statements.h | 4 ++ 2 files changed, 52 insertions(+) diff --git a/unit/testing-utils/require_goto_statements.cpp b/unit/testing-utils/require_goto_statements.cpp index 4c752a42e78..ce4d8a60783 100644 --- a/unit/testing-utils/require_goto_statements.cpp +++ b/unit/testing-utils/require_goto_statements.cpp @@ -13,6 +13,7 @@ #include #include #include +#include /// Expand value of a function to include all child codets /// \param function_value: The value of the function (e.g. got by looking up @@ -141,6 +142,53 @@ require_goto_statements::find_struct_component_assignments( return locations; } +/// Find assignment statements that set this->{component_name} +/// \param statements The statements to look through +/// \param component_name The name of the component whose assignments we are +/// looking for. +/// \return A collection of all non-null assignments to this component +/// and, if present, a null assignment. +require_goto_statements::pointer_assignment_locationt +require_goto_statements::find_this_component_assignment( + const std::vector &statements, + const irep_idt &component_name) +{ + pointer_assignment_locationt locations; + + for(const auto &assignment : statements) + { + if(assignment.get_statement() == ID_assign) + { + const code_assignt &code_assign = to_code_assign(assignment); + + if(code_assign.lhs().id() == ID_member) + { + const member_exprt &member_expr = to_member_expr(code_assign.lhs()); + if( + member_expr.get_component_name() == component_name && + member_expr.op().id() == ID_dereference && + member_expr.op().op0().id() == ID_symbol && + has_suffix( + id2string(to_symbol_expr(member_expr.op().op0()).get_identifier()), + "this")) + { + if( + code_assign.rhs() == + null_pointer_exprt(to_pointer_type(code_assign.lhs().type()))) + { + locations.null_assignment = code_assign; + } + else + { + locations.non_null_assignments.push_back(code_assign); + } + } + } + } + } + return locations; +} + /// For a given variable name, gets the assignments to it in the provided /// instructions. /// \param pointer_name: The name of the variable diff --git a/unit/testing-utils/require_goto_statements.h b/unit/testing-utils/require_goto_statements.h index c8100336a5c..4f5228c4b7f 100644 --- a/unit/testing-utils/require_goto_statements.h +++ b/unit/testing-utils/require_goto_statements.h @@ -74,6 +74,10 @@ pointer_assignment_locationt find_struct_component_assignments( const optionalt &superclass_name, const irep_idt &component_name); +pointer_assignment_locationt find_this_component_assignment( + const std::vector &statements, + const irep_idt &component_name); + std::vector get_all_statements(const exprt &function_value); const std::vector From fa27117a466d023ea0a66fa4a66d5bf3ba1e521e Mon Sep 17 00:00:00 2001 From: thk123 Date: Wed, 21 Mar 2018 11:08:30 +0000 Subject: [PATCH 17/30] Introduce tests for lambdas that are member variables --- .../convert_invoke_dynamic.cpp | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp b/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp index e96d04c8e0e..908bfb287c2 100644 --- a/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp +++ b/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp @@ -278,3 +278,62 @@ SCENARIO( } }); } + +void validate_member_variable_lambda_assignment( + const symbol_tablet &symbol_table, + const std::vector &instructions, + const lambda_assignment_test_datat &test_data, + const std::string lambda_variable_id) +{ + const auto lambda_assignment = + require_goto_statements::find_this_component_assignment( + instructions, lambda_variable_id); + + REQUIRE(lambda_assignment.non_null_assignments.size() == 1); + REQUIRE_FALSE(lambda_assignment.null_assignment.has_value()); + + validate_lamdba_assignement( + symbol_table, instructions, test_data, lambda_assignment); +} + +SCENARIO( + "Converting invokedynamic with a member lambda", + "[core]" + "[lamdba][java_bytecode][java_bytecode_convert_method][!mayfail]") +{ + // NOLINTNEXTLINE(whitespace/braces) + run_test_with_compilers([](const std::string &compiler) { + GIVEN( + "A class with a static lambda variables from " + compiler + " compiler.") + { + symbol_tablet symbol_table = load_java_class( + "MemberLambdas", + "./java_bytecode/java_bytecode_parser/lambda_examples/" + compiler + + "_classes/", + "MemberLambdas."); + + WHEN("Inspecting the assignments of the entry function") + { + const std::vector &instructions = + require_goto_statements::get_all_statements( + symbol_table.lookup_ref("java::MemberLambdas.:()V").value); + + const std::string function_prefix_regex_str = "java::MemberLambdas"; + + THEN( + "The local variable should be assigned a temp object implementing " + "SimpleLambda") + { + lambda_assignment_test_datat test_data; + test_data.lambda_interface = "java::SimpleLambda"; + test_data.lambda_interface_method_descriptor = ".Execute:()V"; + test_data.lambda_function_id = "java::MemberLambdas.lambda$new$0:()V"; + test_data.expected_params = {}; + test_data.should_return_value = false; + validate_member_variable_lambda_assignment( + symbol_table, instructions, test_data, "simpleLambda"); + } + } + } + }); +} From 7f843a7b2b83aeb73abdf75ea38e6b7b35d661d0 Mon Sep 17 00:00:00 2001 From: thk123 Date: Wed, 21 Mar 2018 12:18:41 +0000 Subject: [PATCH 18/30] Add natural language explanation of the test's checks --- .../convert_invoke_dynamic.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp b/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp index 908bfb287c2..1796263aa16 100644 --- a/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp +++ b/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp @@ -26,6 +26,23 @@ struct lambda_assignment_test_datat bool should_return_value; }; +/// Verifies for a given function that contains: +/// {lambda_variable_id} = invokedynamic method_handle_index +/// code looks +/// like: +/// tmp_object_symbol = java_new(lambda_tmp_type) +/// {lambda_variable_id} = (cast)tmp_object_symbol +/// and the type lambda_tmp_type: +/// implements {lambda_interface} +/// has method {lambda_interface_method_descriptor} +/// and the method body looks like +/// function_call_exprt( +/// should_return_value ? symbol_exprt : nil_exprt, +/// {lambda_function_id}, +/// {expected_params}) +/// \param symbol_table: The loaded symbol table +/// \param instructions: The instructions of the method that calls invokedynamic +/// \param test_data: The parameters for the test void validate_lamdba_assignement( const symbol_tablet &symbol_table, const std::vector &instructions, From 5f5994b0b538de611b82fa47b9508a5023c055c6 Mon Sep 17 00:00:00 2001 From: thk123 Date: Wed, 21 Mar 2018 14:42:07 +0000 Subject: [PATCH 19/30] Pull out the logic for getting the inital assignment This will allow sharing code for testing members as well as local variables --- .../convert_invoke_dynamic.cpp | 97 +++++++++++-------- 1 file changed, 58 insertions(+), 39 deletions(-) diff --git a/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp b/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp index 1796263aa16..b94054359e8 100644 --- a/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp +++ b/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp @@ -17,7 +17,6 @@ struct lambda_assignment_test_datat { - std::regex lambda_variable_id; irep_idt lambda_interface; std::string lambda_interface_method_descriptor; irep_idt lambda_function_id; @@ -46,15 +45,10 @@ struct lambda_assignment_test_datat void validate_lamdba_assignement( const symbol_tablet &symbol_table, const std::vector &instructions, - const lambda_assignment_test_datat &test_data) + const lambda_assignment_test_datat &test_data, + const require_goto_statements::pointer_assignment_locationt + &lambda_assignment) { - const auto lambda_assignment = - require_goto_statements::find_pointer_assignments( - test_data.lambda_variable_id, instructions); - - REQUIRE(lambda_assignment.non_null_assignments.size() == 1); - REQUIRE_FALSE(lambda_assignment.null_assignment.has_value()); - const typecast_exprt &rhs_value = require_expr::require_typecast( lambda_assignment.non_null_assignments[0].rhs()); @@ -140,6 +134,25 @@ void validate_lamdba_assignement( } } +/// Find the assignment to the lambda and then call validate_lamdba_assignement +/// for full validation. +void validate_local_variable_lambda_assignment( + const symbol_tablet &symbol_table, + const std::vector &instructions, + const lambda_assignment_test_datat &test_data, + const std::regex lambda_variable_id) +{ + const auto lambda_assignment = + require_goto_statements::find_pointer_assignments( + lambda_variable_id, instructions); + + REQUIRE(lambda_assignment.non_null_assignments.size() == 1); + REQUIRE_FALSE(lambda_assignment.null_assignment.has_value()); + + validate_lamdba_assignement( + symbol_table, instructions, test_data, lambda_assignment); +} + SCENARIO( "Converting invokedynamic with a local lambda", "[core]" @@ -170,24 +183,22 @@ SCENARIO( "SimpleLambda") { lambda_assignment_test_datat test_data; - test_data.lambda_variable_id = - std::regex(function_prefix_regex_str + "::\\d+::simpleLambda$"); - test_data.lambda_interface = "java::SimpleLambda"; test_data.lambda_interface_method_descriptor = ".Execute:()V"; test_data.lambda_function_id = "java::LocalLambdas.pretendLambda:()V"; test_data.expected_params = {}; test_data.should_return_value = false; - validate_lamdba_assignement(symbol_table, instructions, test_data); + validate_local_variable_lambda_assignment( + symbol_table, + instructions, + test_data, + std::regex(function_prefix_regex_str + "::\\d+::simpleLambda$")); } THEN( "The local variable should be assigned a non-null pointer to a " "parameter interface implementor") { lambda_assignment_test_datat test_data; - test_data.lambda_variable_id = - std::regex(function_prefix_regex_str + "::\\d+::paramLambda$"); - test_data.lambda_interface = "java::ParameterLambda"; test_data.lambda_interface_method_descriptor = ".Execute:(ILjava/lang/Object;LDummyGeneric;)V"; @@ -204,23 +215,23 @@ SCENARIO( test_data.expected_params = {integer_param, ref_param, generic_param}; test_data.should_return_value = false; - validate_lamdba_assignement(symbol_table, instructions, test_data); + validate_local_variable_lambda_assignment( + symbol_table, + instructions, + test_data, + std::regex(function_prefix_regex_str + "::\\d+::paramLambda$")); } THEN( "The local variable should be assigned a non-null pointer to a " "array parameter interface implementor") { lambda_assignment_test_datat test_data; - - test_data.lambda_variable_id = - std::regex(function_prefix_regex_str + "::\\d+::arrayParamLambda$"); - test_data.lambda_interface = "java::ArrayParameterLambda"; test_data.lambda_interface_method_descriptor = ".Execute:([I[Ljava/lang/Object;[LDummyGeneric;)V"; test_data.lambda_function_id = "java::LocalLambdas.lambda$test$2:" - "([I[Ljava/lang/Object;[LDummyGeneric;)V"; + "[I[Ljava/lang/Object;[LDummyGeneric;)V"; symbol_exprt integer_param{"primitive", java_type_from_string("[I")}; symbol_exprt ref_param{"reference", @@ -231,33 +242,35 @@ SCENARIO( test_data.expected_params = {integer_param, ref_param, generic_param}; test_data.should_return_value = false; - validate_lamdba_assignement(symbol_table, instructions, test_data); + validate_local_variable_lambda_assignment( + symbol_table, + instructions, + test_data, + std::regex( + function_prefix_regex_str + "::\\d+::arrayParamLambda$")); } THEN( "The local variable should be assigned a temp object implementing " "ReturningLambdaPrimitive") { lambda_assignment_test_datat test_data; - - test_data.lambda_variable_id = std::regex( - function_prefix_regex_str + "::\\d+::returnPrimitiveLambda"); - test_data.lambda_interface = "java::ReturningLambdaPrimitive"; test_data.lambda_interface_method_descriptor = ".Execute:()I"; test_data.lambda_function_id = "java::LocalLambdas.lambda$test$3:()I"; test_data.expected_params = {}; test_data.should_return_value = true; - validate_lamdba_assignement(symbol_table, instructions, test_data); + validate_local_variable_lambda_assignment( + symbol_table, + instructions, + test_data, + std::regex( + function_prefix_regex_str + "::\\d+::returnPrimitiveLambda")); } THEN( "The local variable should be assigned a temp object implementing " "ReturningLambdaReference") { lambda_assignment_test_datat test_data; - - test_data.lambda_variable_id = std::regex( - function_prefix_regex_str + "::\\d+::returnReferenceLambda"); - test_data.lambda_interface = "java::ReturningLambdaReference"; test_data.lambda_interface_method_descriptor = @@ -268,18 +281,18 @@ SCENARIO( "java::LocalLambdas.lambda$test$4:()Ljava/lang/Object;"; test_data.expected_params = {}; test_data.should_return_value = true; - validate_lamdba_assignement(symbol_table, instructions, test_data); + validate_local_variable_lambda_assignment( + symbol_table, + instructions, + test_data, + std::regex( + function_prefix_regex_str + "::\\d+::returnReferenceLambda")); } THEN( "The local variable should be assigned a temp object implementing " "ReturningLambdaSpecalisedGeneric") { lambda_assignment_test_datat test_data; - - test_data.lambda_variable_id = std::regex( - function_prefix_regex_str + - "::\\d+::returningSpecalisedGenericLambda"); - test_data.lambda_interface = "java::ReturningLambdaSpecalisedGeneric"; test_data.lambda_interface_method_descriptor = @@ -288,7 +301,13 @@ SCENARIO( "java::LocalLambdas.lambda$test$5:()LDummyGeneric;"; test_data.expected_params = {}; test_data.should_return_value = true; - validate_lamdba_assignement(symbol_table, instructions, test_data); + validate_local_variable_lambda_assignment( + symbol_table, + instructions, + test_data, + std::regex( + function_prefix_regex_str + + "::\\d+::returningSpecalisedGenericLambda")); } // TODO[TG-2482]: Tests for local lambdas that capture variables } From f9adaa65438f78877a5dffd533926bca15d4f62a Mon Sep 17 00:00:00 2001 From: thk123 Date: Thu, 22 Mar 2018 11:16:44 +0000 Subject: [PATCH 20/30] Adding tests for member methods --- .../convert_invoke_dynamic.cpp | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp b/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp index b94054359e8..85e71a7c028 100644 --- a/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp +++ b/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp @@ -369,6 +369,102 @@ SCENARIO( validate_member_variable_lambda_assignment( symbol_table, instructions, test_data, "simpleLambda"); } + THEN( + "The local variable should be assigned a temp object implementing " + "ParameterLambda") + { + lambda_assignment_test_datat test_data; + test_data.lambda_interface = "java::ParameterLambda"; + test_data.lambda_interface_method_descriptor = + ".Execute:(ILjava/lang/Object;LDummyGeneric;)V"; + test_data.lambda_function_id = + "java::MemberLambdas.lambda$new$1:" + "(ILjava/lang/Object;LDummyGeneric;)V"; + + symbol_exprt integer_param{"primitive", java_int_type()}; + symbol_exprt ref_param{"reference", + java_type_from_string("Ljava/lang/Object;")}; + symbol_exprt generic_param{ + "specalisedGeneric", + java_type_from_string("LDummyGeneric;")}; + test_data.expected_params = {integer_param, ref_param, generic_param}; + + test_data.should_return_value = false; + validate_member_variable_lambda_assignment( + symbol_table, instructions, test_data, "paramLambda"); + } + THEN( + "The local variable should be assigned a non-null pointer to a " + "array parameter interface implementor") + { + lambda_assignment_test_datat test_data; + test_data.lambda_interface = "java::ArrayParameterLambda"; + test_data.lambda_interface_method_descriptor = + ".Execute:([I[Ljava/lang/Object;[LDummyGeneric;)V"; + test_data.lambda_function_id = + "java::MemberLambdas.lambda$new$2:" + "([I[Ljava/lang/Object;[LDummyGeneric;)V"; + + symbol_exprt integer_param{"primitive", java_type_from_string("[I")}; + symbol_exprt ref_param{"reference", + java_type_from_string("[Ljava/lang/Object;")}; + symbol_exprt generic_param{ + "specalisedGeneric", + java_type_from_string("[LDummyGeneric;")}; + test_data.expected_params = {integer_param, ref_param, generic_param}; + test_data.should_return_value = false; + + validate_member_variable_lambda_assignment( + symbol_table, instructions, test_data, "arrayParamLambda"); + } + THEN( + "The local variable should be assigned a temp object implementing " + "ReturningLambdaPrimitive") + { + lambda_assignment_test_datat test_data; + test_data.lambda_interface = "java::ReturningLambdaPrimitive"; + test_data.lambda_interface_method_descriptor = ".Execute:()I"; + test_data.lambda_function_id = "java::MemberLambdas.lambda$new$3:()I"; + test_data.expected_params = {}; + test_data.should_return_value = true; + validate_member_variable_lambda_assignment( + symbol_table, instructions, test_data, "returnPrimitiveLambda"); + } + THEN( + "The local variable should be assigned a temp object implementing " + "ReturningLambdaReference") + { + lambda_assignment_test_datat test_data; + test_data.lambda_interface = "java::ReturningLambdaReference"; + test_data.lambda_interface_method_descriptor = + ".Execute:()Ljava/lang/Object;"; + test_data.lambda_function_id = + "java::MemberLambdas.lambda$new$4:()Ljava/lang/Object;"; + test_data.expected_params = {}; + test_data.should_return_value = true; + validate_member_variable_lambda_assignment( + symbol_table, instructions, test_data, "returnReferenceLambda"); + } + THEN( + "The local variable should be assigned a temp object implementing " + "ReturningLambdaSpecalisedGeneric") + { + lambda_assignment_test_datat test_data; + test_data.lambda_interface = "java::ReturningLambdaSpecalisedGeneric"; + test_data.lambda_interface_method_descriptor = + ".Execute:()LDummyGeneric;"; + test_data.lambda_function_id = + "java::MemberLambdas.lambda$new$5:()LDummyGeneric;"; + test_data.expected_params = {}; + test_data.should_return_value = true; + validate_member_variable_lambda_assignment( + symbol_table, + instructions, + test_data, + "returningSpecalisedGenericLambda"); + } + + // TODO[TG-2486]: Tests for member lambdas that capture member variables } } }); From 4201db9be3233171a4a9384b9d1d6ba50fe089ec Mon Sep 17 00:00:00 2001 From: thk123 Date: Thu, 22 Mar 2018 15:37:17 +0000 Subject: [PATCH 21/30] Adding tests for static lambdas --- .../convert_invoke_dynamic.cpp | 160 ++++++++++++++++++ 1 file changed, 160 insertions(+) diff --git a/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp b/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp index 85e71a7c028..091d6cedaa0 100644 --- a/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp +++ b/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp @@ -469,3 +469,163 @@ SCENARIO( } }); } + +void validate_static_member_variable_lambda_assignment( + const symbol_tablet &symbol_table, + const std::vector &instructions, + const lambda_assignment_test_datat &test_data, + const std::string static_field_name) +{ + const auto lambda_assignment = + require_goto_statements::find_pointer_assignments( + static_field_name, instructions); + + REQUIRE(lambda_assignment.non_null_assignments.size() == 1); + REQUIRE_FALSE(lambda_assignment.null_assignment.has_value()); + + validate_lamdba_assignement( + symbol_table, instructions, test_data, lambda_assignment); +} + +SCENARIO( + "Converting invokedynamic with a static member lambda", + "[core]" + "[lamdba][java_bytecode][java_bytecode_convert_method][!mayfail]") +{ + // NOLINTNEXTLINE(whitespace/braces) + run_test_with_compilers([](const std::string &compiler) { + GIVEN( + "A class with a static lambda variables from " + compiler + " compiler.") + { + symbol_tablet symbol_table = load_java_class( + "StaticLambdas", + "./java_bytecode/java_bytecode_parser/lambda_examples/" + compiler + + "_classes/", + "StaticLambdas."); + + WHEN("Inspecting the assignments of the entry function") + { + const std::vector &instructions = + require_goto_statements::get_all_statements( + symbol_table.lookup_ref("java::StaticLambdas.:()V").value); + + const std::string function_prefix_regex_str = "java::StaticLambdas"; + + THEN( + "The local variable should be assigned a temp object implementing " + "SimpleLambda") + { + lambda_assignment_test_datat test_data; + test_data.lambda_interface = "java::SimpleLambda"; + test_data.lambda_interface_method_descriptor = ".Execute:()V"; + test_data.lambda_function_id = + "java::StaticLambdas.lambda$static$0:()V"; + test_data.expected_params = {}; + test_data.should_return_value = false; + validate_static_member_variable_lambda_assignment( + symbol_table, + instructions, + test_data, + function_prefix_regex_str + ".simpleLambda"); + } + THEN( + "The local variable should be assigned a temp object implementing " + "ParameterLambda") + { + lambda_assignment_test_datat test_data; + test_data.lambda_interface = "java::ParameterLambda"; + test_data.lambda_interface_method_descriptor = + ".Execute:(ILjava/lang/Object;LDummyGeneric;)V"; + test_data.lambda_function_id = + "java::StaticLambdas.lambda$static$1:" + "(ILjava/lang/Object;LDummyGeneric;)V"; + + symbol_exprt integer_param{"primitive", java_int_type()}; + symbol_exprt ref_param{"reference", + java_type_from_string("Ljava/lang/Object;")}; + symbol_exprt generic_param{ + "specalisedGeneric", + java_type_from_string("LDummyGeneric;")}; + test_data.expected_params = {integer_param, ref_param, generic_param}; + + test_data.should_return_value = false; + validate_member_variable_lambda_assignment( + symbol_table, instructions, test_data, "paramLambda"); + } + THEN( + "The local variable should be assigned a non-null pointer to a " + "array parameter interface implementor") + { + lambda_assignment_test_datat test_data; + test_data.lambda_interface = "java::ArrayParameterLambda"; + test_data.lambda_interface_method_descriptor = + ".Execute:([I[Ljava/lang/Object;[LDummyGeneric;)V"; + test_data.lambda_function_id = + "java::StaticLambdas.lambda$static$2:" + "([I[Ljava/lang/Object;[LDummyGeneric;)V"; + + symbol_exprt integer_param{"primitive", java_type_from_string("[I")}; + symbol_exprt ref_param{"reference", + java_type_from_string("[Ljava/lang/Object;")}; + symbol_exprt generic_param{ + "specalisedGeneric", + java_type_from_string("[LDummyGeneric;")}; + test_data.expected_params = {integer_param, ref_param, generic_param}; + test_data.should_return_value = false; + + validate_member_variable_lambda_assignment( + symbol_table, instructions, test_data, "arrayParamLambda"); + } + THEN( + "The local variable should be assigned a temp object implementing " + "ReturningLambdaPrimitive") + { + lambda_assignment_test_datat test_data; + test_data.lambda_interface = "java::ReturningLambdaPrimitive"; + test_data.lambda_interface_method_descriptor = ".Execute:()I"; + test_data.lambda_function_id = + "java::StaticLambdas.lambda$static$3:()I"; + test_data.expected_params = {}; + test_data.should_return_value = true; + validate_member_variable_lambda_assignment( + symbol_table, instructions, test_data, "returnPrimitiveLambda"); + } + THEN( + "The local variable should be assigned a temp object implementing " + "ReturningLambdaReference") + { + lambda_assignment_test_datat test_data; + test_data.lambda_interface = "java::ReturningLambdaReference"; + test_data.lambda_interface_method_descriptor = + ".Execute:()Ljava/lang/Object;"; + test_data.lambda_function_id = + "java::StaticLambdas.lambda$static$4:()Ljava/lang/Object;"; + test_data.expected_params = {}; + test_data.should_return_value = true; + validate_member_variable_lambda_assignment( + symbol_table, instructions, test_data, "returnReferenceLambda"); + } + THEN( + "The local variable should be assigned a temp object implementing " + "ReturningLambdaSpecalisedGeneric") + { + lambda_assignment_test_datat test_data; + test_data.lambda_interface = "java::ReturningLambdaSpecalisedGeneric"; + test_data.lambda_interface_method_descriptor = + ".Execute:()LDummyGeneric;"; + test_data.lambda_function_id = + "java::StaticLambdas.lambda$static$5:()LDummyGeneric;"; + test_data.expected_params = {}; + test_data.should_return_value = true; + validate_member_variable_lambda_assignment( + symbol_table, + instructions, + test_data, + "returningSpecalisedGenericLambda"); + } + + // TODO[TG-2486]: Tests for member lambdas that capture member variables + } + } + }); +} From ac337613805152bba58b8298467ab8f75d092e90 Mon Sep 17 00:00:00 2001 From: thk123 Date: Fri, 23 Mar 2018 11:01:59 +0000 Subject: [PATCH 22/30] Use raw strings to avoid unnecessary escaping --- .../convert_invoke_dynamic.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp b/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp index 091d6cedaa0..d65a6962a82 100644 --- a/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp +++ b/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp @@ -176,7 +176,7 @@ SCENARIO( symbol_table.lookup_ref("java::LocalLambdas.test:()V").value); const std::string function_prefix_regex_str = - "java::LocalLambdas\\.test:\\(\\)V"; + R"(java::LocalLambdas\.test:\(\)V)"; THEN( "The local variable should be assigned a temp object implementing " @@ -192,7 +192,7 @@ SCENARIO( symbol_table, instructions, test_data, - std::regex(function_prefix_regex_str + "::\\d+::simpleLambda$")); + std::regex(function_prefix_regex_str + R"(::\d+::simpleLambda$)")); } THEN( "The local variable should be assigned a non-null pointer to a " @@ -219,7 +219,7 @@ SCENARIO( symbol_table, instructions, test_data, - std::regex(function_prefix_regex_str + "::\\d+::paramLambda$")); + std::regex(function_prefix_regex_str + R"(::\d+::paramLambda$)")); } THEN( "The local variable should be assigned a non-null pointer to a " @@ -247,7 +247,7 @@ SCENARIO( instructions, test_data, std::regex( - function_prefix_regex_str + "::\\d+::arrayParamLambda$")); + function_prefix_regex_str + R"(::\d+::arrayParamLambda$)")); } THEN( "The local variable should be assigned a temp object implementing " @@ -264,7 +264,7 @@ SCENARIO( instructions, test_data, std::regex( - function_prefix_regex_str + "::\\d+::returnPrimitiveLambda")); + function_prefix_regex_str + R"(::\d+::returnPrimitiveLambda$)")); } THEN( "The local variable should be assigned a temp object implementing " @@ -286,7 +286,7 @@ SCENARIO( instructions, test_data, std::regex( - function_prefix_regex_str + "::\\d+::returnReferenceLambda")); + function_prefix_regex_str + R"(::\d+::returnReferenceLambda$)")); } THEN( "The local variable should be assigned a temp object implementing " @@ -307,7 +307,7 @@ SCENARIO( test_data, std::regex( function_prefix_regex_str + - "::\\d+::returningSpecalisedGenericLambda")); + R"(::\d+::returningSpecalisedGenericLambda$)")); } // TODO[TG-2482]: Tests for local lambdas that capture variables } From d7356be2027f84dd694970bd0c0687dd49259215 Mon Sep 17 00:00:00 2001 From: thk123 Date: Fri, 23 Mar 2018 11:15:21 +0000 Subject: [PATCH 23/30] Modified behaviour to find function calls --- .../convert_invoke_dynamic.cpp | 8 +++++-- .../testing-utils/require_goto_statements.cpp | 14 +++++-------- unit/testing-utils/require_goto_statements.h | 21 +------------------ 3 files changed, 12 insertions(+), 31 deletions(-) diff --git a/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp b/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp index d65a6962a82..9742f9b48ec 100644 --- a/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp +++ b/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp @@ -99,10 +99,14 @@ void validate_lamdba_assignement( const std::vector &assignments = require_goto_statements::get_all_statements(method_symbol.value); - code_function_callt function_call = - require_goto_statements::require_function_call( + std::vector function_calls = + require_goto_statements::find_function_calls( assignments, test_data.lambda_function_id); + INFO("Looking for function call of " << test_data.lambda_function_id); + REQUIRE(function_calls.size() == 1); + const code_function_callt &function_call = function_calls[0]; + std::string variable_prefix = id2string(method_identifier) + "::"; // replace all symbol exprs with a prefixed symbol expr std::vector expected_args = test_data.expected_params; diff --git a/unit/testing-utils/require_goto_statements.cpp b/unit/testing-utils/require_goto_statements.cpp index ce4d8a60783..2b265c99736 100644 --- a/unit/testing-utils/require_goto_statements.cpp +++ b/unit/testing-utils/require_goto_statements.cpp @@ -478,16 +478,12 @@ require_goto_statements::require_entry_point_argument_assignment( /// \param statements: The collection of statements to inspect /// \param function_call_identifier: The symbol identifier of the function /// that should have been called -/// \return The first code_function_callt to the relevant function or throws a -/// no_matching_function_callt if no call is found -code_function_callt require_goto_statements::require_function_call( +/// \return All calls to the matching function inside the statements +std::vector require_goto_statements::find_function_calls( const std::vector &statements, const irep_idt &function_call_identifier) { - // TODO: Would appreciate some review comments on whether it makes the most sense: - // - return a vector of matching code_function_calls - // - return an optionalt with the first matching call - // - return a code_function_callt throwing an exception if none found + std::vector function_calls; for(const codet &statement : statements) { if(statement.get_statement() == ID_function_call) @@ -501,10 +497,10 @@ code_function_callt require_goto_statements::require_function_call( to_symbol_expr(function_call.function()).get_identifier() == function_call_identifier) { - return function_call; + function_calls.push_back(function_call); } } } } - throw no_matching_function_callt(function_call_identifier); + return function_calls; } diff --git a/unit/testing-utils/require_goto_statements.h b/unit/testing-utils/require_goto_statements.h index 4f5228c4b7f..c8bc8ca40ff 100644 --- a/unit/testing-utils/require_goto_statements.h +++ b/unit/testing-utils/require_goto_statements.h @@ -49,25 +49,6 @@ class no_decl_found_exceptiont : public std::exception std::string _varname; }; -class no_matching_function_callt : public std::exception -{ -public: - explicit no_matching_function_callt(const irep_idt &function_identifier) - : function_identifier(function_identifier) - { - } - - virtual const char *what() const throw() - { - std::ostringstream stringStream; - stringStream << "Failed to find function call for: " << function_identifier; - return stringStream.str().c_str(); - } - -private: - irep_idt function_identifier; -}; - pointer_assignment_locationt find_struct_component_assignments( const std::vector &statements, const irep_idt &structure_name, @@ -115,7 +96,7 @@ const irep_idt &require_entry_point_argument_assignment( const irep_idt &argument_name, const std::vector &entry_point_statements); -code_function_callt require_function_call( +std::vector find_function_calls( const std::vector &statements, const irep_idt &function_call_identifier); } From e27151b454b6f226c448507c6f874a0235a92bbc Mon Sep 17 00:00:00 2001 From: thk123 Date: Mon, 26 Mar 2018 15:42:21 +0100 Subject: [PATCH 24/30] Correcting typo in the scenario name --- .../java_bytecode_convert_method/convert_invoke_dynamic.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp b/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp index 9742f9b48ec..59f6452904e 100644 --- a/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp +++ b/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp @@ -160,7 +160,7 @@ void validate_local_variable_lambda_assignment( SCENARIO( "Converting invokedynamic with a local lambda", "[core]" - "[lamdba][java_bytecode][java_bytecode_convert_method][!mayfail]") + "[lambdas][java_bytecode][java_bytecode_convert_method][!mayfail]") { // NOLINTNEXTLINE(whitespace/braces) run_test_with_compilers([](const std::string &compiler) { From 28bfc37eb4a85c41a708634304d1dd0772841ec0 Mon Sep 17 00:00:00 2001 From: thk123 Date: Mon, 26 Mar 2018 15:43:00 +0100 Subject: [PATCH 25/30] Amending path to reflect new location --- .../ci_lazy_methods/lazy_load_lambdas.cpp | 15 ++++++++++----- .../convert_invoke_dynamic.cpp | 12 ++++++------ 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/unit/java_bytecode/ci_lazy_methods/lazy_load_lambdas.cpp b/unit/java_bytecode/ci_lazy_methods/lazy_load_lambdas.cpp index e83fa65af09..3c44a323eee 100644 --- a/unit/java_bytecode/ci_lazy_methods/lazy_load_lambdas.cpp +++ b/unit/java_bytecode/ci_lazy_methods/lazy_load_lambdas.cpp @@ -18,7 +18,8 @@ SCENARIO( { const symbol_tablet symbol_table = load_java_class_lazy( "LocalLambdas", - "./java_bytecode/java_bytecode_parse_lambdas/lambda_examples/", + "./java_bytecode/java_bytecode_parse_lambdas/lambda_examples/" + "openjdk_8_classes", "LocalLambdas.test"); THEN("Then the lambdas should be loaded") @@ -68,7 +69,8 @@ SCENARIO( { const symbol_tablet symbol_table = load_java_class_lazy( "MemberLambdas", - "./java_bytecode/java_bytecode_parse_lambdas/lambda_examples/", + "./java_bytecode/java_bytecode_parse_lambdas/lambda_examples/" + "openjdk_8_classes", "MemberLambdas.test"); THEN("Then the lambdas should be loaded") @@ -117,7 +119,8 @@ SCENARIO( { const symbol_tablet symbol_table = load_java_class_lazy( "StaticLambdas", - "./java_bytecode/java_bytecode_parse_lambdas/lambda_examples/", + "./java_bytecode/java_bytecode_parse_lambdas/lambda_examples/" + "openjdk_8_classes", "StaticLambdas.test"); THEN("Then the lambdas should be loaded") @@ -166,7 +169,8 @@ SCENARIO( { const symbol_tablet symbol_table = load_java_class_lazy( "OuterMemberLambdas$Inner", - "./java_bytecode/java_bytecode_parse_lambdas/lambda_examples/", + "./java_bytecode/java_bytecode_parse_lambdas/lambda_examples/" + "openjdk_8_classes", "OuterMemberLambdas$Inner.test"); THEN("Then the lambdas should be loaded") @@ -192,7 +196,8 @@ SCENARIO( { const symbol_tablet symbol_table = load_java_class_lazy( "ExternalLambdaAccessor", - "./java_bytecode/java_bytecode_parse_lambdas/lambda_examples/", + "./java_bytecode/java_bytecode_parse_lambdas/lambda_examples/" + "openjdk_8_classes", "ExternalLambdaAccessor.test"); THEN("Then the lambdas should be loaded") diff --git a/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp b/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp index 59f6452904e..dc3953bce04 100644 --- a/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp +++ b/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp @@ -169,8 +169,8 @@ SCENARIO( { symbol_tablet symbol_table = load_java_class( "LocalLambdas", - "./java_bytecode/java_bytecode_parser/lambda_examples/" + compiler + - "_classes/", + "./java_bytecode/java_bytecode_parse_lambdas/lambda_examples/" + + compiler + "_classes/", "LocalLambdas.test"); WHEN("Inspecting the assignments of the entry function") @@ -348,8 +348,8 @@ SCENARIO( { symbol_tablet symbol_table = load_java_class( "MemberLambdas", - "./java_bytecode/java_bytecode_parser/lambda_examples/" + compiler + - "_classes/", + "./java_bytecode/java_bytecode_parse_lambdas/lambda_examples/" + + compiler + "_classes/", "MemberLambdas."); WHEN("Inspecting the assignments of the entry function") @@ -503,8 +503,8 @@ SCENARIO( { symbol_tablet symbol_table = load_java_class( "StaticLambdas", - "./java_bytecode/java_bytecode_parser/lambda_examples/" + compiler + - "_classes/", + "./java_bytecode/java_bytecode_parse_lambdas/lambda_examples/" + + compiler + "_classes/", "StaticLambdas."); WHEN("Inspecting the assignments of the entry function") From 44a5dcbaa07f3b4078b2d4b5427dc1c856bc104c Mon Sep 17 00:00:00 2001 From: thk123 Date: Mon, 26 Mar 2018 15:43:43 +0100 Subject: [PATCH 26/30] Adding check for inheritance --- .../convert_invoke_dynamic.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp b/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp index dc3953bce04..2fbed084680 100644 --- a/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp +++ b/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp @@ -14,6 +14,7 @@ #include #include #include +#include struct lambda_assignment_test_datat { @@ -85,6 +86,24 @@ void validate_lamdba_assignement( require_type::require_complete_class(lambda_implementor_type_symbol.type); REQUIRE(tmp_lambda_class_type.has_base(test_data.lambda_interface)); + REQUIRE(tmp_lambda_class_type.has_base("java::java.lang.Object")); + + class_hierarchyt class_hierarchy; + class_hierarchy(symbol_table); + + const auto &parents = class_hierarchy.get_parents_trans(tmp_class_identifier); + REQUIRE_THAT( + parents, + Catch::Matchers::Vector::ContainsElementMatcher{ + test_data.lambda_interface}); + + const auto &interface_children = + class_hierarchy.get_children_trans(test_data.lambda_interface); + + REQUIRE_THAT( + interface_children, + Catch::Matchers::Vector::ContainsElementMatcher{ + tmp_class_identifier}); THEN("The function in the class should call the lambda method") { From df895d3f814dfd3268c376dcbbf56021fe1c47b7 Mon Sep 17 00:00:00 2001 From: thk123 Date: Mon, 26 Mar 2018 15:44:15 +0100 Subject: [PATCH 27/30] Temp checkin for checking components --- .../java_bytecode_convert_method/convert_invoke_dynamic.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp b/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp index 2fbed084680..bb8695a6e81 100644 --- a/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp +++ b/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp @@ -105,6 +105,9 @@ void validate_lamdba_assignement( Catch::Matchers::Vector::ContainsElementMatcher{ tmp_class_identifier}); + require_type::require_component(tmp_lambda_class_type, "@java.lang.Object"); + // TODO verify the components of the class have been set correctly + THEN("The function in the class should call the lambda method") { const irep_idt method_identifier = From 54f1c542d02744be8b18612a79603e946e2372c5 Mon Sep 17 00:00:00 2001 From: thk123 Date: Tue, 27 Mar 2018 10:24:19 +0100 Subject: [PATCH 28/30] Adding checks for the super class of the generated class --- .../convert_invoke_dynamic.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp b/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp index bb8695a6e81..1281508189e 100644 --- a/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp +++ b/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp @@ -105,7 +105,20 @@ void validate_lamdba_assignement( Catch::Matchers::Vector::ContainsElementMatcher{ tmp_class_identifier}); - require_type::require_component(tmp_lambda_class_type, "@java.lang.Object"); + const java_class_typet::componentt super_class_component = + require_type::require_component(tmp_lambda_class_type, "@java.lang.Object"); + + const symbol_typet &super_class_type = require_type::require_symbol( + super_class_component.type(), "java::java.lang.Object"); + + const symbolt &base_class_symbol = require_symbol::require_symbol_exists( + symbol_table, super_class_type.get_identifier()); + + REQUIRE(base_class_symbol.is_type); + const class_typet &super_class_type_struct = + require_type::require_incomplete_class(base_class_symbol.type); + + require_type::require_component(super_class_type_struct, "@class_identifier"); // TODO verify the components of the class have been set correctly THEN("The function in the class should call the lambda method") From 397c14ed4fd8c28dcf1aea06843dae89564ec26c Mon Sep 17 00:00:00 2001 From: thk123 Date: Tue, 27 Mar 2018 10:55:27 +0100 Subject: [PATCH 29/30] Correcting typos and adding documentation to unit tests --- .../convert_invoke_dynamic.cpp | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp b/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp index 1281508189e..f7f04e1682f 100644 --- a/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp +++ b/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp @@ -23,7 +23,7 @@ struct lambda_assignment_test_datat irep_idt lambda_function_id; std::vector expected_params; - bool should_return_value; + bool should_return_value = true; }; /// Verifies for a given function that contains: @@ -43,12 +43,12 @@ struct lambda_assignment_test_datat /// \param symbol_table: The loaded symbol table /// \param instructions: The instructions of the method that calls invokedynamic /// \param test_data: The parameters for the test -void validate_lamdba_assignement( +void validate_lambda_assignment( const symbol_tablet &symbol_table, const std::vector &instructions, const lambda_assignment_test_datat &test_data, const require_goto_statements::pointer_assignment_locationt - &lambda_assignment) + &lambda_assignment) { const typecast_exprt &rhs_value = require_expr::require_typecast( lambda_assignment.non_null_assignments[0].rhs()); @@ -188,7 +188,7 @@ void validate_local_variable_lambda_assignment( REQUIRE(lambda_assignment.non_null_assignments.size() == 1); REQUIRE_FALSE(lambda_assignment.null_assignment.has_value()); - validate_lamdba_assignement( + validate_lambda_assignment( symbol_table, instructions, test_data, lambda_assignment); } @@ -261,7 +261,7 @@ SCENARIO( std::regex(function_prefix_regex_str + R"(::\d+::paramLambda$)")); } THEN( - "The local variable should be assigned a non-null pointer to a " + "The local variable should be assigned a non-null pointer to an " "array parameter interface implementor") { lambda_assignment_test_datat test_data; @@ -354,6 +354,8 @@ SCENARIO( }); } +/// Find the assignment to the lambda in the constructor +/// and then call validate_lamdba_assignement for full validation. void validate_member_variable_lambda_assignment( const symbol_tablet &symbol_table, const std::vector &instructions, @@ -367,7 +369,7 @@ void validate_member_variable_lambda_assignment( REQUIRE(lambda_assignment.non_null_assignments.size() == 1); REQUIRE_FALSE(lambda_assignment.null_assignment.has_value()); - validate_lamdba_assignement( + validate_lambda_assignment( symbol_table, instructions, test_data, lambda_assignment); } @@ -433,7 +435,7 @@ SCENARIO( symbol_table, instructions, test_data, "paramLambda"); } THEN( - "The local variable should be assigned a non-null pointer to a " + "The local variable should be assigned a non-null pointer to an " "array parameter interface implementor") { lambda_assignment_test_datat test_data; @@ -509,6 +511,8 @@ SCENARIO( }); } +/// Find the assignment to the lambda in the and then call +/// validate_lamdba_assignement for full validation. void validate_static_member_variable_lambda_assignment( const symbol_tablet &symbol_table, const std::vector &instructions, @@ -522,7 +526,7 @@ void validate_static_member_variable_lambda_assignment( REQUIRE(lambda_assignment.non_null_assignments.size() == 1); REQUIRE_FALSE(lambda_assignment.null_assignment.has_value()); - validate_lamdba_assignement( + validate_lambda_assignment( symbol_table, instructions, test_data, lambda_assignment); } @@ -592,7 +596,7 @@ SCENARIO( symbol_table, instructions, test_data, "paramLambda"); } THEN( - "The local variable should be assigned a non-null pointer to a " + "The local variable should be assigned a non-null pointer to an " "array parameter interface implementor") { lambda_assignment_test_datat test_data; From 31fa0fe5bbe270724aee806e7e21bfc39689988e Mon Sep 17 00:00:00 2001 From: thk123 Date: Tue, 27 Mar 2018 17:17:45 +0100 Subject: [PATCH 30/30] Addressing review comments Cleaned up some logic Removed some redundant tests relating to java.lang.Object Added missing documentation for helper functions Made the validate_lambda_assignment take a single assignment as the calling functions are already checking that there is precisely one) Addressed linting and formatting problems --- .../convert_invoke_dynamic.cpp | 73 +++++++++++++------ 1 file changed, 49 insertions(+), 24 deletions(-) diff --git a/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp b/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp index f7f04e1682f..57233930ac7 100644 --- a/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp +++ b/unit/java_bytecode/java_bytecode_convert_method/convert_invoke_dynamic.cpp @@ -43,15 +43,15 @@ struct lambda_assignment_test_datat /// \param symbol_table: The loaded symbol table /// \param instructions: The instructions of the method that calls invokedynamic /// \param test_data: The parameters for the test +/// \param lambda_assignment: The assignment statement for the lambda method void validate_lambda_assignment( const symbol_tablet &symbol_table, const std::vector &instructions, const lambda_assignment_test_datat &test_data, - const require_goto_statements::pointer_assignment_locationt - &lambda_assignment) + const code_assignt &lambda_assignment) { - const typecast_exprt &rhs_value = require_expr::require_typecast( - lambda_assignment.non_null_assignments[0].rhs()); + const typecast_exprt &rhs_value = + require_expr::require_typecast(lambda_assignment.rhs()); const symbol_exprt &rhs_symbol = require_expr::require_symbol(rhs_value.op0()); @@ -85,8 +85,8 @@ void validate_lambda_assignment( const class_typet &tmp_lambda_class_type = require_type::require_complete_class(lambda_implementor_type_symbol.type); - REQUIRE(tmp_lambda_class_type.has_base(test_data.lambda_interface)); REQUIRE(tmp_lambda_class_type.has_base("java::java.lang.Object")); + REQUIRE(tmp_lambda_class_type.has_base(test_data.lambda_interface)); class_hierarchyt class_hierarchy; class_hierarchy(symbol_table); @@ -94,6 +94,7 @@ void validate_lambda_assignment( const auto &parents = class_hierarchy.get_parents_trans(tmp_class_identifier); REQUIRE_THAT( parents, + // NOLINTNEXTLINE(whitespace/braces) Catch::Matchers::Vector::ContainsElementMatcher{ test_data.lambda_interface}); @@ -102,6 +103,7 @@ void validate_lambda_assignment( REQUIRE_THAT( interface_children, + // NOLINTNEXTLINE(whitespace/braces) Catch::Matchers::Vector::ContainsElementMatcher{ tmp_class_identifier}); @@ -111,16 +113,6 @@ void validate_lambda_assignment( const symbol_typet &super_class_type = require_type::require_symbol( super_class_component.type(), "java::java.lang.Object"); - const symbolt &base_class_symbol = require_symbol::require_symbol_exists( - symbol_table, super_class_type.get_identifier()); - - REQUIRE(base_class_symbol.is_type); - const class_typet &super_class_type_struct = - require_type::require_incomplete_class(base_class_symbol.type); - - require_type::require_component(super_class_type_struct, "@class_identifier"); - // TODO verify the components of the class have been set correctly - THEN("The function in the class should call the lambda method") { const irep_idt method_identifier = @@ -175,6 +167,11 @@ void validate_lambda_assignment( /// Find the assignment to the lambda and then call validate_lamdba_assignement /// for full validation. +/// \param symbol_table: The loaded symbol table +/// \param instructions: The instructions of the method that calls invokedynamic +/// \param test_data: The parameters for the test +/// \param lambda_variable_id: A regex matching the name of the variable which +/// stores the lambda void validate_local_variable_lambda_assignment( const symbol_tablet &symbol_table, const std::vector &instructions, @@ -189,7 +186,10 @@ void validate_local_variable_lambda_assignment( REQUIRE_FALSE(lambda_assignment.null_assignment.has_value()); validate_lambda_assignment( - symbol_table, instructions, test_data, lambda_assignment); + symbol_table, + instructions, + test_data, + lambda_assignment.non_null_assignments[0]); } SCENARIO( @@ -248,6 +248,7 @@ SCENARIO( symbol_exprt integer_param{"primitive", java_int_type()}; symbol_exprt ref_param{"reference", java_type_from_string("Ljava/lang/Object;")}; + // NOLINTNEXTLINE(whitespace/braces) symbol_exprt generic_param{ "specalisedGeneric", java_type_from_string("LDummyGeneric;")}; @@ -275,6 +276,8 @@ SCENARIO( symbol_exprt integer_param{"primitive", java_type_from_string("[I")}; symbol_exprt ref_param{"reference", java_type_from_string("[Ljava/lang/Object;")}; + + // NOLINTNEXTLINE(whitespace/braces) symbol_exprt generic_param{ "specalisedGeneric", java_type_from_string("[LDummyGeneric;")}; @@ -315,7 +318,6 @@ SCENARIO( test_data.lambda_interface_method_descriptor = ".Execute:()Ljava/lang/Object;"; - //"java::LocalLambdas.lambda$test$0:()V" test_data.lambda_function_id = "java::LocalLambdas.lambda$test$4:()Ljava/lang/Object;"; test_data.expected_params = {}; @@ -348,7 +350,8 @@ SCENARIO( function_prefix_regex_str + R"(::\d+::returningSpecalisedGenericLambda$)")); } - // TODO[TG-2482]: Tests for local lambdas that capture variables + // TODO(tkiley): Tests for local lambdas that capture + // TODO(tkiley): variables [TG-2482] } } }); @@ -356,6 +359,11 @@ SCENARIO( /// Find the assignment to the lambda in the constructor /// and then call validate_lamdba_assignement for full validation. +/// \param symbol_table: The loaded symbol table +/// \param instructions: The instructions of the method that calls invokedynamic +/// \param test_data: The parameters for the test +/// \param lambda_variable_id: The name of the member variable that stores the +/// lambda void validate_member_variable_lambda_assignment( const symbol_tablet &symbol_table, const std::vector &instructions, @@ -370,7 +378,10 @@ void validate_member_variable_lambda_assignment( REQUIRE_FALSE(lambda_assignment.null_assignment.has_value()); validate_lambda_assignment( - symbol_table, instructions, test_data, lambda_assignment); + symbol_table, + instructions, + test_data, + lambda_assignment.non_null_assignments[0]); } SCENARIO( @@ -423,8 +434,9 @@ SCENARIO( "(ILjava/lang/Object;LDummyGeneric;)V"; symbol_exprt integer_param{"primitive", java_int_type()}; - symbol_exprt ref_param{"reference", + symbol_exprt ref_param{"reference", // NOLINT(whitespace/braces) java_type_from_string("Ljava/lang/Object;")}; + // NOLINTNEXTLINE(whitespace/braces) symbol_exprt generic_param{ "specalisedGeneric", java_type_from_string("LDummyGeneric;")}; @@ -447,8 +459,9 @@ SCENARIO( "([I[Ljava/lang/Object;[LDummyGeneric;)V"; symbol_exprt integer_param{"primitive", java_type_from_string("[I")}; - symbol_exprt ref_param{"reference", + symbol_exprt ref_param{"reference", // NOLINT(whitespace/braces) java_type_from_string("[Ljava/lang/Object;")}; + // NOLINTNEXTLINE(whitespace/braces) symbol_exprt generic_param{ "specalisedGeneric", java_type_from_string("[LDummyGeneric;")}; @@ -505,7 +518,8 @@ SCENARIO( "returningSpecalisedGenericLambda"); } - // TODO[TG-2486]: Tests for member lambdas that capture member variables + // TODO(tkiley): Tests for member lambdas that capture member variables + // TODO(tkiley): [TG-2486] } } }); @@ -513,6 +527,11 @@ SCENARIO( /// Find the assignment to the lambda in the and then call /// validate_lamdba_assignement for full validation. +/// \param symbol_table: The loaded symbol table +/// \param instructions: The instructions of the method that calls invokedynamic +/// \param test_data: The parameters for the test +/// \param static_field_name: The name of the static variable that stores the +/// lambda void validate_static_member_variable_lambda_assignment( const symbol_tablet &symbol_table, const std::vector &instructions, @@ -527,7 +546,10 @@ void validate_static_member_variable_lambda_assignment( REQUIRE_FALSE(lambda_assignment.null_assignment.has_value()); validate_lambda_assignment( - symbol_table, instructions, test_data, lambda_assignment); + symbol_table, + instructions, + test_data, + lambda_assignment.non_null_assignments[0]); } SCENARIO( @@ -586,6 +608,7 @@ SCENARIO( symbol_exprt integer_param{"primitive", java_int_type()}; symbol_exprt ref_param{"reference", java_type_from_string("Ljava/lang/Object;")}; + // NOLINTNEXTLINE(whitespace/braces) symbol_exprt generic_param{ "specalisedGeneric", java_type_from_string("LDummyGeneric;")}; @@ -610,6 +633,7 @@ SCENARIO( symbol_exprt integer_param{"primitive", java_type_from_string("[I")}; symbol_exprt ref_param{"reference", java_type_from_string("[Ljava/lang/Object;")}; + // NOLINTNEXTLINE(whitespace/braces) symbol_exprt generic_param{ "specalisedGeneric", java_type_from_string("[LDummyGeneric;")}; @@ -667,7 +691,8 @@ SCENARIO( "returningSpecalisedGenericLambda"); } - // TODO[TG-2486]: Tests for member lambdas that capture member variables + // TODO(tkiley): Tests for member lambdas that capture member variables + // TODO(tkiley): [TG-2486] } } });