@@ -610,12 +610,78 @@ static symbol_exprt instantiate_new_object(
610
610
return new_instance_var;
611
611
}
612
612
613
+ // / If \p maybe_boxed_type is a boxed primitive return its unboxing method;
614
+ // / otherwise return empty.
615
+ static optionalt<irep_idt> get_unboxing_method (const typet &maybe_boxed_type)
616
+ {
617
+ const irep_idt &boxed_type_id =
618
+ to_struct_tag_type (maybe_boxed_type.subtype ()).get_identifier ();
619
+ const java_boxed_type_infot *boxed_type_info =
620
+ get_boxed_type_info_by_name (boxed_type_id);
621
+ return boxed_type_info ? boxed_type_info->unboxing_function_name
622
+ : optionalt<irep_idt>{};
623
+ }
624
+
625
+ // / Produce a class_method_descriptor_exprt or symbol_exprt for
626
+ // / \p function_symbol depending on whether virtual dispatch could be required
627
+ // / (i.e., if it is non-static and its 'this' parameter is a non-final type)
628
+ exprt make_function_expr (
629
+ const symbolt &function_symbol,
630
+ const symbol_tablet &symbol_table)
631
+ {
632
+ const auto &method_type = to_java_method_type (function_symbol.type );
633
+ if (!method_type.has_this ())
634
+ return function_symbol.symbol_expr ();
635
+ const irep_idt &declared_on_class_id =
636
+ to_struct_tag_type (method_type.get_this ()->type ().subtype ())
637
+ .get_identifier ();
638
+ const auto &this_symbol = symbol_table.lookup_ref (declared_on_class_id);
639
+ if (to_java_class_type (this_symbol.type ).get_final ())
640
+ return function_symbol.symbol_expr ();
641
+
642
+ // Neither final nor static; make a class_method_descriptor_exprt that will
643
+ // trigger remove_virtual_functions to produce a virtual dispatch table:
644
+
645
+ const std::string &function_name = id2string (function_symbol.name );
646
+ const auto method_name_start_idx = function_name.rfind (' .' );
647
+ const irep_idt mangled_method_name =
648
+ function_name.substr (method_name_start_idx + 1 );
649
+
650
+ return class_method_descriptor_exprt{function_symbol.type ,
651
+ mangled_method_name,
652
+ declared_on_class_id,
653
+ function_symbol.base_name };
654
+ }
655
+
613
656
// / If \p expr needs (un)boxing to satisfy \p required_type, add the required
614
657
// / symbols to \p symbol_table and code to \p code_block, then return an
615
658
// / expression giving the adjusted expression. Otherwise return \p expr
616
659
// / unchanged. \p role is a suggested name prefix for any temporary variable
617
660
// / needed; \p function_id is the id of the function any created code it
618
661
// / added to.
662
+ // /
663
+ // / Regarding the apparent behaviour of the Java compiler / runtime with regard
664
+ // / to adapting generic methods to/from primtitive types:
665
+ // /
666
+ // / When unboxing, it appears to permit widening numeric conversions at the
667
+ // / same time. For example, implementing Consumer<Short> by a function of
668
+ // / type long -> void is possible, as the generated function will look like
669
+ // / impl(Object o) { realfunc(((Number)o).longValue()); }
670
+ // /
671
+ // / On the other hand when boxing to satisfy a generic interface type this is
672
+ // / not permitted: in theory we should be able to implement Producer<Long> by a
673
+ // / function of type () -> int, generating boxing code like
674
+ // / impl() { return Long.valueOf(realfunc()); }
675
+ // /
676
+ // / However it appears there is no way to convey to the lambda metafactory
677
+ // / that a Long is really required rather than an Integer (the obvious
678
+ // / conversion from int), so the compiler forbids this and requires that only
679
+ // / simple boxing is performed.
680
+ // /
681
+ // / Therefore when unboxing we cast to Number first, while when boxing and the
682
+ // / target type is not a specific boxed type (i.e. the target is Object or
683
+ // / Number etc) then we use the primitive type as our cue regarding the boxed
684
+ // / type to produce.
619
685
exprt box_or_unbox_type_if_necessary (
620
686
exprt expr,
621
687
const typet &required_type,
@@ -635,44 +701,40 @@ exprt box_or_unbox_type_if_necessary(
635
701
636
702
// One is a pointer, the other a primitive -- box or unbox as necessary, and
637
703
// check the types are consistent:
638
- if (original_is_pointer)
639
- {
640
- const irep_idt &boxed_type_id =
641
- to_struct_tag_type (original_type.subtype ()).get_identifier ();
642
- const auto *boxed_type_info = get_boxed_type_info_by_name (boxed_type_id);
643
- INVARIANT (
644
- boxed_type_info != nullptr ,
645
- " Only boxed primitive types should participate in a pointer vs."
646
- " primitive type disagreement" );
647
-
648
- symbol_exprt fresh_local = create_and_declare_local (
649
- function_id, role + " _unboxed" , required_type, symbol_table, code_block);
650
- const symbolt &unboxing_function_symbol =
651
- symbol_table.lookup_ref (boxed_type_info->unboxing_function_name );
652
- code_block.add (code_function_callt{
653
- fresh_local, unboxing_function_symbol.symbol_expr (), {expr}});
654
-
655
- return std::move (fresh_local);
656
- }
657
- else
658
- {
659
- const auto *primitive_type_info =
660
- get_java_primitive_type_info (original_type);
661
- INVARIANT (
662
- primitive_type_info != nullptr ,
663
- " A Java non-pointer type involved in a type disagreement should"
664
- " be a primitive" );
665
-
666
- symbol_exprt fresh_local = create_and_declare_local (
667
- function_id, role + " _boxed" , required_type, symbol_table, code_block);
668
- const symbolt &boxed_type_factory_method =
669
- symbol_table.lookup_ref (primitive_type_info->boxed_type_factory_method );
670
-
671
- code_block.add (code_function_callt{
672
- fresh_local, boxed_type_factory_method.symbol_expr (), {expr}});
673
-
674
- return std::move (fresh_local);
675
- }
704
+
705
+ const auto *primitive_type_info = get_java_primitive_type_info (
706
+ original_is_pointer ? required_type : original_type);
707
+ INVARIANT (
708
+ primitive_type_info != nullptr ,
709
+ " A Java non-pointer type involved in a type disagreement should"
710
+ " be a primitive" );
711
+
712
+ const irep_idt fresh_local_name =
713
+ role + (original_is_pointer ? " _unboxed" : " _boxed" );
714
+
715
+ const symbol_exprt fresh_local = create_and_declare_local (
716
+ function_id, fresh_local_name, required_type, symbol_table, code_block);
717
+
718
+ const irep_idt transform_function_id =
719
+ original_is_pointer
720
+ ? get_unboxing_method (original_type) // Use static type if known
721
+ .value_or (primitive_type_info->unboxing_function_name )
722
+ : primitive_type_info->boxed_type_factory_method ;
723
+
724
+ const symbolt &transform_function_symbol =
725
+ symbol_table.lookup_ref (transform_function_id);
726
+
727
+ const typet &transform_function_param_type =
728
+ to_code_type (transform_function_symbol.type ).parameters ()[0 ].type ();
729
+ const exprt cast_expr =
730
+ typecast_exprt::conditional_cast (expr, transform_function_param_type);
731
+
732
+ code_block.add (code_function_callt{
733
+ fresh_local,
734
+ make_function_expr (transform_function_symbol, symbol_table),
735
+ {expr}});
736
+
737
+ return std::move (fresh_local);
676
738
}
677
739
678
740
// / Box or unbox expr as per \ref box_or_unbox_type_if_necessary, then cast the
0 commit comments