Skip to content

Commit 76d4014

Browse files
thk123Matthias Güdemann
thk123
authored and
Matthias Güdemann
committed
Adding tests for inner classes that capture outer class variables
1 parent 9b8a73e commit 76d4014

File tree

4 files changed

+158
-0
lines changed

4 files changed

+158
-0
lines changed

unit/java_bytecode/java_bytecode_parser/java_bytecode_parse_lambda_method_table.cpp

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -797,3 +797,139 @@ SCENARIO(
797797
}
798798
}
799799
}
800+
SCENARIO(
801+
"lambda_method_handle_map with member lambdas capturing outer class "
802+
"variables",
803+
"[core][java_bytecode][java_bytecode_parse_lambda_method_handle]")
804+
{
805+
null_message_handlert message_handler;
806+
GIVEN(
807+
"An inner class with member variables as lambdas that capture outer "
808+
"variables")
809+
{
810+
java_bytecode_parse_treet parse_tree;
811+
java_bytecode_parse(
812+
"./java_bytecode/java_bytecode_parser/lambda_examples/"
813+
"OuterMemberLambdas$Inner.class",
814+
parse_tree,
815+
message_handler);
816+
WHEN("Parsing that class")
817+
{
818+
REQUIRE(parse_tree.loading_successful);
819+
const java_bytecode_parse_treet::classt parsed_class =
820+
parse_tree.parsed_class;
821+
REQUIRE(parsed_class.attribute_bootstrapmethods_read);
822+
REQUIRE(parsed_class.lambda_method_handle_map.size() == 3);
823+
824+
// Field ref for getting the outer class
825+
const reference_typet outer_class_reference_type =
826+
java_reference_type(symbol_typet{"java::OuterMemberLambdas"});
827+
const fieldref_exprt outer_fieldref{
828+
outer_class_reference_type, "this$0", "java::OuterMemberLambdas$Inner"};
829+
830+
THEN(
831+
"There should be an entry for the lambda that returns a primitive "
832+
"local variable and the method it references should have an "
833+
"appropriate descriptor")
834+
{
835+
std::string descriptor = "()I";
836+
const lambda_method_handlet &lambda_entry =
837+
require_parse_tree::require_lambda_entry_for_descriptor(
838+
parsed_class, descriptor);
839+
840+
const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name;
841+
842+
const auto lambda_method =
843+
require_parse_tree::require_method(parsed_class, lambda_impl_name);
844+
// Note here the descriptor of the implementation is different - the
845+
// implementation requries the input to be passed in
846+
REQUIRE(id2string(lambda_method.descriptor) == "()I");
847+
REQUIRE_FALSE(lambda_method.is_static);
848+
849+
const fieldref_exprt primitive_fieldref{
850+
java_int_type(), "memberPrimitive", "java::OuterMemberLambdas"};
851+
852+
std::vector<require_parse_tree::expected_instructiont>
853+
expected_instructions{{"aload_0", {}}, // load this of stack
854+
{"getfield", {outer_fieldref}},
855+
{"getfield", {primitive_fieldref}},
856+
{"ireturn", {}}};
857+
858+
require_parse_tree::require_instructions_match_expectation(
859+
expected_instructions, lambda_method.instructions);
860+
}
861+
THEN(
862+
"There should be an entry for the lambda that returns a reference type "
863+
"local variable and the method it references should have an "
864+
"appropriate descriptor")
865+
{
866+
// Since it is a local variable, the corresponding method takes the
867+
// captured variable as an input
868+
std::string descriptor = "()Ljava/lang/Object;";
869+
const lambda_method_handlet &lambda_entry =
870+
require_parse_tree::require_lambda_entry_for_descriptor(
871+
parsed_class, descriptor);
872+
873+
const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name;
874+
875+
const auto lambda_method =
876+
require_parse_tree::require_method(parsed_class, lambda_impl_name);
877+
REQUIRE(id2string(lambda_method.descriptor) == "()Ljava/lang/Object;");
878+
REQUIRE_FALSE(lambda_method.is_static);
879+
880+
const reference_typet dummy_generic_reference_type =
881+
java_reference_type(symbol_typet{"java::java.lang.Object"});
882+
883+
const fieldref_exprt reference_fieldref{dummy_generic_reference_type,
884+
"memberReference",
885+
"java::OuterMemberLambdas"};
886+
887+
std::vector<require_parse_tree::expected_instructiont>
888+
expected_instructions{{"aload_0", {}}, // load this of stack
889+
{"getfield", {outer_fieldref}},
890+
{"getfield", {reference_fieldref}},
891+
{"areturn", {}}};
892+
893+
require_parse_tree::require_instructions_match_expectation(
894+
expected_instructions, lambda_method.instructions);
895+
}
896+
THEN(
897+
"There should be an entry for the lambda that returns a specialised "
898+
"generic type local variable and the method it references should have "
899+
"an appropriate descriptor")
900+
{
901+
// Since it is a local variable, the corresponding method takes the
902+
// captured variable as an input
903+
std::string descriptor = "()LDummyGeneric;";
904+
const lambda_method_handlet &lambda_entry =
905+
require_parse_tree::require_lambda_entry_for_descriptor(
906+
parsed_class, descriptor);
907+
908+
const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name;
909+
910+
const java_bytecode_parse_treet::methodt &lambda_method =
911+
require_parse_tree::require_method(parsed_class, lambda_impl_name);
912+
REQUIRE(id2string(lambda_method.descriptor) == "()LDummyGeneric;");
913+
REQUIRE_FALSE(lambda_method.is_static);
914+
915+
const reference_typet dummy_generic_reference_type =
916+
java_reference_type(symbol_typet{"java::DummyGeneric"});
917+
918+
const fieldref_exprt generic_reference_fieldref{
919+
dummy_generic_reference_type,
920+
"memberSpecalisedGeneric",
921+
"java::OuterMemberLambdas"};
922+
923+
// since just returning the parameter, nothing to put on the stack
924+
std::vector<require_parse_tree::expected_instructiont>
925+
expected_instructions{{"aload_0", {}}, // load this of stack
926+
{"getfield", {outer_fieldref}},
927+
{"getfield", {generic_reference_fieldref}},
928+
{"areturn", {}}};
929+
930+
require_parse_tree::require_instructions_match_expectation(
931+
expected_instructions, lambda_method.instructions);
932+
}
933+
}
934+
}
935+
}
Binary file not shown.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
public class OuterMemberLambdas {
2+
3+
int memberPrimitive;
4+
Object memberReference;
5+
DummyGeneric<Integer> memberSpecalisedGeneric;
6+
7+
public class Inner {
8+
9+
ReturningLambdaPrimitive returnPrimitiveLambdaCapture = () -> { return memberPrimitive; };
10+
ReturningLambdaReference returnReferenceLambdaCapture = () -> { return memberReference; };
11+
ReturningLambdaSpecalisedGeneric returningSpecalisedGenericLambdaCapture = () -> { return memberSpecalisedGeneric; };
12+
13+
14+
public void testMethod() {
15+
16+
returnPrimitiveLambdaCapture.Execute();
17+
returnReferenceLambdaCapture.Execute();
18+
returningSpecalisedGenericLambdaCapture.Execute();
19+
}
20+
}
21+
}
22+

0 commit comments

Comments
 (0)