Skip to content

Commit 2a3d501

Browse files
committed
Java lambdas: support newinvokespecial lambdas
These encode a lambda that instantiates and constructs an instance of some type without using an intermediate method.
1 parent d4139fb commit 2a3d501

File tree

2 files changed

+92
-2
lines changed

2 files changed

+92
-2
lines changed

jbmc/src/java_bytecode/java_bytecode_parser.cpp

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1905,6 +1905,30 @@ java_bytecode_parsert::parse_method_handle(const method_handle_infot &entry)
19051905
std::replace(class_name.begin(), class_name.end(), '.', '$');
19061906
// replace '/' for '.' (package)
19071907
std::replace(class_name.begin(), class_name.end(), '/', '.');
1908+
1909+
// The following lambda kinds specified in the JVMS (see for example
1910+
// https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html#jvms-5.4.3.5
1911+
// but which aren't actually created by javac. Java has no syntax for a field-
1912+
// refernce like this, and even writing a stereotypical lambda like
1913+
// Producer<FieldType> = instance -> instance.field does not generate this
1914+
// kind of handle, but rather a synthetic method implementing the getfield
1915+
// operation.
1916+
// We don't implement a handle kind that can't be produced yet, but should
1917+
// they ever turn up this is where to fix them.
1918+
1919+
if(
1920+
entry.get_reference_kind() ==
1921+
method_handle_infot::method_handle_kindt::REF_getField ||
1922+
entry.get_reference_kind() ==
1923+
method_handle_infot::method_handle_kindt::REF_putField ||
1924+
entry.get_reference_kind() ==
1925+
method_handle_infot::method_handle_kindt::REF_getStatic ||
1926+
entry.get_reference_kind() ==
1927+
method_handle_infot::method_handle_kindt::REF_putStatic)
1928+
{
1929+
return {};
1930+
}
1931+
19081932
const std::string method_ref =
19091933
class_name + "." + name_and_type.get_name(pool_entry_lambda) + ':' +
19101934
name_and_type.get_descriptor(pool_entry_lambda);
@@ -1914,7 +1938,11 @@ java_bytecode_parsert::parse_method_handle(const method_handle_infot &entry)
19141938
lambda_method_handle.lambda_method_name =
19151939
name_and_type.get_name(pool_entry_lambda);
19161940
lambda_method_handle.lambda_method_ref = method_ref;
1917-
lambda_method_handle.handle_type = method_handle_typet::LAMBDA_METHOD_HANDLE;
1941+
lambda_method_handle.handle_type =
1942+
entry.get_reference_kind() ==
1943+
method_handle_infot::method_handle_kindt::REF_newInvokeSpecial
1944+
? method_handle_typet::LAMBDA_CONSTRUCTOR_HANDLE
1945+
: method_handle_typet::LAMBDA_METHOD_HANDLE;
19181946

19191947
return lambda_method_handle;
19201948
}

jbmc/src/java_bytecode/lambda_synthesis.cpp

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,49 @@ codet invokedynamic_synthetic_constructor(
508508
return std::move(result);
509509
}
510510

511+
static symbol_exprt instantiate_new_object(
512+
const irep_idt &function_id,
513+
const symbolt &lambda_method_symbol,
514+
symbol_table_baset &symbol_table,
515+
code_blockt &result)
516+
{
517+
// We must instantiate the object, then call the requested constructor
518+
const auto &method_type = to_code_type(lambda_method_symbol.type);
519+
INVARIANT(
520+
method_type.get_bool(ID_constructor),
521+
"REF_NewInvokeSpecial lambda must refer to a constructor");
522+
const auto &created_type = method_type.parameters().at(0).type();
523+
irep_idt created_class =
524+
to_struct_tag_type(created_type.subtype()).get_identifier();
525+
526+
// Call static init if it exists:
527+
irep_idt static_init_name = clinit_wrapper_name(created_class);
528+
if(const auto *static_init_symbol = symbol_table.lookup(static_init_name))
529+
{
530+
result.add(code_function_callt{static_init_symbol->symbol_expr(), {}});
531+
}
532+
533+
// Make a local to hold the new instance:
534+
irep_idt new_instance_var_basename = "newinvokespecial_instance";
535+
irep_idt new_instance_var_name =
536+
id2string(function_id) + "::" + id2string(new_instance_var_basename);
537+
auxiliary_symbolt new_instance_var_symbol;
538+
new_instance_var_symbol.name = new_instance_var_name;
539+
new_instance_var_symbol.base_name = new_instance_var_basename;
540+
new_instance_var_symbol.mode = ID_java;
541+
new_instance_var_symbol.type = created_type;
542+
bool add_failed = symbol_table.add(new_instance_var_symbol);
543+
POSTCONDITION(!add_failed);
544+
symbol_exprt new_instance_var = new_instance_var_symbol.symbol_expr();
545+
result.add(code_declt{new_instance_var});
546+
547+
// Instantiate the object:
548+
side_effect_exprt java_new_expr(ID_java_new, created_type, {});
549+
result.add(code_assignt{new_instance_var, java_new_expr});
550+
551+
return new_instance_var;
552+
}
553+
511554
codet invokedynamic_synthetic_method(
512555
const irep_idt &function_id,
513556
symbol_table_baset &symbol_table,
@@ -565,8 +608,20 @@ codet invokedynamic_synthetic_method(
565608
const auto &lambda_method_symbol =
566609
ns.lookup(lambda_method_handle.get_lambda_method_identifier());
567610
const auto handle_type = lambda_method_handle.get_handle_type();
611+
const auto is_constructor_lambda =
612+
handle_type ==
613+
java_class_typet::method_handle_typet::LAMBDA_CONSTRUCTOR_HANDLE;
568614

569-
if(return_type != empty_typet())
615+
if(is_constructor_lambda)
616+
{
617+
auto new_instance_var = instantiate_new_object(
618+
function_id, lambda_method_symbol, symbol_table, result);
619+
620+
// Prepend the newly created object to the lambda arg list:
621+
lambda_method_args.insert(lambda_method_args.begin(), new_instance_var);
622+
}
623+
624+
if(return_type != empty_typet() && !is_constructor_lambda)
570625
{
571626
result.add(code_returnt(side_effect_expr_function_callt(
572627
lambda_method_symbol.symbol_expr(),
@@ -580,5 +635,12 @@ codet invokedynamic_synthetic_method(
580635
lambda_method_symbol.symbol_expr(), lambda_method_args));
581636
}
582637

638+
if(is_constructor_lambda)
639+
{
640+
// Return the newly-created object.
641+
result.add(code_returnt{
642+
typecast_exprt::conditional_cast(lambda_method_args.at(0), return_type)});
643+
}
644+
583645
return std::move(result);
584646
}

0 commit comments

Comments
 (0)