Skip to content

Commit 9b8a73e

Browse files
thk123Matthias Güdemann
thk123
authored and
Matthias Güdemann
committed
Adding tests for lambdas as member variables
1 parent db1f3b5 commit 9b8a73e

File tree

3 files changed

+310
-0
lines changed

3 files changed

+310
-0
lines changed

unit/java_bytecode/java_bytecode_parser/java_bytecode_parse_lambda_method_table.cpp

Lines changed: 272 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,3 +525,275 @@ SCENARIO(
525525
}
526526
}
527527
}
528+
SCENARIO(
529+
"lambda_method_handle_map with member lambdas",
530+
"[core][java_bytecode][java_bytecode_parse_lambda_method_handle]")
531+
{
532+
null_message_handlert message_handler;
533+
GIVEN("A class that has lambdas as member variables")
534+
{
535+
java_bytecode_parse_treet parse_tree;
536+
java_bytecode_parse(
537+
"./java_bytecode/java_bytecode_parser/lambda_examples/"
538+
"MemberLambdas.class",
539+
parse_tree,
540+
message_handler);
541+
WHEN("Parsing that class")
542+
{
543+
REQUIRE(parse_tree.loading_successful);
544+
const java_bytecode_parse_treet::classt parsed_class =
545+
parse_tree.parsed_class;
546+
REQUIRE(parsed_class.attribute_bootstrapmethods_read);
547+
REQUIRE(parsed_class.lambda_method_handle_map.size() == 12);
548+
549+
// Simple lambdas
550+
THEN(
551+
"There should be an entry for the lambda that has no parameters or "
552+
"returns and the method it references should have an appropriate "
553+
"descriptor")
554+
{
555+
const lambda_method_handlet &lambda_entry =
556+
require_parse_tree::require_lambda_entry_for_descriptor(
557+
parsed_class, "()V");
558+
559+
const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name;
560+
561+
const auto lambda_method =
562+
require_parse_tree::require_method(parsed_class, lambda_impl_name);
563+
REQUIRE(id2string(lambda_method.descriptor) == "()V");
564+
}
565+
566+
// Parameter lambdas
567+
THEN(
568+
"There should be an entry for the lambda that takes parameters and the "
569+
"method it references should have an appropriate descriptor")
570+
{
571+
std::string descriptor = "(ILjava/lang/Object;LDummyGeneric;)V";
572+
const lambda_method_handlet &lambda_entry =
573+
require_parse_tree::require_lambda_entry_for_descriptor(
574+
parsed_class, descriptor);
575+
576+
const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name;
577+
578+
const auto lambda_method =
579+
require_parse_tree::require_method(parsed_class, lambda_impl_name);
580+
REQUIRE(id2string(lambda_method.descriptor) == descriptor);
581+
}
582+
THEN(
583+
"There should be an entry for the lambda that takes array parameters "
584+
"and the method it references should have an appropriate descriptor")
585+
{
586+
std::string descriptor = "([I[Ljava/lang/Object;[LDummyGeneric;)V";
587+
const lambda_method_handlet &lambda_entry =
588+
require_parse_tree::require_lambda_entry_for_descriptor(
589+
parsed_class, descriptor);
590+
591+
const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name;
592+
593+
const auto lambda_method =
594+
require_parse_tree::require_method(parsed_class, lambda_impl_name);
595+
REQUIRE(id2string(lambda_method.descriptor) == descriptor);
596+
}
597+
598+
// Return lambdas
599+
THEN(
600+
"There should be an entry for the lambda that returns a primitive and "
601+
"the method it references should have an appropriate descriptor")
602+
{
603+
std::string descriptor = "()I";
604+
const lambda_method_handlet &lambda_entry =
605+
require_parse_tree::require_lambda_entry_for_descriptor(
606+
parsed_class, descriptor);
607+
608+
const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name;
609+
610+
const auto lambda_method =
611+
require_parse_tree::require_method(parsed_class, lambda_impl_name);
612+
REQUIRE(id2string(lambda_method.descriptor) == descriptor);
613+
}
614+
THEN(
615+
"There should be an entry for the lambda that returns a reference type "
616+
"and the method it references should have an appropriate descriptor")
617+
{
618+
std::string descriptor = "()Ljava/lang/Object;";
619+
const lambda_method_handlet &lambda_entry =
620+
require_parse_tree::require_lambda_entry_for_descriptor(
621+
parsed_class, descriptor);
622+
623+
const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name;
624+
625+
const auto lambda_method =
626+
require_parse_tree::require_method(parsed_class, lambda_impl_name);
627+
REQUIRE(id2string(lambda_method.descriptor) == descriptor);
628+
}
629+
THEN(
630+
"There should be an entry for the lambda that returns a specialised "
631+
"generic type and the method it references should have an appropriate "
632+
"descriptor")
633+
{
634+
std::string descriptor = "()LDummyGeneric;";
635+
const lambda_method_handlet &lambda_entry =
636+
require_parse_tree::require_lambda_entry_for_descriptor(
637+
parsed_class, descriptor);
638+
639+
const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name;
640+
641+
const auto lambda_method =
642+
require_parse_tree::require_method(parsed_class, lambda_impl_name);
643+
REQUIRE(id2string(lambda_method.descriptor) == descriptor);
644+
}
645+
646+
// Array returning lambdas
647+
THEN(
648+
"There should be an entry for the lambda that returns an array of "
649+
"primitives and the method it references should have an appropriate "
650+
"descriptor")
651+
{
652+
std::string descriptor = "()[I";
653+
const lambda_method_handlet &lambda_entry =
654+
require_parse_tree::require_lambda_entry_for_descriptor(
655+
parsed_class, descriptor);
656+
657+
const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name;
658+
659+
const auto lambda_method =
660+
require_parse_tree::require_method(parsed_class, lambda_impl_name);
661+
REQUIRE(id2string(lambda_method.descriptor) == descriptor);
662+
}
663+
THEN(
664+
"There should be an entry for the lambda that returns an array of "
665+
"reference types and the method it references should have an "
666+
"appropriate descriptor")
667+
{
668+
std::string descriptor = "()[Ljava/lang/Object;";
669+
const lambda_method_handlet &lambda_entry =
670+
require_parse_tree::require_lambda_entry_for_descriptor(
671+
parsed_class, descriptor);
672+
673+
const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name;
674+
675+
const auto lambda_method =
676+
require_parse_tree::require_method(parsed_class, lambda_impl_name);
677+
REQUIRE(id2string(lambda_method.descriptor) == descriptor);
678+
}
679+
THEN(
680+
"There should be an entry for the lambda that returns an array of "
681+
"specialised generic types and the method it references should have an "
682+
"appropriate descriptor")
683+
{
684+
std::string descriptor = "()[LDummyGeneric;";
685+
const lambda_method_handlet &lambda_entry =
686+
require_parse_tree::require_lambda_entry_for_descriptor(
687+
parsed_class, descriptor);
688+
689+
const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name;
690+
691+
const auto lambda_method =
692+
require_parse_tree::require_method(parsed_class, lambda_impl_name);
693+
REQUIRE(id2string(lambda_method.descriptor) == descriptor);
694+
}
695+
696+
// Capturing lamdbas
697+
THEN(
698+
"There should be an entry for the lambda that returns a primitive "
699+
"local variable and the method it references should have an "
700+
"appropriate descriptor")
701+
{
702+
std::string descriptor = "()I";
703+
const lambda_method_handlet &lambda_entry =
704+
require_parse_tree::require_lambda_entry_for_descriptor(
705+
parsed_class, descriptor, 1);
706+
707+
const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name;
708+
709+
const auto lambda_method =
710+
require_parse_tree::require_method(parsed_class, lambda_impl_name);
711+
// Note here the descriptor of the implementation is different - the
712+
// implementation requries the input to be passed in
713+
REQUIRE(id2string(lambda_method.descriptor) == "()I");
714+
REQUIRE_FALSE(lambda_method.is_static);
715+
716+
const fieldref_exprt primitive_fieldref{
717+
java_int_type(), "memberPrimitive", "java::MemberLambdas"};
718+
719+
std::vector<require_parse_tree::expected_instructiont>
720+
expected_instructions{{"aload_0", {}}, // load this of stack
721+
{"getfield", {primitive_fieldref}},
722+
{"ireturn", {}}};
723+
724+
require_parse_tree::require_instructions_match_expectation(
725+
expected_instructions, lambda_method.instructions);
726+
}
727+
THEN(
728+
"There should be an entry for the lambda that returns a reference type "
729+
"local variable and the method it references should have an "
730+
"appropriate descriptor")
731+
{
732+
// Since it is a local variable, the corresponding method takes the
733+
// captured variable as an input
734+
std::string descriptor = "()Ljava/lang/Object;";
735+
const lambda_method_handlet &lambda_entry =
736+
require_parse_tree::require_lambda_entry_for_descriptor(
737+
parsed_class, descriptor, 1);
738+
739+
const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name;
740+
741+
const auto lambda_method =
742+
require_parse_tree::require_method(parsed_class, lambda_impl_name);
743+
REQUIRE(id2string(lambda_method.descriptor) == "()Ljava/lang/Object;");
744+
REQUIRE_FALSE(lambda_method.is_static);
745+
746+
const reference_typet dummy_generic_reference_type =
747+
java_reference_type(symbol_typet{"java::java.lang.Object"});
748+
749+
const fieldref_exprt reference_fieldref{dummy_generic_reference_type,
750+
"memberReference",
751+
"java::MemberLambdas"};
752+
753+
std::vector<require_parse_tree::expected_instructiont>
754+
expected_instructions{{"aload_0", {}}, // load this of stack
755+
{"getfield", {reference_fieldref}},
756+
{"areturn", {}}};
757+
758+
require_parse_tree::require_instructions_match_expectation(
759+
expected_instructions, lambda_method.instructions);
760+
}
761+
THEN(
762+
"There should be an entry for the lambda that returns a specialised "
763+
"generic type local variable and the method it references should have "
764+
"an appropriate descriptor")
765+
{
766+
// Since it is a local variable, the corresponding method takes the
767+
// captured variable as an input
768+
std::string descriptor = "()LDummyGeneric;";
769+
const lambda_method_handlet &lambda_entry =
770+
require_parse_tree::require_lambda_entry_for_descriptor(
771+
parsed_class, descriptor, 1);
772+
773+
const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name;
774+
775+
const java_bytecode_parse_treet::methodt &lambda_method =
776+
require_parse_tree::require_method(parsed_class, lambda_impl_name);
777+
REQUIRE(id2string(lambda_method.descriptor) == "()LDummyGeneric;");
778+
REQUIRE_FALSE(lambda_method.is_static);
779+
780+
const reference_typet dummy_generic_reference_type =
781+
java_reference_type(symbol_typet{"java::DummyGeneric"});
782+
783+
const fieldref_exprt generic_reference_fieldref{
784+
dummy_generic_reference_type,
785+
"memberSpecalisedGeneric",
786+
"java::MemberLambdas"};
787+
788+
// since just returning the parameter, nothing to put on the stack
789+
std::vector<require_parse_tree::expected_instructiont>
790+
expected_instructions{{"aload_0", {}}, // load this of stack
791+
{"getfield", {generic_reference_fieldref}},
792+
{"areturn", {}}};
793+
794+
require_parse_tree::require_instructions_match_expectation(
795+
expected_instructions, lambda_method.instructions);
796+
}
797+
}
798+
}
799+
}
Binary file not shown.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
public class MemberLambdas {
2+
3+
int memberPrimitive;
4+
Object memberReference;
5+
DummyGeneric<Integer> memberSpecalisedGeneric;
6+
7+
SimpleLambda simpleLambda = () -> { /*NOP*/ };
8+
ParameterLambda paramLambda = (int primitive, Object reference, DummyGeneric<Integer> specalisedGeneric) -> {};
9+
ArrayParameterLambda arrayParamLambda = (int[] primitive, Object[] reference, DummyGeneric<Integer>[] specalisedGeneric) -> {};
10+
ReturningLambdaPrimitive returnPrimitiveLambda = () -> { return 1; };
11+
ReturningLambdaReference returnReferenceLambda = () -> { return null; };
12+
ReturningLambdaSpecalisedGeneric returningSpecalisedGenericLambda = () -> { return null; };
13+
ReturningLambdaPrimitiveArray returnPrimitiveArrayLambda = () -> { return null; };
14+
ReturningLambdaReferenceArray returnReferenceArrayLambda = () -> { return null; };
15+
ReturningLambdaSpecalisedGenericArray returningSpecalisedGenericArrayLambda = () -> { return null; };
16+
17+
ReturningLambdaPrimitive returnPrimitiveLambdaCapture = () -> { return memberPrimitive; };
18+
ReturningLambdaReference returnReferenceLambdaCapture = () -> { return memberReference; };
19+
ReturningLambdaSpecalisedGeneric returningSpecalisedGenericLambdaCapture = () -> { return memberSpecalisedGeneric; };
20+
21+
22+
public void testMethod() {
23+
simpleLambda.Execute();
24+
paramLambda.Execute(4, null, null);
25+
arrayParamLambda.Execute(null, null, null);
26+
returnPrimitiveLambda.Execute();
27+
returnReferenceLambda.Execute();
28+
returningSpecalisedGenericLambda.Execute();
29+
returnPrimitiveArrayLambda.Execute();
30+
returnReferenceArrayLambda.Execute();
31+
returningSpecalisedGenericArrayLambda.Execute();
32+
33+
returnPrimitiveLambdaCapture.Execute();
34+
returnReferenceLambdaCapture.Execute();
35+
returningSpecalisedGenericLambdaCapture.Execute();
36+
}
37+
}
38+

0 commit comments

Comments
 (0)