Skip to content

Commit e79a655

Browse files
author
Matthias Güdemann
committed
Initial support for reading BootstrapMethods attribute
1 parent 3e793f5 commit e79a655

File tree

2 files changed

+204
-0
lines changed

2 files changed

+204
-0
lines changed

src/java_bytecode/java_bytecode_parse_tree.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,8 +173,34 @@ class java_bytecode_parse_treet
173173
bool is_abstract=false;
174174
bool is_enum=false;
175175
bool is_public=false, is_protected=false, is_private=false;
176+
bool read_attribute_bootstrapmethods = false;
176177
size_t enum_elements=0;
177178

179+
enum class method_handle_typet
180+
{
181+
BOOTSTRAP_METHOD_HANDLE,
182+
BOOTSTRAP_METHOD_HANDLE_ALT,
183+
LAMBDA_METHOD_HANDLE,
184+
UNKNOWN_HANDLE
185+
};
186+
187+
struct lambda_method_handlet
188+
{
189+
method_handle_typet handle_type;
190+
irep_idt lambda_method_name;
191+
irep_idt interface_type;
192+
irep_idt method_type;
193+
lambda_method_handlet()
194+
: handle_type(method_handle_typet::UNKNOWN_HANDLE)
195+
{
196+
}
197+
};
198+
199+
typedef std::vector<lambda_method_handlet> lambda_method_handlest;
200+
typedef std::map<irep_idt, lambda_method_handlest>
201+
lambda_method_handle_mapt;
202+
lambda_method_handle_mapt lambda_method_handle_map;
203+
178204
typedef std::list<irep_idt> implementst;
179205
implementst implements;
180206
optionalt<std::string> signature;

src/java_bytecode/java_bytecode_parser.cpp

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,14 @@ class java_bytecode_parsert:public parsert
4747
typedef java_bytecode_parse_treet::instructiont instructiont;
4848
typedef java_bytecode_parse_treet::annotationt annotationt;
4949
typedef java_bytecode_parse_treet::annotationst annotationst;
50+
typedef java_bytecode_parse_treet::classt::lambda_method_handlet
51+
lambda_method_handlet;
52+
typedef java_bytecode_parse_treet::classt::method_handle_typet
53+
method_handle_typet;
54+
typedef java_bytecode_parse_treet::classt::lambda_method_handlest
55+
lambda_method_handlest;
56+
typedef java_bytecode_parse_treet::classt::lambda_method_handlest
57+
lambda_method_handle_mapt;
5058

5159
java_bytecode_parse_treet parse_tree;
5260

@@ -126,6 +134,7 @@ class java_bytecode_parsert:public parsert
126134
void get_class_refs();
127135
void get_class_refs_rec(const typet &);
128136
void parse_local_variable_type_table(methodt &method);
137+
void parse_methodhandle(const pool_entryt &, lambda_method_handlet &);
129138

130139
void skip_bytes(std::size_t bytes)
131140
{
@@ -1406,6 +1415,124 @@ void java_bytecode_parsert::rclass_attribute(classt &parsed_class)
14061415
{
14071416
rRuntimeAnnotation_attribute(parsed_class.annotations);
14081417
}
1418+
else if(attribute_name == "BootstrapMethods")
1419+
{
1420+
INVARIANT(
1421+
!parsed_class.read_attribute_bootstrapmethods,
1422+
"only one BootstrapMethods argument is allowed in a class file");
1423+
1424+
// mark as read in parsed class
1425+
parsed_class.read_attribute_bootstrapmethods = true;
1426+
u2 num_bootstrap_methods = read_u2();
1427+
for(size_t i = 0; i < num_bootstrap_methods; i++)
1428+
{
1429+
u2 bootstrap_methodhandle_ref = read_u2();
1430+
const pool_entryt &entry = pool_entry(bootstrap_methodhandle_ref);
1431+
u2 num_bootstrap_arguments = read_u2();
1432+
1433+
lambda_method_handlet handle;
1434+
status() << "INFO: parse BootstrapMethod handle "
1435+
<< num_bootstrap_arguments << " #args"
1436+
<< eom;
1437+
parse_methodhandle(entry, handle);
1438+
if(
1439+
handle.handle_type ==
1440+
method_handle_typet::BOOTSTRAP_METHOD_HANDLE_ALT ||
1441+
handle.handle_type == method_handle_typet::BOOTSTRAP_METHOD_HANDLE)
1442+
{
1443+
// try parsing bootstrap method handle
1444+
if(num_bootstrap_arguments >= 3)
1445+
{
1446+
// each entry contains a MethodHandle structure
1447+
// u2 tag
1448+
// u2 reference kind which must be in the range from 1 to 9
1449+
// u2 reference index into the constant pool
1450+
//
1451+
// reference kinds use the following
1452+
// 1 to 4 must point to a CONSTANT_Fieldref structure
1453+
// 5 or 8 must point to a CONSTANT_Methodref structure
1454+
// 6 or 7 must point to a CONSTANT_Methodref or
1455+
// CONSTANT_InterfaceMethodref structure, if the class file version
1456+
// number is 52.0 or above, to a CONSTANT_Methodref only in the case
1457+
// of less than 52.0
1458+
// 9 must point to a CONSTANT_InterfaceMethodref structure
1459+
1460+
// the index must point to a CONSTANT_String
1461+
// CONSTANT_Class
1462+
// CONSTANT_Integer
1463+
// CONSTANT_Long
1464+
// CONSTANT_Float
1465+
// CONSTANT_Double
1466+
// CONSTANT_MethodHandle
1467+
// CONSTANT_MethodType
1468+
u2 arg_index1 = read_u2();
1469+
u2 arg_index2 = read_u2();
1470+
u2 arg_index3 = read_u2();
1471+
1472+
// skip rest
1473+
for(size_t i = 3; i < num_bootstrap_arguments; i++)
1474+
read_u2();
1475+
1476+
const pool_entryt &arg1 = pool_entry(arg_index1);
1477+
const pool_entryt &arg2 = pool_entry(arg_index2);
1478+
const pool_entryt &arg3 = pool_entry(arg_index3);
1479+
1480+
if(!(arg1.tag == CONSTANT_MethodType &&
1481+
arg2.tag == CONSTANT_MethodHandle &&
1482+
arg3.tag == CONSTANT_MethodType))
1483+
return;
1484+
1485+
lambda_method_handlet real_handle;
1486+
status() << "INFO: parse lambda handle" << eom;
1487+
const pool_entryt &lambda_entry = pool_entry(arg_index2);
1488+
parse_methodhandle(lambda_entry, real_handle);
1489+
if(
1490+
real_handle.handle_type !=
1491+
method_handle_typet::LAMBDA_METHOD_HANDLE)
1492+
{
1493+
lambda_method_handlet empty_handle;
1494+
parsed_class.lambda_method_handle_map[parsed_class.name].push_back(
1495+
empty_handle);
1496+
error() << "ERROR: could not parse lambda function method handle"
1497+
<< eom;
1498+
}
1499+
else
1500+
{
1501+
real_handle.interface_type = pool_entry(arg1.ref1).s;
1502+
real_handle.method_type = pool_entry(arg3.ref1).s;
1503+
parsed_class.lambda_method_handle_map[parsed_class.name].push_back(
1504+
real_handle);
1505+
status()
1506+
<< "lambda function reference "
1507+
<< id2string(real_handle.lambda_method_name) << " in class \""
1508+
<< parsed_class.name << "\""
1509+
<< "\n interface type is "
1510+
<< id2string(real_handle.interface_type = pool_entry(arg1.ref1).s)
1511+
<< "\n method type is "
1512+
<< id2string(real_handle.interface_type = pool_entry(arg3.ref1).s)
1513+
<< eom;
1514+
}
1515+
}
1516+
else
1517+
{
1518+
// skip arguments here
1519+
for(size_t i = 0; i < num_bootstrap_arguments; i++)
1520+
read_u2();
1521+
error() << "ERROR: num_bootstrap_arguments must be 3" << eom;
1522+
}
1523+
}
1524+
else
1525+
{
1526+
lambda_method_handlet empty_handle;
1527+
parsed_class.lambda_method_handle_map[parsed_class.name].push_back(
1528+
empty_handle);
1529+
// skip arguments here
1530+
for(size_t i = 0; i < num_bootstrap_arguments; i++)
1531+
read_u2();
1532+
error() << "ERROR: could not parse BootstrapMethods entry" << eom;
1533+
}
1534+
}
1535+
}
14091536
else
14101537
skip_bytes(attribute_length);
14111538
}
@@ -1534,3 +1661,54 @@ void java_bytecode_parsert::parse_local_variable_type_table(methodt &method)
15341661
"Entry in LocalVariableTypeTable must be present in LVT");
15351662
}
15361663
}
1664+
1665+
/// Read method handle pointed to from constant pool entry at index, return type
1666+
/// of method handle and name if lambda function is found.
1667+
/// \param entry: the constant pool entry of the methodhandle_info structure
1668+
/// \param[out]: the method_handle type of the methodhandle_structure, either
1669+
/// for a recognized bootstrap method, for a lambda function or unknown
1670+
void java_bytecode_parsert::parse_methodhandle(
1671+
const pool_entryt &entry,
1672+
lambda_method_handlet &handle)
1673+
{
1674+
INVARIANT(
1675+
entry.tag == CONSTANT_MethodHandle,
1676+
"constant pool entry must be a MethodHandle");
1677+
const auto &ref_entry = pool_entry(entry.ref2);
1678+
INVARIANT(
1679+
(entry.ref1 > 0 && entry.ref1 < 10),
1680+
"reference kind of Methodhandle must be in the range of 1 to 9");
1681+
1682+
const auto &class_entry = pool_entry(ref_entry.ref1);
1683+
const auto &nameandtype_entry = pool_entry(ref_entry.ref2);
1684+
1685+
const std::string method_name =
1686+
id2string(pool_entry(class_entry.ref1).s) + "."
1687+
+ id2string(pool_entry(nameandtype_entry.ref1).s)
1688+
+ id2string(pool_entry(nameandtype_entry.ref2).s);
1689+
1690+
if(
1691+
method_name ==
1692+
"java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/"
1693+
"MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/"
1694+
"lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/"
1695+
"MethodType;)Ljava/lang/invoke/CallSite;")
1696+
handle.handle_type = method_handle_typet::BOOTSTRAP_METHOD_HANDLE;
1697+
1698+
// names seem to be lambda$$POSTFIX$NUM
1699+
// where $POSTFIX is $FUN for a function name in which the lambda is define
1700+
// "static" when it is a static member of the class
1701+
// "new" when it is a class variable, instantiated in <init>
1702+
if(has_prefix(id2string(pool_entry(nameandtype_entry.ref1).s), "lambda$"))
1703+
{
1704+
handle.lambda_method_name = pool_entry(nameandtype_entry.ref1).s;
1705+
handle.handle_type = method_handle_typet::LAMBDA_METHOD_HANDLE;
1706+
}
1707+
1708+
else if(
1709+
method_name ==
1710+
"java/lang/invoke/LambdaMetafactory.altMetafactory(Ljava/lang/invoke/"
1711+
"MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/"
1712+
"MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;")
1713+
handle.handle_type = method_handle_typet::BOOTSTRAP_METHOD_HANDLE_ALT;
1714+
}

0 commit comments

Comments
 (0)