From e79a65514d38eadf2c20b623106dee3f492b8643 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20G=C3=BCdemann?= Date: Mon, 5 Feb 2018 09:12:51 +0100 Subject: [PATCH 01/18] Initial support for reading BootstrapMethods attribute --- src/java_bytecode/java_bytecode_parse_tree.h | 26 +++ src/java_bytecode/java_bytecode_parser.cpp | 178 +++++++++++++++++++ 2 files changed, 204 insertions(+) diff --git a/src/java_bytecode/java_bytecode_parse_tree.h b/src/java_bytecode/java_bytecode_parse_tree.h index bf5b36c5a2b..734a41b258a 100644 --- a/src/java_bytecode/java_bytecode_parse_tree.h +++ b/src/java_bytecode/java_bytecode_parse_tree.h @@ -173,8 +173,34 @@ class java_bytecode_parse_treet bool is_abstract=false; bool is_enum=false; bool is_public=false, is_protected=false, is_private=false; + bool read_attribute_bootstrapmethods = false; size_t enum_elements=0; + enum class method_handle_typet + { + BOOTSTRAP_METHOD_HANDLE, + BOOTSTRAP_METHOD_HANDLE_ALT, + LAMBDA_METHOD_HANDLE, + UNKNOWN_HANDLE + }; + + struct lambda_method_handlet + { + method_handle_typet handle_type; + irep_idt lambda_method_name; + irep_idt interface_type; + irep_idt method_type; + lambda_method_handlet() + : handle_type(method_handle_typet::UNKNOWN_HANDLE) + { + } + }; + + typedef std::vector lambda_method_handlest; + typedef std::map + lambda_method_handle_mapt; + lambda_method_handle_mapt lambda_method_handle_map; + typedef std::list implementst; implementst implements; optionalt signature; diff --git a/src/java_bytecode/java_bytecode_parser.cpp b/src/java_bytecode/java_bytecode_parser.cpp index 3c45f87c399..ac11f2dc5e1 100644 --- a/src/java_bytecode/java_bytecode_parser.cpp +++ b/src/java_bytecode/java_bytecode_parser.cpp @@ -47,6 +47,14 @@ class java_bytecode_parsert:public parsert typedef java_bytecode_parse_treet::instructiont instructiont; typedef java_bytecode_parse_treet::annotationt annotationt; typedef java_bytecode_parse_treet::annotationst annotationst; + typedef java_bytecode_parse_treet::classt::lambda_method_handlet + lambda_method_handlet; + typedef java_bytecode_parse_treet::classt::method_handle_typet + method_handle_typet; + typedef java_bytecode_parse_treet::classt::lambda_method_handlest + lambda_method_handlest; + typedef java_bytecode_parse_treet::classt::lambda_method_handlest + lambda_method_handle_mapt; java_bytecode_parse_treet parse_tree; @@ -126,6 +134,7 @@ class java_bytecode_parsert:public parsert void get_class_refs(); void get_class_refs_rec(const typet &); void parse_local_variable_type_table(methodt &method); + void parse_methodhandle(const pool_entryt &, lambda_method_handlet &); void skip_bytes(std::size_t bytes) { @@ -1406,6 +1415,124 @@ void java_bytecode_parsert::rclass_attribute(classt &parsed_class) { rRuntimeAnnotation_attribute(parsed_class.annotations); } + else if(attribute_name == "BootstrapMethods") + { + INVARIANT( + !parsed_class.read_attribute_bootstrapmethods, + "only one BootstrapMethods argument is allowed in a class file"); + + // mark as read in parsed class + parsed_class.read_attribute_bootstrapmethods = true; + u2 num_bootstrap_methods = read_u2(); + for(size_t i = 0; i < num_bootstrap_methods; i++) + { + u2 bootstrap_methodhandle_ref = read_u2(); + const pool_entryt &entry = pool_entry(bootstrap_methodhandle_ref); + u2 num_bootstrap_arguments = read_u2(); + + lambda_method_handlet handle; + status() << "INFO: parse BootstrapMethod handle " + << num_bootstrap_arguments << " #args" + << eom; + parse_methodhandle(entry, handle); + if( + handle.handle_type == + method_handle_typet::BOOTSTRAP_METHOD_HANDLE_ALT || + handle.handle_type == method_handle_typet::BOOTSTRAP_METHOD_HANDLE) + { + // try parsing bootstrap method handle + if(num_bootstrap_arguments >= 3) + { + // each entry contains a MethodHandle structure + // u2 tag + // u2 reference kind which must be in the range from 1 to 9 + // u2 reference index into the constant pool + // + // reference kinds use the following + // 1 to 4 must point to a CONSTANT_Fieldref structure + // 5 or 8 must point to a CONSTANT_Methodref structure + // 6 or 7 must point to a CONSTANT_Methodref or + // CONSTANT_InterfaceMethodref structure, if the class file version + // number is 52.0 or above, to a CONSTANT_Methodref only in the case + // of less than 52.0 + // 9 must point to a CONSTANT_InterfaceMethodref structure + + // the index must point to a CONSTANT_String + // CONSTANT_Class + // CONSTANT_Integer + // CONSTANT_Long + // CONSTANT_Float + // CONSTANT_Double + // CONSTANT_MethodHandle + // CONSTANT_MethodType + u2 arg_index1 = read_u2(); + u2 arg_index2 = read_u2(); + u2 arg_index3 = read_u2(); + + // skip rest + for(size_t i = 3; i < num_bootstrap_arguments; i++) + read_u2(); + + const pool_entryt &arg1 = pool_entry(arg_index1); + const pool_entryt &arg2 = pool_entry(arg_index2); + const pool_entryt &arg3 = pool_entry(arg_index3); + + if(!(arg1.tag == CONSTANT_MethodType && + arg2.tag == CONSTANT_MethodHandle && + arg3.tag == CONSTANT_MethodType)) + return; + + lambda_method_handlet real_handle; + status() << "INFO: parse lambda handle" << eom; + const pool_entryt &lambda_entry = pool_entry(arg_index2); + parse_methodhandle(lambda_entry, real_handle); + if( + real_handle.handle_type != + method_handle_typet::LAMBDA_METHOD_HANDLE) + { + lambda_method_handlet empty_handle; + parsed_class.lambda_method_handle_map[parsed_class.name].push_back( + empty_handle); + error() << "ERROR: could not parse lambda function method handle" + << eom; + } + else + { + real_handle.interface_type = pool_entry(arg1.ref1).s; + real_handle.method_type = pool_entry(arg3.ref1).s; + parsed_class.lambda_method_handle_map[parsed_class.name].push_back( + real_handle); + status() + << "lambda function reference " + << id2string(real_handle.lambda_method_name) << " in class \"" + << parsed_class.name << "\"" + << "\n interface type is " + << id2string(real_handle.interface_type = pool_entry(arg1.ref1).s) + << "\n method type is " + << id2string(real_handle.interface_type = pool_entry(arg3.ref1).s) + << eom; + } + } + else + { + // skip arguments here + for(size_t i = 0; i < num_bootstrap_arguments; i++) + read_u2(); + error() << "ERROR: num_bootstrap_arguments must be 3" << eom; + } + } + else + { + lambda_method_handlet empty_handle; + parsed_class.lambda_method_handle_map[parsed_class.name].push_back( + empty_handle); + // skip arguments here + for(size_t i = 0; i < num_bootstrap_arguments; i++) + read_u2(); + error() << "ERROR: could not parse BootstrapMethods entry" << eom; + } + } + } else skip_bytes(attribute_length); } @@ -1534,3 +1661,54 @@ void java_bytecode_parsert::parse_local_variable_type_table(methodt &method) "Entry in LocalVariableTypeTable must be present in LVT"); } } + +/// Read method handle pointed to from constant pool entry at index, return type +/// of method handle and name if lambda function is found. +/// \param entry: the constant pool entry of the methodhandle_info structure +/// \param[out]: the method_handle type of the methodhandle_structure, either +/// for a recognized bootstrap method, for a lambda function or unknown +void java_bytecode_parsert::parse_methodhandle( + const pool_entryt &entry, + lambda_method_handlet &handle) +{ + INVARIANT( + entry.tag == CONSTANT_MethodHandle, + "constant pool entry must be a MethodHandle"); + const auto &ref_entry = pool_entry(entry.ref2); + INVARIANT( + (entry.ref1 > 0 && entry.ref1 < 10), + "reference kind of Methodhandle must be in the range of 1 to 9"); + + const auto &class_entry = pool_entry(ref_entry.ref1); + const auto &nameandtype_entry = pool_entry(ref_entry.ref2); + + const std::string method_name = + id2string(pool_entry(class_entry.ref1).s) + "." + + id2string(pool_entry(nameandtype_entry.ref1).s) + + id2string(pool_entry(nameandtype_entry.ref2).s); + + if( + method_name == + "java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/" + "MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/" + "lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/" + "MethodType;)Ljava/lang/invoke/CallSite;") + handle.handle_type = method_handle_typet::BOOTSTRAP_METHOD_HANDLE; + + // names seem to be lambda$$POSTFIX$NUM + // where $POSTFIX is $FUN for a function name in which the lambda is define + // "static" when it is a static member of the class + // "new" when it is a class variable, instantiated in + if(has_prefix(id2string(pool_entry(nameandtype_entry.ref1).s), "lambda$")) + { + handle.lambda_method_name = pool_entry(nameandtype_entry.ref1).s; + handle.handle_type = method_handle_typet::LAMBDA_METHOD_HANDLE; + } + + else if( + method_name == + "java/lang/invoke/LambdaMetafactory.altMetafactory(Ljava/lang/invoke/" + "MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/" + "MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;") + handle.handle_type = method_handle_typet::BOOTSTRAP_METHOD_HANDLE_ALT; +} From 603aced733eeb52777344a2af63322db9d4fea60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20G=C3=BCdemann?= Date: Wed, 7 Feb 2018 08:51:02 +0100 Subject: [PATCH 02/18] Add regression test for lambda functions Parses some class files with different lambda functions, matches status output of found references to lambdas. --- regression/cbmc-java/lambda1/B.class | Bin 0 -> 1349 bytes regression/cbmc-java/lambda1/B.java | 9 ++ regression/cbmc-java/lambda1/C.class | Bin 0 -> 582 bytes .../cbmc-java/lambda1/CustomLambda.class | Bin 0 -> 299 bytes .../cbmc-java/lambda1/CustomLambda.java | 4 + .../cbmc-java/lambda1/Lambdatest$A.class | Bin 0 -> 401 bytes regression/cbmc-java/lambda1/Lambdatest.class | Bin 0 -> 4959 bytes regression/cbmc-java/lambda1/Lambdatest.java | 84 ++++++++++++++++++ regression/cbmc-java/lambda1/test.desc | 12 +++ 9 files changed, 109 insertions(+) create mode 100644 regression/cbmc-java/lambda1/B.class create mode 100644 regression/cbmc-java/lambda1/B.java create mode 100644 regression/cbmc-java/lambda1/C.class create mode 100644 regression/cbmc-java/lambda1/CustomLambda.class create mode 100644 regression/cbmc-java/lambda1/CustomLambda.java create mode 100644 regression/cbmc-java/lambda1/Lambdatest$A.class create mode 100644 regression/cbmc-java/lambda1/Lambdatest.class create mode 100644 regression/cbmc-java/lambda1/Lambdatest.java create mode 100644 regression/cbmc-java/lambda1/test.desc diff --git a/regression/cbmc-java/lambda1/B.class b/regression/cbmc-java/lambda1/B.class new file mode 100644 index 0000000000000000000000000000000000000000..ee0d07a678c0a5129273c7af562f1ebb91bc452a GIT binary patch literal 1349 zcma)5TT|0O6#lj?O&g-rig@8>v0$l6P!TV!72C?lOqGW+yl>M0fi&sl!pO6KNXPLq zJUBix{wBw>NyY|?j1Sq}bM~C?eCOMflV8W*0j%Ja3>k$ivY3-`3&r$Wws7*}ecl$< ze4b+Nh|9YY?nzi;U`OOY-(;9>?ejyf`krB`JHDlPhHa_q(V@bSZ5z85_k2fZ zXq*>Y4KGb@?W#@N@0faJ*i$3Kbk(p7uf~upEp8K!hTSFXv6f-!Z~b0JceXesGvr&g z#?5W+7{ZR6DR0kkDYaE6q@=5R1W?)#IP4vPn7r5N@}lc;&(Mm?3=5@U5{ttwQ7zCR zPM;xD)y#;j+_rs3)7K3_EnQy{(F!ghuV5641XaN@?kjkJhYY#8ZF{cg@ctX!+q1jw zSQ;xiJfdwihbaY*@kGG|TqN3c3Lli-?Ck3rH5GG(Vls?&168-V=@SJKugM@C3TtzR z;Yz7_Mqe_X;@Mv+ge5BR=*T70oRF*|yx%uzN@hz@%s+-hLRGLNQ-+t840R zF-(jPS=)0AYd7vWmAG}(KQ)v8e;JlMA2_aOgxuh!*)}}AB7xp&en_9Q`eB$kKMnA;G;3`=R z*KnOykzAuP2@zeM{EWmPTMoPuMvw^*Cc>x)L7?9ds5x28MufE*5Tk14FG!9fb?^=8 z4}_h-fKMvMM dmul = x -> x * 3.1415; + + public void set(int x) { + y = x; + } +} diff --git a/regression/cbmc-java/lambda1/C.class b/regression/cbmc-java/lambda1/C.class new file mode 100644 index 0000000000000000000000000000000000000000..710b045719f2213623b02ccf0c895eca27f506c2 GIT binary patch literal 582 zcmZut$x6de6g@XhjH%I1w$9+PtzZ`po0ftE1RB5b?wxPb-CXm6ea^pt9g}C;k z#G5o?P#3x9PUqZ{_wo7q2H+4od1zSEv7U#D4IP_0wsdS07CO?EVV97rH~IwC@&y+E zvE#QJHw2P|%0DwgHA}e|Oq^kuFpQoYyCRHyHgJ?-rYp7;1)(>y#cVJVoy`3EYL{*0 zdtT%Zol_bAMPa@hIzhM}n>3+gkx=?$*u)A7Y}q0de}?nB1T%XWyZodY_$m3NbNsO+ zR|aP-Mq<>OFQ6w*6y!NBB+W}Kac<5R7?-)C_$trud6TN;B+8eo3Y-}M2 zUKqzU45#trK0doggw96WA{7gnxq`vFEsf4soF`j;tNsyQKCCh_7oW0}Nhan>X~RZp zRT73bjSH*N3UjtpMif7siii;0HWK>(H+TS{;U+t65+2ZWzU5p4o^yYfkH?y{(Q)dz HL>IUMPGd}< literal 0 HcmV?d00001 diff --git a/regression/cbmc-java/lambda1/CustomLambda.java b/regression/cbmc-java/lambda1/CustomLambda.java new file mode 100644 index 00000000000..1091fe62c46 --- /dev/null +++ b/regression/cbmc-java/lambda1/CustomLambda.java @@ -0,0 +1,4 @@ +@FunctionalInterface +interface CustomLambda { + boolean is_ok(T t); +} diff --git a/regression/cbmc-java/lambda1/Lambdatest$A.class b/regression/cbmc-java/lambda1/Lambdatest$A.class new file mode 100644 index 0000000000000000000000000000000000000000..71fae8c53b3a761a908f28c6e739fed80a8b05ad GIT binary patch literal 401 zcmYjNO-sW-6r4@d#Kzdf`mGlaIkXk52X9KD6of+1gVOsZT{I<4AW6l)-*yqz$H#vXkgcYi9H(!HVy^E3u_`^RZmG7TnM;fC>PO8R;sM7 z$X_Q}QVj*X?)soN6)?y7OmPqT=|<+F5QW5Y!YM>dXi-2b!zt$i&Sq%wOP+(mO0 zR|1W$4(qIRIOP>g9>icZVFA9cO^F1n1&9A8yCD@?)Qr2ZoPa`6daKfW(m*K&0~r~V<8V+ha5;ep9*g5scszzUo=6~y zC#Cn91RC&^h<>gad+_-L8f9qU=>(eaRDwNs_$32hj-eA@iQ}vIn&5vuhHu~r1K%|8 ztbuPSsN)LeRH2Y9P2}xzIa^j}nyZwZ;)+7+^n}}+wpV5|_J~6BOm3lIJC#ya;hwt1 z(di}ovOSo$3k!o&1t+_ZEsczEN7T+_6n1;76(^S;oUat-oLsRmIG#J{cPaPK))G4W z$$ZgvMnd$3ObG?`7=Kgz*Xv(OODgy>@|(%3OQ$t!&Lf$Lc>HcLo3^+ zbA{}g%F1lEbl#rLb0#reoU`*6>{3p~{$zu*n4{-R4PL+(Xjn?4^)}Qu^-o&EsCt+1 zR}{8uD;_%M*A#YW^R*pOI$xoq-h#E&suj=vAGp5zliI|{d4=J3z^g6-(>(U5v$D!| zrIJ_J<$0yv&sr3o*lJz3tm@JbFS{X^V*sV5T=nzCB9CN=zh(YNLNT)xlcHU>YO+{i zG>xPuCBSN_&HzTz@UL2%F+u=^IAyw z2V~(z?taO_c@g=Mfgj8FMH4^4Pr2|j8U5VEFYwDY1HY2duTAX7fQcjM=hSaZjJTcO za_~D7FN2|H)zo;AXX5wxgMn9s^>Zd(#cKv$rypvj*KImGyOf=Cqz<|v=T+Gl{)nqL zg}Wy>lD-(@9d=EjF;{-1$o#NU>AGu$Mf5@aApLz>P*jm_of7DELffmWc~-<;Lc8YG zXMf!-v};QNg?%Ah^bqpNi011Ghji&`UWJm-N>FzQh3YcPK(63iu=5p$AFrt?QS^qw z9+sLg+p7EMMdPyUI?H0(5eV0nN`bne(4W$;u&q&L%Z4;>O7ka{yo_6A;~BS@)p#

f z++9P+6)qQ-vx8m$G6U^-d(J7A7y$8=+Q`5^c;inOi^~;W4*$^)$_Mb0_mo}8Z9LCg_1!IcD!1ON$DFTr!p`Ssa!z){z&{oC-r|rjdlc^4S`NKD z*xh`xHuA@k#V6}t_9EEFmgOoKr#WWjVhbrU?w19O-yM`;U1WQRqglRS6~C#z>rj0g zM8Y*h*=dkYW1khfhNiwZVLW^t+peMcHOfYCm~Ds4Axgn!v>?WD96e~|!x}I>Obp?E zjualigWM(h=Bba+FbDeg-#{#izasuB$59uv$=$cz#pv_odGB)%umXOmfL|)$m*N?Wa<7G*7-z46yW22xKwaR({fu_XoXp!U(Y6rG;dX!Hg&dVLz!Ei}9tkH!ZA8qd=ValjC1Gz*P( z(&!)!EiK%oRxWMs9WP%7NbLZr9Y#0FB}q#1#q-oqKyfIbxYs?Snx`yA_9=K2rvr+* zrvi$1-iD`+kj^ma9MfpLQ%{}786KM?*dz~3Lcn?*JNx}m?OaFKE1dI+hL_-zuC@+O zHZat*#qKBNNzcRVi*xv7Hb|U<)OPP}YvX0^YYR@)w=HoM zI~MCK@=i3RdCLmCWx0h=yu~+yk9gd7eT$FS6b9F;!Lgw?nNVuP3xn&>;9A_G&cmgLtJL^7x*4u-9lM0yr4Yx(>fjz>qq!E;pR|nb}_s=H^L+urBswRy9C`FYcU}Iw%Ws&LofS(!QXkmbt23D oIc#%p custom = x -> true; + BiFunction add = (x0, y0) -> x0.intValue() + y0; + int z = 10; + + A a; + B b = new B(); + + public Integer g(Float x, Integer y, BiFunction fun) { + return fun.apply(x, y); + } + + public int f(Float x, Integer y, Integer z) { + Integer tmp = add.apply(x, y); + Function mul = (a) -> a * tmp; + return mul.apply(z); + } + + public int i(int x) { + int z = 5; + Function foo = (a) -> a * z; + return foo.apply(x); + } + + public int j(int x) { + Function foo = (a) -> a * z; + return foo.apply(x); + } + + public int k(int x) { + a.x = 10; + + Function foo = (y) -> y * a.x; + return foo.apply(x); + } + + public int l(int x) { + b.y = 10; + Function foo = (y) -> { + int r = y * b.y; + b.set(r); + return r; + }; + b = new B(); + b.y = 14; + return foo.apply(x); + } + + public int m(int x) { + b.y = 10; + Function foo = (y) -> { + int r = y * b.y; + b.y = r; + return r; + }; + return foo.apply(x); + } + + // test static field of different class + public double d(Double x) { + return B.dmul.apply(x); + } + + public int capture2(Float a) { + return add.apply(a, 1); + } + + public boolean custom(Integer i) { + return custom.is_ok(i); + } +} + +class C implements CustomLambda { + public boolean is_ok(Integer i) { + return true; + } +} diff --git a/regression/cbmc-java/lambda1/test.desc b/regression/cbmc-java/lambda1/test.desc new file mode 100644 index 00000000000..9ec5bd91cbd --- /dev/null +++ b/regression/cbmc-java/lambda1/test.desc @@ -0,0 +1,12 @@ +CORE +Lambdatest.class +--verbosity 10 --show-goto-functions +lambda function reference lambda\$new\$0 in class \"Lambdatest\" +lambda function reference lambda\$new\$1 in class \"Lambdatest\" +lambda function reference lambda\$f\$2 in class \"Lambdatest\" +lambda function reference lambda\$i\$3 in class \"Lambdatest\" +lambda function reference lambda\$j\$4 in class \"Lambdatest\" +lambda function reference lambda\$k\$5 in class \"Lambdatest\" +lambda function reference lambda\$l\$6 in class \"Lambdatest\" +lambda function reference lambda\$m\$7 in class \"Lambdatest\" +lambda function reference lambda\$static\$0 in class \"B\" From 01dcda5e62b01cc1ba357c85fbda58b55011c807 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20G=C3=BCdemann?= Date: Fri, 9 Feb 2018 10:57:06 +0100 Subject: [PATCH 03/18] status()->debug() --- src/java_bytecode/java_bytecode_parser.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/java_bytecode/java_bytecode_parser.cpp b/src/java_bytecode/java_bytecode_parser.cpp index ac11f2dc5e1..578089e68b3 100644 --- a/src/java_bytecode/java_bytecode_parser.cpp +++ b/src/java_bytecode/java_bytecode_parser.cpp @@ -1431,7 +1431,7 @@ void java_bytecode_parsert::rclass_attribute(classt &parsed_class) u2 num_bootstrap_arguments = read_u2(); lambda_method_handlet handle; - status() << "INFO: parse BootstrapMethod handle " + debug() << "INFO: parse BootstrapMethod handle " << num_bootstrap_arguments << " #args" << eom; parse_methodhandle(entry, handle); @@ -1483,7 +1483,7 @@ void java_bytecode_parsert::rclass_attribute(classt &parsed_class) return; lambda_method_handlet real_handle; - status() << "INFO: parse lambda handle" << eom; + debug() << "INFO: parse lambda handle" << eom; const pool_entryt &lambda_entry = pool_entry(arg_index2); parse_methodhandle(lambda_entry, real_handle); if( @@ -1502,14 +1502,14 @@ void java_bytecode_parsert::rclass_attribute(classt &parsed_class) real_handle.method_type = pool_entry(arg3.ref1).s; parsed_class.lambda_method_handle_map[parsed_class.name].push_back( real_handle); - status() + debug() << "lambda function reference " << id2string(real_handle.lambda_method_name) << " in class \"" << parsed_class.name << "\"" << "\n interface type is " - << id2string(real_handle.interface_type = pool_entry(arg1.ref1).s) + << id2string(pool_entry(arg1.ref1).s) << "\n method type is " - << id2string(real_handle.interface_type = pool_entry(arg3.ref1).s) + << id2string(pool_entry(arg3.ref1).s) << eom; } } From f765a0de72061508218c004d7068b7ce5ca0d7c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20G=C3=BCdemann?= Date: Fri, 9 Feb 2018 12:15:42 +0100 Subject: [PATCH 04/18] Rename, some more comments --- src/java_bytecode/java_bytecode_parse_tree.h | 5 +- src/java_bytecode/java_bytecode_parser.cpp | 88 ++++++++++++-------- 2 files changed, 55 insertions(+), 38 deletions(-) diff --git a/src/java_bytecode/java_bytecode_parse_tree.h b/src/java_bytecode/java_bytecode_parse_tree.h index 734a41b258a..20a5a43418f 100644 --- a/src/java_bytecode/java_bytecode_parse_tree.h +++ b/src/java_bytecode/java_bytecode_parse_tree.h @@ -173,7 +173,7 @@ class java_bytecode_parse_treet bool is_abstract=false; bool is_enum=false; bool is_public=false, is_protected=false, is_private=false; - bool read_attribute_bootstrapmethods = false; + bool attribute_bootstrapmethods_read = false; size_t enum_elements=0; enum class method_handle_typet @@ -184,8 +184,9 @@ class java_bytecode_parse_treet UNKNOWN_HANDLE }; - struct lambda_method_handlet + class lambda_method_handlet { + public: method_handle_typet handle_type; irep_idt lambda_method_name; irep_idt interface_type; diff --git a/src/java_bytecode/java_bytecode_parser.cpp b/src/java_bytecode/java_bytecode_parser.cpp index 578089e68b3..e3b8db61925 100644 --- a/src/java_bytecode/java_bytecode_parser.cpp +++ b/src/java_bytecode/java_bytecode_parser.cpp @@ -53,7 +53,7 @@ class java_bytecode_parsert:public parsert method_handle_typet; typedef java_bytecode_parse_treet::classt::lambda_method_handlest lambda_method_handlest; - typedef java_bytecode_parse_treet::classt::lambda_method_handlest + typedef java_bytecode_parse_treet::classt::lambda_method_handle_mapt lambda_method_handle_mapt; java_bytecode_parse_treet parse_tree; @@ -134,7 +134,7 @@ class java_bytecode_parsert:public parsert void get_class_refs(); void get_class_refs_rec(const typet &); void parse_local_variable_type_table(methodt &method); - void parse_methodhandle(const pool_entryt &, lambda_method_handlet &); + void parse_method_handle(const pool_entryt &, lambda_method_handlet &); void skip_bytes(std::size_t bytes) { @@ -1418,11 +1418,11 @@ void java_bytecode_parsert::rclass_attribute(classt &parsed_class) else if(attribute_name == "BootstrapMethods") { INVARIANT( - !parsed_class.read_attribute_bootstrapmethods, + !parsed_class.attribute_bootstrapmethods_read, "only one BootstrapMethods argument is allowed in a class file"); // mark as read in parsed class - parsed_class.read_attribute_bootstrapmethods = true; + parsed_class.attribute_bootstrapmethods_read = true; u2 num_bootstrap_methods = read_u2(); for(size_t i = 0; i < num_bootstrap_methods; i++) { @@ -1434,7 +1434,7 @@ void java_bytecode_parsert::rclass_attribute(classt &parsed_class) debug() << "INFO: parse BootstrapMethod handle " << num_bootstrap_arguments << " #args" << eom; - parse_methodhandle(entry, handle); + parse_method_handle(entry, handle); if( handle.handle_type == method_handle_typet::BOOTSTRAP_METHOD_HANDLE_ALT || @@ -1465,29 +1465,47 @@ void java_bytecode_parsert::rclass_attribute(classt &parsed_class) // CONSTANT_Double // CONSTANT_MethodHandle // CONSTANT_MethodType - u2 arg_index1 = read_u2(); - u2 arg_index2 = read_u2(); - u2 arg_index3 = read_u2(); - // skip rest + // We read the three arguments here to see whether they correspond to + // our hypotheses for this being a lambda function entry. + + u2 argument_index1 = read_u2(); + u2 argument_index2 = read_u2(); + u2 argument_index3 = read_u2(); + + // The additional arguments for the altmetafactory call are skipped, + // as they are currently not used. We verify though that they are of + // CONSTANT_Integer type, cases where this does not hold will be + // analyzed further. + bool recognized = true; for(size_t i = 3; i < num_bootstrap_arguments; i++) - read_u2(); + { + u2 skipped_argument = read_u2(); + recognized |= pool_entry(skipped_argument).tag == CONSTANT_Integer; + } + if(!recognized) + { + debug() << "format of BootstrapMethods entry not recognized" + << eom; + return; + } - const pool_entryt &arg1 = pool_entry(arg_index1); - const pool_entryt &arg2 = pool_entry(arg_index2); - const pool_entryt &arg3 = pool_entry(arg_index3); + const pool_entryt &interface_type_argument = + pool_entry(argument_index1); + const pool_entryt &method_handle_argument = + pool_entry(argument_index2); + const pool_entryt &method_type_argument = pool_entry(argument_index3); - if(!(arg1.tag == CONSTANT_MethodType && - arg2.tag == CONSTANT_MethodHandle && - arg3.tag == CONSTANT_MethodType)) + if(!(interface_type_argument.tag == CONSTANT_MethodType && + method_handle_argument.tag == CONSTANT_MethodHandle && + method_type_argument.tag == CONSTANT_MethodType)) return; - lambda_method_handlet real_handle; + lambda_method_handlet lambda_method_handle; debug() << "INFO: parse lambda handle" << eom; - const pool_entryt &lambda_entry = pool_entry(arg_index2); - parse_methodhandle(lambda_entry, real_handle); + parse_method_handle(method_handle_argument, lambda_method_handle); if( - real_handle.handle_type != + lambda_method_handle.handle_type != method_handle_typet::LAMBDA_METHOD_HANDLE) { lambda_method_handlet empty_handle; @@ -1498,27 +1516,27 @@ void java_bytecode_parsert::rclass_attribute(classt &parsed_class) } else { - real_handle.interface_type = pool_entry(arg1.ref1).s; - real_handle.method_type = pool_entry(arg3.ref1).s; + lambda_method_handle.interface_type = pool_entry(interface_type_argument.ref1).s; + lambda_method_handle.method_type = pool_entry(method_type_argument.ref1).s; parsed_class.lambda_method_handle_map[parsed_class.name].push_back( - real_handle); + lambda_method_handle); debug() << "lambda function reference " - << id2string(real_handle.lambda_method_name) << " in class \"" + << id2string(lambda_method_handle.lambda_method_name) << " in class \"" << parsed_class.name << "\"" << "\n interface type is " - << id2string(pool_entry(arg1.ref1).s) + << id2string(pool_entry(interface_type_argument.ref1).s) << "\n method type is " - << id2string(pool_entry(arg3.ref1).s) + << id2string(pool_entry(method_type_argument.ref1).s) << eom; } } else { - // skip arguments here + // skip bytes to align for next entry for(size_t i = 0; i < num_bootstrap_arguments; i++) read_u2(); - error() << "ERROR: num_bootstrap_arguments must be 3" << eom; + error() << "ERROR: num_bootstrap_arguments must be at least 3" << eom; } } else @@ -1526,9 +1544,9 @@ void java_bytecode_parsert::rclass_attribute(classt &parsed_class) lambda_method_handlet empty_handle; parsed_class.lambda_method_handle_map[parsed_class.name].push_back( empty_handle); - // skip arguments here + // skip bytes to align for next entry for(size_t i = 0; i < num_bootstrap_arguments; i++) - read_u2(); + read_u2(); error() << "ERROR: could not parse BootstrapMethods entry" << eom; } } @@ -1665,9 +1683,9 @@ void java_bytecode_parsert::parse_local_variable_type_table(methodt &method) /// Read method handle pointed to from constant pool entry at index, return type /// of method handle and name if lambda function is found. /// \param entry: the constant pool entry of the methodhandle_info structure -/// \param[out]: the method_handle type of the methodhandle_structure, either -/// for a recognized bootstrap method, for a lambda function or unknown -void java_bytecode_parsert::parse_methodhandle( +/// \param[out] handle: the method_handle type of the methodhandle_structure, +/// either for a recognized bootstrap method, for a lambda function or unknown +void java_bytecode_parsert::parse_method_handle( const pool_entryt &entry, lambda_method_handlet &handle) { @@ -1694,17 +1712,15 @@ void java_bytecode_parsert::parse_methodhandle( "lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/" "MethodType;)Ljava/lang/invoke/CallSite;") handle.handle_type = method_handle_typet::BOOTSTRAP_METHOD_HANDLE; - // names seem to be lambda$$POSTFIX$NUM // where $POSTFIX is $FUN for a function name in which the lambda is define // "static" when it is a static member of the class // "new" when it is a class variable, instantiated in - if(has_prefix(id2string(pool_entry(nameandtype_entry.ref1).s), "lambda$")) + else if(has_prefix(id2string(pool_entry(nameandtype_entry.ref1).s), "lambda$")) { handle.lambda_method_name = pool_entry(nameandtype_entry.ref1).s; handle.handle_type = method_handle_typet::LAMBDA_METHOD_HANDLE; } - else if( method_name == "java/lang/invoke/LambdaMetafactory.altMetafactory(Ljava/lang/invoke/" From 6d44836dacd0f43e29a126ec148bc185781ea22a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20G=C3=BCdemann?= Date: Fri, 9 Feb 2018 15:12:55 +0100 Subject: [PATCH 05/18] Use optionalt as return value --- src/java_bytecode/java_bytecode_parse_tree.h | 3 +- src/java_bytecode/java_bytecode_parser.cpp | 125 ++++++++++--------- 2 files changed, 69 insertions(+), 59 deletions(-) diff --git a/src/java_bytecode/java_bytecode_parse_tree.h b/src/java_bytecode/java_bytecode_parse_tree.h index 20a5a43418f..26d6b707f37 100644 --- a/src/java_bytecode/java_bytecode_parse_tree.h +++ b/src/java_bytecode/java_bytecode_parse_tree.h @@ -197,8 +197,7 @@ class java_bytecode_parse_treet } }; - typedef std::vector lambda_method_handlest; - typedef std::map + typedef std::map, lambda_method_handlet> lambda_method_handle_mapt; lambda_method_handle_mapt lambda_method_handle_map; diff --git a/src/java_bytecode/java_bytecode_parser.cpp b/src/java_bytecode/java_bytecode_parser.cpp index e3b8db61925..ee4501fc089 100644 --- a/src/java_bytecode/java_bytecode_parser.cpp +++ b/src/java_bytecode/java_bytecode_parser.cpp @@ -19,6 +19,7 @@ Author: Daniel Kroening, kroening@kroening.com #include #include #include +#include #include "java_bytecode_parse_tree.h" #include "java_types.h" @@ -47,12 +48,10 @@ class java_bytecode_parsert:public parsert typedef java_bytecode_parse_treet::instructiont instructiont; typedef java_bytecode_parse_treet::annotationt annotationt; typedef java_bytecode_parse_treet::annotationst annotationst; - typedef java_bytecode_parse_treet::classt::lambda_method_handlet - lambda_method_handlet; typedef java_bytecode_parse_treet::classt::method_handle_typet method_handle_typet; - typedef java_bytecode_parse_treet::classt::lambda_method_handlest - lambda_method_handlest; + typedef java_bytecode_parse_treet::classt::lambda_method_handlet + lambda_method_handlet; typedef java_bytecode_parse_treet::classt::lambda_method_handle_mapt lambda_method_handle_mapt; @@ -134,7 +133,7 @@ class java_bytecode_parsert:public parsert void get_class_refs(); void get_class_refs_rec(const typet &); void parse_local_variable_type_table(methodt &method); - void parse_method_handle(const pool_entryt &, lambda_method_handlet &); + optionalt parse_method_handle(const pool_entryt &); void skip_bytes(std::size_t bytes) { @@ -1417,6 +1416,8 @@ void java_bytecode_parsert::rclass_attribute(classt &parsed_class) } else if(attribute_name == "BootstrapMethods") { + // for this attribute + // cf. https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.23 INVARIANT( !parsed_class.attribute_bootstrapmethods_read, "only one BootstrapMethods argument is allowed in a class file"); @@ -1430,15 +1431,15 @@ void java_bytecode_parsert::rclass_attribute(classt &parsed_class) const pool_entryt &entry = pool_entry(bootstrap_methodhandle_ref); u2 num_bootstrap_arguments = read_u2(); - lambda_method_handlet handle; + optionalt handle = parse_method_handle(entry); debug() << "INFO: parse BootstrapMethod handle " - << num_bootstrap_arguments << " #args" - << eom; - parse_method_handle(entry, handle); + << num_bootstrap_arguments << " #args" << eom; + if( - handle.handle_type == - method_handle_typet::BOOTSTRAP_METHOD_HANDLE_ALT || - handle.handle_type == method_handle_typet::BOOTSTRAP_METHOD_HANDLE) + handle.has_value() && + (handle->handle_type == + method_handle_typet::BOOTSTRAP_METHOD_HANDLE_ALT || + handle->handle_type == method_handle_typet::BOOTSTRAP_METHOD_HANDLE)) { // try parsing bootstrap method handle if(num_bootstrap_arguments >= 3) @@ -1485,8 +1486,7 @@ void java_bytecode_parsert::rclass_attribute(classt &parsed_class) } if(!recognized) { - debug() << "format of BootstrapMethods entry not recognized" - << eom; + debug() << "format of BootstrapMethods entry not recognized" << eom; return; } @@ -1496,39 +1496,40 @@ void java_bytecode_parsert::rclass_attribute(classt &parsed_class) pool_entry(argument_index2); const pool_entryt &method_type_argument = pool_entry(argument_index3); - if(!(interface_type_argument.tag == CONSTANT_MethodType && - method_handle_argument.tag == CONSTANT_MethodHandle && - method_type_argument.tag == CONSTANT_MethodType)) + if( + !(interface_type_argument.tag == CONSTANT_MethodType && + method_handle_argument.tag == CONSTANT_MethodHandle && + method_type_argument.tag == CONSTANT_MethodType)) return; - lambda_method_handlet lambda_method_handle; debug() << "INFO: parse lambda handle" << eom; - parse_method_handle(method_handle_argument, lambda_method_handle); + optionalt lambda_method_handle = + parse_method_handle(method_handle_argument); + if( - lambda_method_handle.handle_type != - method_handle_typet::LAMBDA_METHOD_HANDLE) + lambda_method_handle.has_value() && + lambda_method_handle->handle_type != + method_handle_typet::LAMBDA_METHOD_HANDLE) { - lambda_method_handlet empty_handle; - parsed_class.lambda_method_handle_map[parsed_class.name].push_back( - empty_handle); error() << "ERROR: could not parse lambda function method handle" << eom; } else { - lambda_method_handle.interface_type = pool_entry(interface_type_argument.ref1).s; - lambda_method_handle.method_type = pool_entry(method_type_argument.ref1).s; - parsed_class.lambda_method_handle_map[parsed_class.name].push_back( - lambda_method_handle); - debug() - << "lambda function reference " - << id2string(lambda_method_handle.lambda_method_name) << " in class \"" - << parsed_class.name << "\"" - << "\n interface type is " - << id2string(pool_entry(interface_type_argument.ref1).s) - << "\n method type is " - << id2string(pool_entry(method_type_argument.ref1).s) - << eom; + lambda_method_handle->interface_type = + pool_entry(interface_type_argument.ref1).s; + lambda_method_handle->method_type = + pool_entry(method_type_argument.ref1).s; + debug() << "lambda function reference " + << id2string(lambda_method_handle->lambda_method_name) + << " in class \"" << parsed_class.name << "\"" + << "\n interface type is " + << id2string(pool_entry(interface_type_argument.ref1).s) + << "\n method type is " + << id2string(pool_entry(method_type_argument.ref1).s) + << eom; + parsed_class.lambda_method_handle_map[{parsed_class.name, i}] = + *lambda_method_handle; } } else @@ -1541,9 +1542,6 @@ void java_bytecode_parsert::rclass_attribute(classt &parsed_class) } else { - lambda_method_handlet empty_handle; - parsed_class.lambda_method_handle_map[parsed_class.name].push_back( - empty_handle); // skip bytes to align for next entry for(size_t i = 0; i < num_bootstrap_arguments; i++) read_u2(); @@ -1683,15 +1681,15 @@ void java_bytecode_parsert::parse_local_variable_type_table(methodt &method) /// Read method handle pointed to from constant pool entry at index, return type /// of method handle and name if lambda function is found. /// \param entry: the constant pool entry of the methodhandle_info structure -/// \param[out] handle: the method_handle type of the methodhandle_structure, -/// either for a recognized bootstrap method, for a lambda function or unknown -void java_bytecode_parsert::parse_method_handle( - const pool_entryt &entry, - lambda_method_handlet &handle) +/// \returns: the method_handle type of the methodhandle_structure, +/// either for a recognized bootstrap method or for a lambda function +optionalt +java_bytecode_parsert::parse_method_handle(const pool_entryt &entry) { INVARIANT( entry.tag == CONSTANT_MethodHandle, "constant pool entry must be a MethodHandle"); + lambda_method_handlet lambda_method_handle; const auto &ref_entry = pool_entry(entry.ref2); INVARIANT( (entry.ref1 > 0 && entry.ref1 < 10), @@ -1701,9 +1699,9 @@ void java_bytecode_parsert::parse_method_handle( const auto &nameandtype_entry = pool_entry(ref_entry.ref2); const std::string method_name = - id2string(pool_entry(class_entry.ref1).s) + "." - + id2string(pool_entry(nameandtype_entry.ref1).s) - + id2string(pool_entry(nameandtype_entry.ref2).s); + id2string(pool_entry(class_entry.ref1).s) + "." + + id2string(pool_entry(nameandtype_entry.ref1).s) + + id2string(pool_entry(nameandtype_entry.ref2).s); if( method_name == @@ -1711,20 +1709,33 @@ void java_bytecode_parsert::parse_method_handle( "MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/" "lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/" "MethodType;)Ljava/lang/invoke/CallSite;") - handle.handle_type = method_handle_typet::BOOTSTRAP_METHOD_HANDLE; - // names seem to be lambda$$POSTFIX$NUM - // where $POSTFIX is $FUN for a function name in which the lambda is define - // "static" when it is a static member of the class - // "new" when it is a class variable, instantiated in - else if(has_prefix(id2string(pool_entry(nameandtype_entry.ref1).s), "lambda$")) { - handle.lambda_method_name = pool_entry(nameandtype_entry.ref1).s; - handle.handle_type = method_handle_typet::LAMBDA_METHOD_HANDLE; + lambda_method_handle.handle_type = + method_handle_typet::BOOTSTRAP_METHOD_HANDLE; + } + else if( + has_prefix(id2string(pool_entry(nameandtype_entry.ref1).s), "lambda$")) + { + // names seem to be lambda$POSTFIX$NUM + // where POSTFIX is FUN for a function name in which the lambda is define + // "static" when it is a static member of the class + // "new" when it is a class variable, instantiated in + lambda_method_handle.lambda_method_name = + pool_entry(nameandtype_entry.ref1).s; + lambda_method_handle.handle_type = + method_handle_typet::LAMBDA_METHOD_HANDLE; } else if( method_name == "java/lang/invoke/LambdaMetafactory.altMetafactory(Ljava/lang/invoke/" "MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/" "MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;") - handle.handle_type = method_handle_typet::BOOTSTRAP_METHOD_HANDLE_ALT; + { + lambda_method_handle.handle_type = + method_handle_typet::BOOTSTRAP_METHOD_HANDLE_ALT; + } + else + return {}; + + return lambda_method_handle; } From 0e400819c102be3de4a62186779f7f871a2e796f Mon Sep 17 00:00:00 2001 From: thk123 Date: Wed, 14 Feb 2018 15:48:00 +0000 Subject: [PATCH 06/18] Adding validation on the type of descriptor found --- src/java_bytecode/java_bytecode_parser.cpp | 51 +++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/src/java_bytecode/java_bytecode_parser.cpp b/src/java_bytecode/java_bytecode_parser.cpp index ee4501fc089..c8dc098c2fa 100644 --- a/src/java_bytecode/java_bytecode_parser.cpp +++ b/src/java_bytecode/java_bytecode_parser.cpp @@ -1678,6 +1678,21 @@ void java_bytecode_parsert::parse_local_variable_type_table(methodt &method) } } +/// Correspond to the different valid values for field reference_kind +/// From Java 8 spec 4.4.8 +enum class method_handle_kindt +{ + REF_getField = 1, + REF_getStatic = 2, + REF_putField = 3, + REF_putStatic = 4, + REF_invokeVirtual = 5, + REF_invokeStatic = 6, + REF_invokeSpecial = 7, + REF_newInvokeSpecial = 8, + REF_invokeInterface = 9 +}; + /// Read method handle pointed to from constant pool entry at index, return type /// of method handle and name if lambda function is found. /// \param entry: the constant pool entry of the methodhandle_info structure @@ -1690,14 +1705,48 @@ java_bytecode_parsert::parse_method_handle(const pool_entryt &entry) entry.tag == CONSTANT_MethodHandle, "constant pool entry must be a MethodHandle"); lambda_method_handlet lambda_method_handle; - const auto &ref_entry = pool_entry(entry.ref2); + INVARIANT( (entry.ref1 > 0 && entry.ref1 < 10), "reference kind of Methodhandle must be in the range of 1 to 9"); + const pool_entryt ref_entry = pool_entry(entry.ref2); const auto &class_entry = pool_entry(ref_entry.ref1); const auto &nameandtype_entry = pool_entry(ref_entry.ref2); + method_handle_kindt method_handle_kind = (method_handle_kindt)entry.ref1; + switch(method_handle_kind) + { + case method_handle_kindt::REF_getField: + case method_handle_kindt::REF_getStatic: + case method_handle_kindt::REF_putField: + case method_handle_kindt::REF_putStatic: + { + INVARIANT(ref_entry.tag == CONSTANT_Fieldref, "4.4.2"); + break; + } + case method_handle_kindt::REF_invokeVirtual: + case method_handle_kindt::REF_newInvokeSpecial: + { + + INVARIANT(ref_entry.tag == CONSTANT_Methodref, "4.4.2"); + break; + } + case method_handle_kindt::REF_invokeStatic: + case method_handle_kindt::REF_invokeSpecial: + { + INVARIANT( + ref_entry.tag == CONSTANT_Methodref || + ref_entry.tag == CONSTANT_InterfaceMethodref, + "4.4.2"); + break; + } + case method_handle_kindt::REF_invokeInterface: + { + INVARIANT(ref_entry.tag == CONSTANT_InterfaceMethodref,""); + break; + } + } const std::string method_name = id2string(pool_entry(class_entry.ref1).s) + "." + id2string(pool_entry(nameandtype_entry.ref1).s) + From 3ac7d09a3dd562773a53aa4eb0a229f8f26fe470 Mon Sep 17 00:00:00 2001 From: thk123 Date: Wed, 14 Feb 2018 15:45:25 +0000 Subject: [PATCH 07/18] Introduce classes representing relevant constant pool entries --- src/java_bytecode/java_bytecode_parser.cpp | 119 +++++++++++++++++++++ 1 file changed, 119 insertions(+) diff --git a/src/java_bytecode/java_bytecode_parser.cpp b/src/java_bytecode/java_bytecode_parser.cpp index c8dc098c2fa..115992b811e 100644 --- a/src/java_bytecode/java_bytecode_parser.cpp +++ b/src/java_bytecode/java_bytecode_parser.cpp @@ -210,6 +210,125 @@ class java_bytecode_parsert:public parsert #define VTYPE_INFO_OBJECT 7 #define VTYPE_INFO_UNINIT 8 + +class structured_pool_entryt +{ +public: + explicit structured_pool_entryt(java_bytecode_parsert::pool_entryt entry) + : tag(entry.tag) + { + } + + u1 get_tag() const { return tag; } + + typedef std::function pool_entry_lookupt; + typedef java_bytecode_parsert::pool_entryt pool_entryt; + +protected: + static std::string read_utf8_constant(const pool_entryt &entry) + { + INVARIANT( + entry.tag == CONSTANT_Utf8, "Name entry must be a constant UTF-8"); + return id2string(entry.s); + } + +private: + u1 tag; +}; + +/// Corresponds to the CONSTANT_Class_info Structure +/// Described in Java 8 specification 4.4.1 +class class_infot : public structured_pool_entryt +{ +public: + explicit class_infot(const pool_entryt &entry): structured_pool_entryt(entry) + { + PRECONDITION(entry.tag == CONSTANT_Class); + name_index=entry.ref1; + } + + std::string get_name(pool_entry_lookupt pool_entry) const + { + const pool_entryt &name_entry = pool_entry(name_index); + return read_utf8_constant(name_entry); + } + +private: + u2 name_index; +}; + +/// Corresponds to the CONSTANT_NameAndType_info Structure +/// Described in Java 8 specification 4.4.6 +class name_and_type_infot : public structured_pool_entryt +{ +public: + explicit name_and_type_infot(java_bytecode_parsert::pool_entryt entry) + : structured_pool_entryt(entry) + { + PRECONDITION(entry.tag == CONSTANT_NameAndType); + name_index = entry.ref1; + descriptor_index = entry.ref2; + } + + std::string get_name(pool_entry_lookupt pool_entry) const + { + const pool_entryt &name_entry = pool_entry(name_index); + return read_utf8_constant(name_entry); + } + + std::string get_descriptor(pool_entry_lookupt pool_entry) const + { + const pool_entryt &descriptor_entry = pool_entry(descriptor_index); + return read_utf8_constant(descriptor_entry); + } + +private: + u2 name_index; + u2 descriptor_index; +}; + +class base_ref_infot : public structured_pool_entryt +{ +public: + explicit base_ref_infot(pool_entryt entry) + : structured_pool_entryt(entry) + { + static std::set info_tags = { + CONSTANT_Fieldref, CONSTANT_Methodref, CONSTANT_InterfaceMethodref}; + PRECONDITION(info_tags.find(entry.tag) != info_tags.end()); + class_index=entry.ref1; + name_and_type_index=entry.ref2; + } + + u1 get_class_index() const { return class_index; } + u1 get_name_and_type_index() const { return name_and_type_index; } + + name_and_type_infot get_name_and_type(pool_entry_lookupt pool_entry) const + { + const pool_entryt &name_and_type_entry = + pool_entry(name_and_type_index); + + INVARIANT( + name_and_type_entry.tag == CONSTANT_NameAndType, + "name_and_typeindex did not correspond to a name_and_type in the constants " + "pool"); + + return name_and_type_infot{name_and_type_entry}; + } + + class_infot get_class(pool_entry_lookupt pool_entry) const + { + const pool_entryt &class_entry = + pool_entry(class_index); + + return class_infot{class_entry}; + } + +private: + u2 class_index; + u2 name_and_type_index; +}; + bool java_bytecode_parsert::parse() { try From e3ff312105b40a81c1799c8f8eb706169e16dff7 Mon Sep 17 00:00:00 2001 From: thk123 Date: Wed, 14 Feb 2018 15:52:18 +0000 Subject: [PATCH 08/18] Use the strcutured classes to simplify and make more explict the code --- src/java_bytecode/java_bytecode_parser.cpp | 28 +++++++++++++--------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/java_bytecode/java_bytecode_parser.cpp b/src/java_bytecode/java_bytecode_parser.cpp index 115992b811e..d2c4efa4ef4 100644 --- a/src/java_bytecode/java_bytecode_parser.cpp +++ b/src/java_bytecode/java_bytecode_parser.cpp @@ -1829,9 +1829,7 @@ java_bytecode_parsert::parse_method_handle(const pool_entryt &entry) (entry.ref1 > 0 && entry.ref1 < 10), "reference kind of Methodhandle must be in the range of 1 to 9"); - const pool_entryt ref_entry = pool_entry(entry.ref2); - const auto &class_entry = pool_entry(ref_entry.ref1); - const auto &nameandtype_entry = pool_entry(ref_entry.ref2); + const base_ref_infot ref_entry{pool_entry(entry.ref2)}; method_handle_kindt method_handle_kind = (method_handle_kindt)entry.ref1; switch(method_handle_kind) @@ -1841,35 +1839,43 @@ java_bytecode_parsert::parse_method_handle(const pool_entryt &entry) case method_handle_kindt::REF_putField: case method_handle_kindt::REF_putStatic: { - INVARIANT(ref_entry.tag == CONSTANT_Fieldref, "4.4.2"); + INVARIANT(ref_entry.get_tag() == CONSTANT_Fieldref, "4.4.2"); break; } case method_handle_kindt::REF_invokeVirtual: case method_handle_kindt::REF_newInvokeSpecial: { - INVARIANT(ref_entry.tag == CONSTANT_Methodref, "4.4.2"); + INVARIANT(ref_entry.get_tag() == CONSTANT_Methodref, "4.4.2"); break; } case method_handle_kindt::REF_invokeStatic: case method_handle_kindt::REF_invokeSpecial: { INVARIANT( - ref_entry.tag == CONSTANT_Methodref || - ref_entry.tag == CONSTANT_InterfaceMethodref, + ref_entry.get_tag() == CONSTANT_Methodref || + ref_entry.get_tag() == CONSTANT_InterfaceMethodref, "4.4.2"); break; } case method_handle_kindt::REF_invokeInterface: { - INVARIANT(ref_entry.tag == CONSTANT_InterfaceMethodref,""); + INVARIANT(ref_entry.get_tag() == CONSTANT_InterfaceMethodref,""); break; } } + + const std::function pool_entry_lambda = + [this](u2 index) -> pool_entryt & { return pool_entry(index); }; + + const class_infot &class_entry=ref_entry.get_class(pool_entry_lambda); + const name_and_type_infot &name_and_type = + ref_entry.get_name_and_type(pool_entry_lambda); + const std::string method_name = - id2string(pool_entry(class_entry.ref1).s) + "." + - id2string(pool_entry(nameandtype_entry.ref1).s) + - id2string(pool_entry(nameandtype_entry.ref2).s); + class_entry.get_name(pool_entry_lambda) + "." + + name_and_type.get_name(pool_entry_lambda) + + name_and_type.get_descriptor(pool_entry_lambda); if( method_name == From b56e42ee5da5f65334634e075351ef84ba1bf9ab Mon Sep 17 00:00:00 2001 From: thk123 Date: Thu, 15 Feb 2018 15:05:38 +0000 Subject: [PATCH 09/18] Fix missing swap for classt --- src/java_bytecode/java_bytecode_parse_tree.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/java_bytecode/java_bytecode_parse_tree.cpp b/src/java_bytecode/java_bytecode_parse_tree.cpp index b8a60d8ddca..6f6f0b92d41 100644 --- a/src/java_bytecode/java_bytecode_parse_tree.cpp +++ b/src/java_bytecode/java_bytecode_parse_tree.cpp @@ -33,6 +33,9 @@ void java_bytecode_parse_treet::classt::swap( other.fields.swap(fields); other.methods.swap(methods); other.annotations.swap(annotations); + std::swap( + other.attribute_bootstrapmethods_read, attribute_bootstrapmethods_read); + std::swap(other.lambda_method_handle_map, lambda_method_handle_map); } void java_bytecode_parse_treet::output(std::ostream &out) const From ca5dae4ab1d12fb7bd71369da4b761eff0479551 Mon Sep 17 00:00:00 2001 From: thk123 Date: Thu, 15 Feb 2018 15:11:36 +0000 Subject: [PATCH 10/18] Fixup Use the strcutured classes to simplify and make more explict the code --- src/java_bytecode/java_bytecode_parser.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/java_bytecode/java_bytecode_parser.cpp b/src/java_bytecode/java_bytecode_parser.cpp index d2c4efa4ef4..c84858a6961 100644 --- a/src/java_bytecode/java_bytecode_parser.cpp +++ b/src/java_bytecode/java_bytecode_parser.cpp @@ -1888,14 +1888,14 @@ java_bytecode_parsert::parse_method_handle(const pool_entryt &entry) method_handle_typet::BOOTSTRAP_METHOD_HANDLE; } else if( - has_prefix(id2string(pool_entry(nameandtype_entry.ref1).s), "lambda$")) + has_prefix(name_and_type.get_name(pool_entry_lambda), "lambda$")) { // names seem to be lambda$POSTFIX$NUM // where POSTFIX is FUN for a function name in which the lambda is define // "static" when it is a static member of the class // "new" when it is a class variable, instantiated in lambda_method_handle.lambda_method_name = - pool_entry(nameandtype_entry.ref1).s; + name_and_type.get_name(pool_entry_lambda); lambda_method_handle.handle_type = method_handle_typet::LAMBDA_METHOD_HANDLE; } From 463ffe1921f034c15312030613f1ec1a2eab38e2 Mon Sep 17 00:00:00 2001 From: thk123 Date: Thu, 15 Feb 2018 15:49:07 +0000 Subject: [PATCH 11/18] Refactor parse_method_handle to just deal with the lambda special case Moved general parsing of a CONSTANT_methodhandle_info into a strcutured class --- src/java_bytecode/java_bytecode_parser.cpp | 358 ++++++++++----------- 1 file changed, 174 insertions(+), 184 deletions(-) diff --git a/src/java_bytecode/java_bytecode_parser.cpp b/src/java_bytecode/java_bytecode_parser.cpp index c84858a6961..e83c423cd60 100644 --- a/src/java_bytecode/java_bytecode_parser.cpp +++ b/src/java_bytecode/java_bytecode_parser.cpp @@ -133,7 +133,8 @@ class java_bytecode_parsert:public parsert void get_class_refs(); void get_class_refs_rec(const typet &); void parse_local_variable_type_table(methodt &method); - optionalt parse_method_handle(const pool_entryt &); + optionalt + parse_method_handle(const class method_handle_infot &entry); void skip_bytes(std::size_t bytes) { @@ -329,6 +330,81 @@ class base_ref_infot : public structured_pool_entryt u2 name_and_type_index; }; +class method_handle_infot : public structured_pool_entryt +{ +public: + + /// Correspond to the different valid values for field reference_kind +/// From Java 8 spec 4.4.8 + enum class method_handle_kindt + { + REF_getField = 1, + REF_getStatic = 2, + REF_putField = 3, + REF_putStatic = 4, + REF_invokeVirtual = 5, + REF_invokeStatic = 6, + REF_invokeSpecial = 7, + REF_newInvokeSpecial = 8, + REF_invokeInterface = 9 + }; + + explicit method_handle_infot(java_bytecode_parsert::pool_entryt entry) + : structured_pool_entryt(entry) + { + PRECONDITION(entry.tag == CONSTANT_MethodHandle); + PRECONDITION(entry.ref1 > 0 && entry.ref1 < 10); // Java 8 spec 4.4.8 + reference_kind = static_cast(entry.ref1); + reference_index = entry.ref2; + } + + base_ref_infot get_reference(pool_entry_lookupt pool_entry) const + { + const base_ref_infot ref_entry{pool_entry(reference_index)}; + + // validate the correctness of the constant pool entry + switch(reference_kind) + { + case method_handle_kindt::REF_getField: + case method_handle_kindt::REF_getStatic: + case method_handle_kindt::REF_putField: + case method_handle_kindt::REF_putStatic: + { + INVARIANT(ref_entry.get_tag() == CONSTANT_Fieldref, "4.4.2"); + break; + } + case method_handle_kindt::REF_invokeVirtual: + case method_handle_kindt::REF_newInvokeSpecial: + { + + INVARIANT(ref_entry.get_tag() == CONSTANT_Methodref, "4.4.2"); + break; + } + case method_handle_kindt::REF_invokeStatic: + case method_handle_kindt::REF_invokeSpecial: + { + INVARIANT( + ref_entry.get_tag() == CONSTANT_Methodref || + ref_entry.get_tag() == CONSTANT_InterfaceMethodref, + "4.4.2"); + break; + } + case method_handle_kindt::REF_invokeInterface: + { + INVARIANT(ref_entry.get_tag() == CONSTANT_InterfaceMethodref,""); + break; + } + } + + return ref_entry; + } + + +private: + method_handle_kindt reference_kind; + u2 reference_index; +}; + bool java_bytecode_parsert::parse() { try @@ -1548,115 +1624,102 @@ void java_bytecode_parsert::rclass_attribute(classt &parsed_class) { u2 bootstrap_methodhandle_ref = read_u2(); const pool_entryt &entry = pool_entry(bootstrap_methodhandle_ref); - u2 num_bootstrap_arguments = read_u2(); - optionalt handle = parse_method_handle(entry); + method_handle_infot method_handle{entry}; + + u2 num_bootstrap_arguments = read_u2(); debug() << "INFO: parse BootstrapMethod handle " << num_bootstrap_arguments << " #args" << eom; - if( - handle.has_value() && - (handle->handle_type == - method_handle_typet::BOOTSTRAP_METHOD_HANDLE_ALT || - handle->handle_type == method_handle_typet::BOOTSTRAP_METHOD_HANDLE)) - { // try parsing bootstrap method handle - if(num_bootstrap_arguments >= 3) + if(num_bootstrap_arguments >= 3) + { + // each entry contains a MethodHandle structure + // u2 tag + // u2 reference kind which must be in the range from 1 to 9 + // u2 reference index into the constant pool + // + // reference kinds use the following + // 1 to 4 must point to a CONSTANT_Fieldref structure + // 5 or 8 must point to a CONSTANT_Methodref structure + // 6 or 7 must point to a CONSTANT_Methodref or + // CONSTANT_InterfaceMethodref structure, if the class file version + // number is 52.0 or above, to a CONSTANT_Methodref only in the case + // of less than 52.0 + // 9 must point to a CONSTANT_InterfaceMethodref structure + + // the index must point to a CONSTANT_String + // CONSTANT_Class + // CONSTANT_Integer + // CONSTANT_Long + // CONSTANT_Float + // CONSTANT_Double + // CONSTANT_MethodHandle + // CONSTANT_MethodType + + // We read the three arguments here to see whether they correspond to + // our hypotheses for this being a lambda function entry. + + u2 argument_index1 = read_u2(); + u2 argument_index2 = read_u2(); + u2 argument_index3 = read_u2(); + + // The additional arguments for the altmetafactory call are skipped, + // as they are currently not used. We verify though that they are of + // CONSTANT_Integer type, cases where this does not hold will be + // analyzed further. + bool recognized = true; + for(size_t i = 3; i < num_bootstrap_arguments; i++) + { + u2 skipped_argument = read_u2(); + recognized |= pool_entry(skipped_argument).tag == CONSTANT_Integer; + } + if(!recognized) { - // each entry contains a MethodHandle structure - // u2 tag - // u2 reference kind which must be in the range from 1 to 9 - // u2 reference index into the constant pool - // - // reference kinds use the following - // 1 to 4 must point to a CONSTANT_Fieldref structure - // 5 or 8 must point to a CONSTANT_Methodref structure - // 6 or 7 must point to a CONSTANT_Methodref or - // CONSTANT_InterfaceMethodref structure, if the class file version - // number is 52.0 or above, to a CONSTANT_Methodref only in the case - // of less than 52.0 - // 9 must point to a CONSTANT_InterfaceMethodref structure - - // the index must point to a CONSTANT_String - // CONSTANT_Class - // CONSTANT_Integer - // CONSTANT_Long - // CONSTANT_Float - // CONSTANT_Double - // CONSTANT_MethodHandle - // CONSTANT_MethodType - - // We read the three arguments here to see whether they correspond to - // our hypotheses for this being a lambda function entry. - - u2 argument_index1 = read_u2(); - u2 argument_index2 = read_u2(); - u2 argument_index3 = read_u2(); - - // The additional arguments for the altmetafactory call are skipped, - // as they are currently not used. We verify though that they are of - // CONSTANT_Integer type, cases where this does not hold will be - // analyzed further. - bool recognized = true; - for(size_t i = 3; i < num_bootstrap_arguments; i++) - { - u2 skipped_argument = read_u2(); - recognized |= pool_entry(skipped_argument).tag == CONSTANT_Integer; - } - if(!recognized) - { - debug() << "format of BootstrapMethods entry not recognized" << eom; - return; - } - - const pool_entryt &interface_type_argument = - pool_entry(argument_index1); - const pool_entryt &method_handle_argument = - pool_entry(argument_index2); - const pool_entryt &method_type_argument = pool_entry(argument_index3); - - if( - !(interface_type_argument.tag == CONSTANT_MethodType && - method_handle_argument.tag == CONSTANT_MethodHandle && - method_type_argument.tag == CONSTANT_MethodType)) - return; - - debug() << "INFO: parse lambda handle" << eom; - optionalt lambda_method_handle = - parse_method_handle(method_handle_argument); - - if( - lambda_method_handle.has_value() && - lambda_method_handle->handle_type != - method_handle_typet::LAMBDA_METHOD_HANDLE) - { - error() << "ERROR: could not parse lambda function method handle" - << eom; - } - else - { - lambda_method_handle->interface_type = - pool_entry(interface_type_argument.ref1).s; - lambda_method_handle->method_type = - pool_entry(method_type_argument.ref1).s; - debug() << "lambda function reference " - << id2string(lambda_method_handle->lambda_method_name) - << " in class \"" << parsed_class.name << "\"" - << "\n interface type is " - << id2string(pool_entry(interface_type_argument.ref1).s) - << "\n method type is " - << id2string(pool_entry(method_type_argument.ref1).s) - << eom; - parsed_class.lambda_method_handle_map[{parsed_class.name, i}] = - *lambda_method_handle; - } + debug() << "format of BootstrapMethods entry not recognized" << eom; + return; + } + + const pool_entryt &interface_type_argument = + pool_entry(argument_index1); + const pool_entryt &method_handle_argument = + pool_entry(argument_index2); + const pool_entryt &method_type_argument = pool_entry(argument_index3); + + if( + !(interface_type_argument.tag == CONSTANT_MethodType && + method_handle_argument.tag == CONSTANT_MethodHandle && + method_type_argument.tag == CONSTANT_MethodType)) + return; + + debug() << "INFO: parse lambda handle" << eom; + optionalt lambda_method_handle = + parse_method_handle(method_handle_infot{method_handle_argument}); + + if( + lambda_method_handle.has_value() && + lambda_method_handle->handle_type != + method_handle_typet::LAMBDA_METHOD_HANDLE) + { + error() << "ERROR: could not parse lambda function method handle" + << eom; } else { - // skip bytes to align for next entry - for(size_t i = 0; i < num_bootstrap_arguments; i++) - read_u2(); - error() << "ERROR: num_bootstrap_arguments must be at least 3" << eom; + lambda_method_handle->interface_type = + pool_entry(interface_type_argument.ref1).s; + lambda_method_handle->method_type = + pool_entry(method_type_argument.ref1).s; + debug() << "lambda function reference " + << id2string(lambda_method_handle->lambda_method_name) + << " in class \"" << parsed_class.name << "\"" + << "\n interface type is " + << id2string(pool_entry(interface_type_argument.ref1).s) + << "\n method type is " + << id2string(pool_entry(method_type_argument.ref1).s) + << eom; + parsed_class.lambda_method_handle_map[{parsed_class.name, i}] = + *lambda_method_handle; } } else @@ -1664,7 +1727,7 @@ void java_bytecode_parsert::rclass_attribute(classt &parsed_class) // skip bytes to align for next entry for(size_t i = 0; i < num_bootstrap_arguments; i++) read_u2(); - error() << "ERROR: could not parse BootstrapMethods entry" << eom; + error() << "ERROR: num_bootstrap_arguments must be at least 3" << eom; } } } @@ -1797,20 +1860,7 @@ void java_bytecode_parsert::parse_local_variable_type_table(methodt &method) } } -/// Correspond to the different valid values for field reference_kind -/// From Java 8 spec 4.4.8 -enum class method_handle_kindt -{ - REF_getField = 1, - REF_getStatic = 2, - REF_putField = 3, - REF_putStatic = 4, - REF_invokeVirtual = 5, - REF_invokeStatic = 6, - REF_invokeSpecial = 7, - REF_newInvokeSpecial = 8, - REF_invokeInterface = 9 -}; + /// Read method handle pointed to from constant pool entry at index, return type /// of method handle and name if lambda function is found. @@ -1818,56 +1868,13 @@ enum class method_handle_kindt /// \returns: the method_handle type of the methodhandle_structure, /// either for a recognized bootstrap method or for a lambda function optionalt -java_bytecode_parsert::parse_method_handle(const pool_entryt &entry) +java_bytecode_parsert::parse_method_handle(const method_handle_infot &entry) { - INVARIANT( - entry.tag == CONSTANT_MethodHandle, - "constant pool entry must be a MethodHandle"); - lambda_method_handlet lambda_method_handle; - - INVARIANT( - (entry.ref1 > 0 && entry.ref1 < 10), - "reference kind of Methodhandle must be in the range of 1 to 9"); - - const base_ref_infot ref_entry{pool_entry(entry.ref2)}; - - method_handle_kindt method_handle_kind = (method_handle_kindt)entry.ref1; - switch(method_handle_kind) - { - case method_handle_kindt::REF_getField: - case method_handle_kindt::REF_getStatic: - case method_handle_kindt::REF_putField: - case method_handle_kindt::REF_putStatic: - { - INVARIANT(ref_entry.get_tag() == CONSTANT_Fieldref, "4.4.2"); - break; - } - case method_handle_kindt::REF_invokeVirtual: - case method_handle_kindt::REF_newInvokeSpecial: - { - - INVARIANT(ref_entry.get_tag() == CONSTANT_Methodref, "4.4.2"); - break; - } - case method_handle_kindt::REF_invokeStatic: - case method_handle_kindt::REF_invokeSpecial: - { - INVARIANT( - ref_entry.get_tag() == CONSTANT_Methodref || - ref_entry.get_tag() == CONSTANT_InterfaceMethodref, - "4.4.2"); - break; - } - case method_handle_kindt::REF_invokeInterface: - { - INVARIANT(ref_entry.get_tag() == CONSTANT_InterfaceMethodref,""); - break; - } - } - const std::function pool_entry_lambda = [this](u2 index) -> pool_entryt & { return pool_entry(index); }; + const base_ref_infot &ref_entry = entry.get_reference(pool_entry_lambda); + const class_infot &class_entry=ref_entry.get_class(pool_entry_lambda); const name_and_type_infot &name_and_type = ref_entry.get_name_and_type(pool_entry_lambda); @@ -1877,17 +1884,9 @@ java_bytecode_parsert::parse_method_handle(const pool_entryt &entry) name_and_type.get_name(pool_entry_lambda) + name_and_type.get_descriptor(pool_entry_lambda); + lambda_method_handlet lambda_method_handle; + if( - method_name == - "java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/" - "MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/" - "lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/" - "MethodType;)Ljava/lang/invoke/CallSite;") - { - lambda_method_handle.handle_type = - method_handle_typet::BOOTSTRAP_METHOD_HANDLE; - } - else if( has_prefix(name_and_type.get_name(pool_entry_lambda), "lambda$")) { // names seem to be lambda$POSTFIX$NUM @@ -1898,18 +1897,9 @@ java_bytecode_parsert::parse_method_handle(const pool_entryt &entry) name_and_type.get_name(pool_entry_lambda); lambda_method_handle.handle_type = method_handle_typet::LAMBDA_METHOD_HANDLE; + + return lambda_method_handle; } - else if( - method_name == - "java/lang/invoke/LambdaMetafactory.altMetafactory(Ljava/lang/invoke/" - "MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/" - "MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;") - { - lambda_method_handle.handle_type = - method_handle_typet::BOOTSTRAP_METHOD_HANDLE_ALT; - } - else - return {}; - return lambda_method_handle; + return {}; } From e0ccb12bad344ce6fcd9be1e6c1fc3a14a12e89e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20G=C3=BCdemann?= Date: Mon, 19 Feb 2018 15:00:58 +0100 Subject: [PATCH 12/18] Refactor BootstrapMethods attribute reading into function --- src/java_bytecode/java_bytecode_parser.cpp | 229 +++++++++++---------- 1 file changed, 118 insertions(+), 111 deletions(-) diff --git a/src/java_bytecode/java_bytecode_parser.cpp b/src/java_bytecode/java_bytecode_parser.cpp index e83c423cd60..760db18f6db 100644 --- a/src/java_bytecode/java_bytecode_parser.cpp +++ b/src/java_bytecode/java_bytecode_parser.cpp @@ -135,6 +135,7 @@ class java_bytecode_parsert:public parsert void parse_local_variable_type_table(methodt &method); optionalt parse_method_handle(const class method_handle_infot &entry); + void read_bootstrapmethods_entry(classt &); void skip_bytes(std::size_t bytes) { @@ -1619,117 +1620,7 @@ void java_bytecode_parsert::rclass_attribute(classt &parsed_class) // mark as read in parsed class parsed_class.attribute_bootstrapmethods_read = true; - u2 num_bootstrap_methods = read_u2(); - for(size_t i = 0; i < num_bootstrap_methods; i++) - { - u2 bootstrap_methodhandle_ref = read_u2(); - const pool_entryt &entry = pool_entry(bootstrap_methodhandle_ref); - - method_handle_infot method_handle{entry}; - - u2 num_bootstrap_arguments = read_u2(); - debug() << "INFO: parse BootstrapMethod handle " - << num_bootstrap_arguments << " #args" << eom; - - // try parsing bootstrap method handle - if(num_bootstrap_arguments >= 3) - { - // each entry contains a MethodHandle structure - // u2 tag - // u2 reference kind which must be in the range from 1 to 9 - // u2 reference index into the constant pool - // - // reference kinds use the following - // 1 to 4 must point to a CONSTANT_Fieldref structure - // 5 or 8 must point to a CONSTANT_Methodref structure - // 6 or 7 must point to a CONSTANT_Methodref or - // CONSTANT_InterfaceMethodref structure, if the class file version - // number is 52.0 or above, to a CONSTANT_Methodref only in the case - // of less than 52.0 - // 9 must point to a CONSTANT_InterfaceMethodref structure - - // the index must point to a CONSTANT_String - // CONSTANT_Class - // CONSTANT_Integer - // CONSTANT_Long - // CONSTANT_Float - // CONSTANT_Double - // CONSTANT_MethodHandle - // CONSTANT_MethodType - - // We read the three arguments here to see whether they correspond to - // our hypotheses for this being a lambda function entry. - - u2 argument_index1 = read_u2(); - u2 argument_index2 = read_u2(); - u2 argument_index3 = read_u2(); - - // The additional arguments for the altmetafactory call are skipped, - // as they are currently not used. We verify though that they are of - // CONSTANT_Integer type, cases where this does not hold will be - // analyzed further. - bool recognized = true; - for(size_t i = 3; i < num_bootstrap_arguments; i++) - { - u2 skipped_argument = read_u2(); - recognized |= pool_entry(skipped_argument).tag == CONSTANT_Integer; - } - if(!recognized) - { - debug() << "format of BootstrapMethods entry not recognized" << eom; - return; - } - - const pool_entryt &interface_type_argument = - pool_entry(argument_index1); - const pool_entryt &method_handle_argument = - pool_entry(argument_index2); - const pool_entryt &method_type_argument = pool_entry(argument_index3); - - if( - !(interface_type_argument.tag == CONSTANT_MethodType && - method_handle_argument.tag == CONSTANT_MethodHandle && - method_type_argument.tag == CONSTANT_MethodType)) - return; - - debug() << "INFO: parse lambda handle" << eom; - optionalt lambda_method_handle = - parse_method_handle(method_handle_infot{method_handle_argument}); - - if( - lambda_method_handle.has_value() && - lambda_method_handle->handle_type != - method_handle_typet::LAMBDA_METHOD_HANDLE) - { - error() << "ERROR: could not parse lambda function method handle" - << eom; - } - else - { - lambda_method_handle->interface_type = - pool_entry(interface_type_argument.ref1).s; - lambda_method_handle->method_type = - pool_entry(method_type_argument.ref1).s; - debug() << "lambda function reference " - << id2string(lambda_method_handle->lambda_method_name) - << " in class \"" << parsed_class.name << "\"" - << "\n interface type is " - << id2string(pool_entry(interface_type_argument.ref1).s) - << "\n method type is " - << id2string(pool_entry(method_type_argument.ref1).s) - << eom; - parsed_class.lambda_method_handle_map[{parsed_class.name, i}] = - *lambda_method_handle; - } - } - else - { - // skip bytes to align for next entry - for(size_t i = 0; i < num_bootstrap_arguments; i++) - read_u2(); - error() << "ERROR: num_bootstrap_arguments must be at least 3" << eom; - } - } + read_bootstrapmethods_entry(parsed_class); } else skip_bytes(attribute_length); @@ -1903,3 +1794,119 @@ java_bytecode_parsert::parse_method_handle(const method_handle_infot &entry) return {}; } + +/// Read all entries of the `BootstrapMethods` attribute of a class file. +/// \param parsed_class: the class representation of the class file that is +/// currently parsed +void java_bytecode_parsert::read_bootstrapmethods_entry( + classt &parsed_class) +{ + u2 num_bootstrap_methods = read_u2(); + for(size_t i = 0; i < num_bootstrap_methods; i++) + { + u2 bootstrap_methodhandle_ref = read_u2(); + const pool_entryt &entry = pool_entry(bootstrap_methodhandle_ref); + + method_handle_infot method_handle{entry}; + + u2 num_bootstrap_arguments = read_u2(); + debug() << "INFO: parse BootstrapMethod handle " + << num_bootstrap_arguments << " #args" << eom; + + // try parsing bootstrap method handle + if(num_bootstrap_arguments >= 3) + { + // each entry contains a MethodHandle structure + // u2 tag + // u2 reference kind which must be in the range from 1 to 9 + // u2 reference index into the constant pool + // + // reference kinds use the following + // 1 to 4 must point to a CONSTANT_Fieldref structure + // 5 or 8 must point to a CONSTANT_Methodref structure + // 6 or 7 must point to a CONSTANT_Methodref or + // CONSTANT_InterfaceMethodref structure, if the class file version + // number is 52.0 or above, to a CONSTANT_Methodref only in the case + // of less than 52.0 + // 9 must point to a CONSTANT_InterfaceMethodref structure + + // the index must point to a CONSTANT_String + // CONSTANT_Class + // CONSTANT_Integer + // CONSTANT_Long + // CONSTANT_Float + // CONSTANT_Double + // CONSTANT_MethodHandle + // CONSTANT_MethodType + + // We read the three arguments here to see whether they correspond to + // our hypotheses for this being a lambda function entry. + + u2 argument_index1 = read_u2(); + u2 argument_index2 = read_u2(); + u2 argument_index3 = read_u2(); + + // The additional arguments for the altmetafactory call are skipped, + // as they are currently not used. We verify though that they are of + // CONSTANT_Integer type, cases where this does not hold will be + // analyzed further. + bool recognized = true; + for(size_t i = 3; i < num_bootstrap_arguments; i++) + { + u2 skipped_argument = read_u2(); + recognized &= pool_entry(skipped_argument).tag == CONSTANT_Integer; + } + if(!recognized) + { + debug() << "format of BootstrapMethods entry not recognized" << eom; + return; + } + + const pool_entryt &interface_type_argument = pool_entry(argument_index1); + const pool_entryt &method_handle_argument = pool_entry(argument_index2); + const pool_entryt &method_type_argument = pool_entry(argument_index3); + + if( + !(interface_type_argument.tag == CONSTANT_MethodType && + method_handle_argument.tag == CONSTANT_MethodHandle && + method_type_argument.tag == CONSTANT_MethodType)) + return; + + debug() << "INFO: parse lambda handle" << eom; + optionalt lambda_method_handle = + parse_method_handle(method_handle_infot{method_handle_argument}); + + if( + lambda_method_handle.has_value() && + lambda_method_handle->handle_type != + method_handle_typet::LAMBDA_METHOD_HANDLE) + { + error() << "ERROR: could not parse lambda function method handle" + << eom; + } + else + { + lambda_method_handle->interface_type = + pool_entry(interface_type_argument.ref1).s; + lambda_method_handle->method_type = + pool_entry(method_type_argument.ref1).s; + debug() << "lambda function reference " + << id2string(lambda_method_handle->lambda_method_name) + << " in class \"" << parsed_class.name << "\"" + << "\n interface type is " + << id2string(pool_entry(interface_type_argument.ref1).s) + << "\n method type is " + << id2string(pool_entry(method_type_argument.ref1).s) << eom; + parsed_class.lambda_method_handle_map[{parsed_class.name, i}] = + *lambda_method_handle; + } + } + else + { + // skip bytes to align for next entry + for(size_t i = 0; i < num_bootstrap_arguments; i++) + read_u2(); + error() << "ERROR: num_bootstrap_arguments must be at least 3" << eom; + } + } +} From f79e89516083192fb7a6cd9518230d164352ab60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20G=C3=BCdemann?= Date: Tue, 20 Feb 2018 09:46:09 +0100 Subject: [PATCH 13/18] Fix format and account for reviewer's comments --- src/java_bytecode/java_bytecode_parse_tree.h | 11 +- src/java_bytecode/java_bytecode_parser.cpp | 113 +++++++++++-------- 2 files changed, 72 insertions(+), 52 deletions(-) diff --git a/src/java_bytecode/java_bytecode_parse_tree.h b/src/java_bytecode/java_bytecode_parse_tree.h index 26d6b707f37..b4593f58d2e 100644 --- a/src/java_bytecode/java_bytecode_parse_tree.h +++ b/src/java_bytecode/java_bytecode_parse_tree.h @@ -178,12 +178,11 @@ class java_bytecode_parse_treet enum class method_handle_typet { - BOOTSTRAP_METHOD_HANDLE, - BOOTSTRAP_METHOD_HANDLE_ALT, LAMBDA_METHOD_HANDLE, UNKNOWN_HANDLE }; + typedef std::vector u2_valuest; class lambda_method_handlet { public: @@ -191,10 +190,10 @@ class java_bytecode_parse_treet irep_idt lambda_method_name; irep_idt interface_type; irep_idt method_type; - lambda_method_handlet() - : handle_type(method_handle_typet::UNKNOWN_HANDLE) - { - } + u2_valuest u2_values; + lambda_method_handlet() : handle_type(method_handle_typet::UNKNOWN_HANDLE) + { + } }; typedef std::map, lambda_method_handlet> diff --git a/src/java_bytecode/java_bytecode_parser.cpp b/src/java_bytecode/java_bytecode_parser.cpp index 760db18f6db..cce0f95a89c 100644 --- a/src/java_bytecode/java_bytecode_parser.cpp +++ b/src/java_bytecode/java_bytecode_parser.cpp @@ -54,6 +54,7 @@ class java_bytecode_parsert:public parsert lambda_method_handlet; typedef java_bytecode_parse_treet::classt::lambda_method_handle_mapt lambda_method_handle_mapt; + typedef java_bytecode_parse_treet::classt::u2_valuest u2_valuest; java_bytecode_parse_treet parse_tree; @@ -212,7 +213,6 @@ class java_bytecode_parsert:public parsert #define VTYPE_INFO_OBJECT 7 #define VTYPE_INFO_UNINIT 8 - class structured_pool_entryt { public: @@ -221,9 +221,13 @@ class structured_pool_entryt { } - u1 get_tag() const { return tag; } + u1 get_tag() const + { + return tag; + } - typedef std::function pool_entry_lookupt; + typedef std::function + pool_entry_lookupt; typedef java_bytecode_parsert::pool_entryt pool_entryt; protected: @@ -243,10 +247,10 @@ class structured_pool_entryt class class_infot : public structured_pool_entryt { public: - explicit class_infot(const pool_entryt &entry): structured_pool_entryt(entry) + explicit class_infot(const pool_entryt &entry) : structured_pool_entryt(entry) { PRECONDITION(entry.tag == CONSTANT_Class); - name_index=entry.ref1; + name_index = entry.ref1; } std::string get_name(pool_entry_lookupt pool_entry) const @@ -292,36 +296,39 @@ class name_and_type_infot : public structured_pool_entryt class base_ref_infot : public structured_pool_entryt { public: - explicit base_ref_infot(pool_entryt entry) - : structured_pool_entryt(entry) + explicit base_ref_infot(pool_entryt entry) : structured_pool_entryt(entry) { static std::set info_tags = { CONSTANT_Fieldref, CONSTANT_Methodref, CONSTANT_InterfaceMethodref}; PRECONDITION(info_tags.find(entry.tag) != info_tags.end()); - class_index=entry.ref1; - name_and_type_index=entry.ref2; + class_index = entry.ref1; + name_and_type_index = entry.ref2; } - u1 get_class_index() const { return class_index; } - u1 get_name_and_type_index() const { return name_and_type_index; } + u1 get_class_index() const + { + return class_index; + } + u1 get_name_and_type_index() const + { + return name_and_type_index; + } name_and_type_infot get_name_and_type(pool_entry_lookupt pool_entry) const { - const pool_entryt &name_and_type_entry = - pool_entry(name_and_type_index); + const pool_entryt &name_and_type_entry = pool_entry(name_and_type_index); INVARIANT( name_and_type_entry.tag == CONSTANT_NameAndType, - "name_and_typeindex did not correspond to a name_and_type in the constants " - "pool"); + "name_and_typeindex did not correspond to a name_and_type in the " + "constant pool"); return name_and_type_infot{name_and_type_entry}; } class_infot get_class(pool_entry_lookupt pool_entry) const { - const pool_entryt &class_entry = - pool_entry(class_index); + const pool_entryt &class_entry = pool_entry(class_index); return class_infot{class_entry}; } @@ -334,9 +341,9 @@ class base_ref_infot : public structured_pool_entryt class method_handle_infot : public structured_pool_entryt { public: - - /// Correspond to the different valid values for field reference_kind -/// From Java 8 spec 4.4.8 + /// Correspond to the different valid values for field reference_kind From + /// Java 8 spec 4.4.8 + /// (https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html) enum class method_handle_kindt { REF_getField = 1, @@ -377,7 +384,6 @@ class method_handle_infot : public structured_pool_entryt case method_handle_kindt::REF_invokeVirtual: case method_handle_kindt::REF_newInvokeSpecial: { - INVARIANT(ref_entry.get_tag() == CONSTANT_Methodref, "4.4.2"); break; } @@ -386,13 +392,13 @@ class method_handle_infot : public structured_pool_entryt { INVARIANT( ref_entry.get_tag() == CONSTANT_Methodref || - ref_entry.get_tag() == CONSTANT_InterfaceMethodref, + ref_entry.get_tag() == CONSTANT_InterfaceMethodref, "4.4.2"); break; } case method_handle_kindt::REF_invokeInterface: { - INVARIANT(ref_entry.get_tag() == CONSTANT_InterfaceMethodref,""); + INVARIANT(ref_entry.get_tag() == CONSTANT_InterfaceMethodref, ""); break; } } @@ -400,7 +406,6 @@ class method_handle_infot : public structured_pool_entryt return ref_entry; } - private: method_handle_kindt reference_kind; u2 reference_index; @@ -1751,8 +1756,6 @@ void java_bytecode_parsert::parse_local_variable_type_table(methodt &method) } } - - /// Read method handle pointed to from constant pool entry at index, return type /// of method handle and name if lambda function is found. /// \param entry: the constant pool entry of the methodhandle_info structure @@ -1766,7 +1769,7 @@ java_bytecode_parsert::parse_method_handle(const method_handle_infot &entry) const base_ref_infot &ref_entry = entry.get_reference(pool_entry_lambda); - const class_infot &class_entry=ref_entry.get_class(pool_entry_lambda); + const class_infot &class_entry = ref_entry.get_class(pool_entry_lambda); const name_and_type_infot &name_and_type = ref_entry.get_name_and_type(pool_entry_lambda); @@ -1777,13 +1780,12 @@ java_bytecode_parsert::parse_method_handle(const method_handle_infot &entry) lambda_method_handlet lambda_method_handle; - if( - has_prefix(name_and_type.get_name(pool_entry_lambda), "lambda$")) + if(has_prefix(name_and_type.get_name(pool_entry_lambda), "lambda$")) { // names seem to be lambda$POSTFIX$NUM // where POSTFIX is FUN for a function name in which the lambda is define - // "static" when it is a static member of the class - // "new" when it is a class variable, instantiated in + // "static" when it is a static member of the class + // "new" when it is a class variable, instantiated in lambda_method_handle.lambda_method_name = name_and_type.get_name(pool_entry_lambda); lambda_method_handle.handle_type = @@ -1798,8 +1800,7 @@ java_bytecode_parsert::parse_method_handle(const method_handle_infot &entry) /// Read all entries of the `BootstrapMethods` attribute of a class file. /// \param parsed_class: the class representation of the class file that is /// currently parsed -void java_bytecode_parsert::read_bootstrapmethods_entry( - classt &parsed_class) +void java_bytecode_parsert::read_bootstrapmethods_entry(classt &parsed_class) { u2 num_bootstrap_methods = read_u2(); for(size_t i = 0; i < num_bootstrap_methods; i++) @@ -1810,8 +1811,13 @@ void java_bytecode_parsert::read_bootstrapmethods_entry( method_handle_infot method_handle{entry}; u2 num_bootstrap_arguments = read_u2(); - debug() << "INFO: parse BootstrapMethod handle " - << num_bootstrap_arguments << " #args" << eom; + debug() << "INFO: parse BootstrapMethod handle " << num_bootstrap_arguments + << " #args" << eom; + + // read u2 values of entry into vector + u2_valuest u2_values(num_bootstrap_arguments); + for(size_t i = 0; i < num_bootstrap_arguments; i++) + u2_values[i] = read_u2(); // try parsing bootstrap method handle if(num_bootstrap_arguments >= 3) @@ -1842,9 +1848,9 @@ void java_bytecode_parsert::read_bootstrapmethods_entry( // We read the three arguments here to see whether they correspond to // our hypotheses for this being a lambda function entry. - u2 argument_index1 = read_u2(); - u2 argument_index2 = read_u2(); - u2 argument_index3 = read_u2(); + u2 argument_index1 = u2_values[0]; + u2 argument_index2 = u2_values[1]; + u2 argument_index3 = u2_values[2]; // The additional arguments for the altmetafactory call are skipped, // as they are currently not used. We verify though that they are of @@ -1853,15 +1859,19 @@ void java_bytecode_parsert::read_bootstrapmethods_entry( bool recognized = true; for(size_t i = 3; i < num_bootstrap_arguments; i++) { - u2 skipped_argument = read_u2(); + u2 skipped_argument = u2_values[i]; recognized &= pool_entry(skipped_argument).tag == CONSTANT_Integer; } if(!recognized) { debug() << "format of BootstrapMethods entry not recognized" << eom; + lambda_method_handlet lambda_method_handle; + lambda_method_handle.handle_type = method_handle_typet::UNKNOWN_HANDLE; + lambda_method_handle.u2_values = std::move(u2_values); + parsed_class.lambda_method_handle_map[{parsed_class.name, i}] = + lambda_method_handle; return; } - const pool_entryt &interface_type_argument = pool_entry(argument_index1); const pool_entryt &method_handle_argument = pool_entry(argument_index2); const pool_entryt &method_type_argument = pool_entry(argument_index3); @@ -1870,17 +1880,25 @@ void java_bytecode_parsert::read_bootstrapmethods_entry( !(interface_type_argument.tag == CONSTANT_MethodType && method_handle_argument.tag == CONSTANT_MethodHandle && method_type_argument.tag == CONSTANT_MethodType)) + { + lambda_method_handlet lambda_method_handle; + lambda_method_handle.handle_type = method_handle_typet::UNKNOWN_HANDLE; + lambda_method_handle.u2_values = std::move(u2_values); + parsed_class.lambda_method_handle_map[{parsed_class.name, i}] = + lambda_method_handle; return; + } debug() << "INFO: parse lambda handle" << eom; optionalt lambda_method_handle = parse_method_handle(method_handle_infot{method_handle_argument}); if( - lambda_method_handle.has_value() && + !lambda_method_handle.has_value() || lambda_method_handle->handle_type != method_handle_typet::LAMBDA_METHOD_HANDLE) { + lambda_method_handle->u2_values = std::move(u2_values); error() << "ERROR: could not parse lambda function method handle" << eom; } @@ -1890,6 +1908,7 @@ void java_bytecode_parsert::read_bootstrapmethods_entry( pool_entry(interface_type_argument.ref1).s; lambda_method_handle->method_type = pool_entry(method_type_argument.ref1).s; + lambda_method_handle->u2_values = std::move(u2_values); debug() << "lambda function reference " << id2string(lambda_method_handle->lambda_method_name) << " in class \"" << parsed_class.name << "\"" @@ -1897,15 +1916,17 @@ void java_bytecode_parsert::read_bootstrapmethods_entry( << id2string(pool_entry(interface_type_argument.ref1).s) << "\n method type is " << id2string(pool_entry(method_type_argument.ref1).s) << eom; - parsed_class.lambda_method_handle_map[{parsed_class.name, i}] = - *lambda_method_handle; } + parsed_class.lambda_method_handle_map[{parsed_class.name, i}] = + *lambda_method_handle; } else { - // skip bytes to align for next entry - for(size_t i = 0; i < num_bootstrap_arguments; i++) - read_u2(); + lambda_method_handlet lambda_method_handle; + lambda_method_handle.handle_type = method_handle_typet::UNKNOWN_HANDLE; + lambda_method_handle.u2_values = std::move(u2_values); + parsed_class.lambda_method_handle_map[{parsed_class.name, i}] = + lambda_method_handle; error() << "ERROR: num_bootstrap_arguments must be at least 3" << eom; } } From ebdcfb15d78b073be32e27c6853154eeea33c088 Mon Sep 17 00:00:00 2001 From: thk123 Date: Fri, 23 Feb 2018 17:53:14 +0000 Subject: [PATCH 14/18] Adding utiltiy functions required for unit tests --- unit/testing-utils/require_parse_tree.cpp | 117 ++++++++++++++++++++++ unit/testing-utils/require_parse_tree.h | 60 +++++++++++ 2 files changed, 177 insertions(+) create mode 100644 unit/testing-utils/require_parse_tree.cpp create mode 100644 unit/testing-utils/require_parse_tree.h diff --git a/unit/testing-utils/require_parse_tree.cpp b/unit/testing-utils/require_parse_tree.cpp new file mode 100644 index 00000000000..70922d4b55a --- /dev/null +++ b/unit/testing-utils/require_parse_tree.cpp @@ -0,0 +1,117 @@ +/*******************************************************************\ + + Module: Unit test utilities + + Author: DiffBlue Limited. All rights reserved. + +\*******************************************************************/ + +#include "require_parse_tree.h" + +/// Find in the parsed class a specific entry within the +/// lambda_method_handle_map with a matching descriptor. Will fail if no +/// matching lambda entry found. +/// \param parsed_class: the class to inspect +/// \param descriptor: the descriptor the lambda method should have +/// \param entry_index: the number to skip (i.e. if multiple entries with the +/// same descriptor +/// \return +require_parse_tree::lambda_method_handlet +require_parse_tree::require_lambda_entry_for_descriptor( + const java_bytecode_parse_treet::classt &parsed_class, + const std::string &descriptor, + const size_t entry_index) +{ + REQUIRE(entry_index < parsed_class.lambda_method_handle_map.size()); + typedef java_bytecode_parse_treet::classt::lambda_method_handle_mapt:: + value_type lambda_method_entryt; + + size_t matches_found = 0; + + const auto matching_lambda_entry = std::find_if( + parsed_class.lambda_method_handle_map.begin(), + parsed_class.lambda_method_handle_map.end(), + [&descriptor, &matches_found, &entry_index]( + const lambda_method_entryt &entry) { + if(entry.second.method_type == descriptor) + { + ++matches_found; + return matches_found == entry_index + 1; + } + return false; + }); + + INFO("Looking for descriptor: " << descriptor); + std::ostringstream found_entries; + for(const auto entry : parsed_class.lambda_method_handle_map) + { + found_entries << id2string(entry.first.first) << ": " + << id2string(entry.second.method_type) << std::endl; + } + INFO("Found descriptors:\n" << found_entries.str()); + + REQUIRE(matching_lambda_entry != parsed_class.lambda_method_handle_map.end()); + + return matching_lambda_entry->second; +} + +/// Finds a specific method in the parsed class with a matching name. +/// \param parsed_class: The class +/// \param method_name: The name of the method to look for +/// \return The methodt structure with the corresponding name +const require_parse_tree::methodt require_parse_tree::require_method( + const java_bytecode_parse_treet::classt &parsed_class, + const irep_idt &method_name) +{ + const auto method = std::find_if( + parsed_class.methods.begin(), + parsed_class.methods.end(), + [&method_name](const java_bytecode_parse_treet::methodt &method) { + return method.name == method_name; + }); + + INFO("Looking for method: " << method_name); + std::ostringstream found_methods; + for(const auto entry : parsed_class.methods) + { + found_methods << id2string(entry.name) << std::endl; + } + INFO("Found methods:\n" << found_methods.str()); + + REQUIRE(method != parsed_class.methods.end()); + + return *method; +} + +/// Verify whether a given methods instructions match an expectation +/// \param expected_instructions: The expected instructions for a given method +/// \param instructions: The instructions of a method +void require_parse_tree::require_instructions_match_expectation( + const expected_instructionst &expected_instructions, + const java_bytecode_parse_treet::methodt::instructionst instructions) +{ + REQUIRE(instructions.size() == expected_instructions.size()); + auto actual_instruction_it = instructions.begin(); + for(const auto expected_instruction : expected_instructions) + { + expected_instruction.require_instructions_equal(*actual_instruction_it); + ++actual_instruction_it; + } +} + +/// Check whether a given instruction matches an expectation of the instruction +/// \param actual_instruction: The instruction to check +void require_parse_tree::expected_instructiont::require_instructions_equal( + java_bytecode_parse_treet::instructiont actual_instruction) const +{ + REQUIRE(actual_instruction.statement == instruction_mnemoic); + REQUIRE(actual_instruction.args.size() == instruction_arguments.size()); + auto actual_arg_it = actual_instruction.args.begin(); + for(const exprt &expected_arg : actual_instruction.args) + { + INFO("Expected argument" << expected_arg.pretty()); + INFO("Actual argument" << actual_arg_it->pretty()); + REQUIRE(*actual_arg_it == expected_arg); + ++actual_arg_it; + } +} diff --git a/unit/testing-utils/require_parse_tree.h b/unit/testing-utils/require_parse_tree.h new file mode 100644 index 00000000000..35b3ab195d2 --- /dev/null +++ b/unit/testing-utils/require_parse_tree.h @@ -0,0 +1,60 @@ +/*******************************************************************\ + + Module: Unit test utilities + + Author: DiffBlue Limited. All rights reserved. + +\*******************************************************************/ + +/// \file +/// Utilties for inspecting java_parse_treet + +#ifndef CPROVER_TESTING_UTILS_REQUIRE_PARSE_TREE_H +#define CPROVER_TESTING_UTILS_REQUIRE_PARSE_TREE_H + +#include +#include "catch.hpp" + +// NOLINTNEXTLINE(readability/namespace) +namespace require_parse_tree +{ +typedef java_bytecode_parse_treet::classt::lambda_method_handlet + lambda_method_handlet; + +lambda_method_handlet require_lambda_entry_for_descriptor( + const java_bytecode_parse_treet::classt &parsed_class, + const std::string &descriptor, + const size_t entry_index = 0); + +typedef java_bytecode_parse_treet::methodt methodt; + +const methodt require_method( + const java_bytecode_parse_treet::classt &parsed_class, + const irep_idt &method_name); + +struct expected_instructiont +{ + expected_instructiont( + const irep_idt &instruction_mnemoic, + const std::vector &instruction_arguments) + : instruction_mnemoic(instruction_mnemoic), + instruction_arguments(instruction_arguments) + { + } + + void require_instructions_equal( + java_bytecode_parse_treet::instructiont actual_instruction) const; + +private: + const irep_idt instruction_mnemoic; + const std::vector instruction_arguments; +}; + +typedef std::vector expected_instructionst; + +void require_instructions_match_expectation( + const expected_instructionst &expected_instructions, + const java_bytecode_parse_treet::methodt::instructionst instructions); +} + +#endif //CPROVER_TESTING_UTILS_REQUIRE_PARSE_TREE_H From ba8b958e4c42fcd74e18028c46c809c48d9ef8dc Mon Sep 17 00:00:00 2001 From: thk123 Date: Fri, 23 Feb 2018 18:01:48 +0000 Subject: [PATCH 15/18] Adding tests for static lambdas --- ...ava_bytecode_parse_lambda_method_table.cpp | 278 ++++++++++++++++++ .../ArrayParameterLambda.class | Bin 0 -> 257 bytes .../lambda_examples/DummyGeneric.class | Bin 0 -> 441 bytes .../lambda_examples/LambdaInterfaces.java | 39 +++ .../lambda_examples/ParameterLambda.class | Bin 0 -> 246 bytes .../ReturningLambdaPrimitive.class | Bin 0 -> 142 bytes .../ReturningLambdaPrimitiveArray.class | Bin 0 -> 148 bytes .../ReturningLambdaReference.class | Bin 0 -> 159 bytes .../ReturningLambdaReferenceArray.class | Bin 0 -> 165 bytes .../ReturningLambdaSpecalisedGeneric.class | Bin 0 -> 223 bytes ...eturningLambdaSpecalisedGenericArray.class | Bin 0 -> 230 bytes .../lambda_examples/SimpleLambda.class | Bin 0 -> 130 bytes .../lambda_examples/StaticLambdas.class | Bin 0 -> 4245 bytes .../lambda_examples/StaticLambdas.java | 37 +++ 14 files changed, 354 insertions(+) create mode 100644 unit/java_bytecode/java_bytecode_parser/java_bytecode_parse_lambda_method_table.cpp create mode 100644 unit/java_bytecode/java_bytecode_parser/lambda_examples/ArrayParameterLambda.class create mode 100644 unit/java_bytecode/java_bytecode_parser/lambda_examples/DummyGeneric.class create mode 100644 unit/java_bytecode/java_bytecode_parser/lambda_examples/LambdaInterfaces.java create mode 100644 unit/java_bytecode/java_bytecode_parser/lambda_examples/ParameterLambda.class create mode 100644 unit/java_bytecode/java_bytecode_parser/lambda_examples/ReturningLambdaPrimitive.class create mode 100644 unit/java_bytecode/java_bytecode_parser/lambda_examples/ReturningLambdaPrimitiveArray.class create mode 100644 unit/java_bytecode/java_bytecode_parser/lambda_examples/ReturningLambdaReference.class create mode 100644 unit/java_bytecode/java_bytecode_parser/lambda_examples/ReturningLambdaReferenceArray.class create mode 100644 unit/java_bytecode/java_bytecode_parser/lambda_examples/ReturningLambdaSpecalisedGeneric.class create mode 100644 unit/java_bytecode/java_bytecode_parser/lambda_examples/ReturningLambdaSpecalisedGenericArray.class create mode 100644 unit/java_bytecode/java_bytecode_parser/lambda_examples/SimpleLambda.class create mode 100644 unit/java_bytecode/java_bytecode_parser/lambda_examples/StaticLambdas.class create mode 100644 unit/java_bytecode/java_bytecode_parser/lambda_examples/StaticLambdas.java diff --git a/unit/java_bytecode/java_bytecode_parser/java_bytecode_parse_lambda_method_table.cpp b/unit/java_bytecode/java_bytecode_parser/java_bytecode_parse_lambda_method_table.cpp new file mode 100644 index 00000000000..91fc9c18df2 --- /dev/null +++ b/unit/java_bytecode/java_bytecode_parser/java_bytecode_parse_lambda_method_table.cpp @@ -0,0 +1,278 @@ +/*******************************************************************\ + + Module: Unit tests for parsing generic classes + + Author: DiffBlue Limited. All rights reserved. + +\*******************************************************************/ + +#include +#include +#include + +#include + +#include +#include + +#include +#include + +typedef java_bytecode_parse_treet::classt::lambda_method_handlet + lambda_method_handlet; + +SCENARIO( + "lambda_method_handle_map with static lambdas", + "[core][java_bytecode][java_bytecode_parse_lambda_method_handle]") +{ + null_message_handlert message_handler; + GIVEN("A class with a static lambda variables") + { + java_bytecode_parse_treet parse_tree; + java_bytecode_parse( + "./java_bytecode/java_bytecode_parser/lambda_examples/" + "StaticLambdas.class", + parse_tree, + message_handler); + WHEN("Parsing that class") + { + REQUIRE(parse_tree.loading_successful); + const java_bytecode_parse_treet::classt parsed_class = + parse_tree.parsed_class; + REQUIRE(parsed_class.attribute_bootstrapmethods_read); + REQUIRE(parsed_class.lambda_method_handle_map.size() == 12); + + // Simple lambdas + THEN( + "There should be an entry for the lambda that has no parameters or " + "returns and the method it references should have an appropriate " + "descriptor") + { + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, "()V"); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == "()V"); + } + + // Parameter lambdas + THEN( + "There should be an entry for the lambda that takes parameters and the " + "method it references should have an appropriate descriptor") + { + std::string descriptor = "(ILjava/lang/Object;LDummyGeneric;)V"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + THEN( + "There should be an entry for the lambda that takes array parameters " + "and the method it references should have an appropriate descriptor") + { + std::string descriptor = "([I[Ljava/lang/Object;[LDummyGeneric;)V"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + + // Return lambdas + THEN( + "There should be an entry for the lambda that returns a primitive and " + "the method it references should have an appropriate descriptor") + { + std::string descriptor = "()I"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + THEN( + "There should be an entry for the lambda that returns a reference type " + "and the method it references should have an appropriate descriptor") + { + std::string descriptor = "()Ljava/lang/Object;"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + THEN( + "There should be an entry for the lambda that returns a specialised " + "generic type and the method it references should have an appropriate " + "descriptor") + { + std::string descriptor = "()LDummyGeneric;"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + + // Array returning lambdas + THEN( + "There should be an entry for the lambda that returns an array of " + "primitives and the method it references should have an appropriate " + "descriptor") + { + std::string descriptor = "()[I"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + THEN( + "There should be an entry for the lambda that returns an array of " + "reference types and the method it references should have an " + "appropriate descriptor") + { + std::string descriptor = "()[Ljava/lang/Object;"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + THEN( + "There should be an entry for the lambda that returns an array of " + "specialised generic types and the method it references should have an " + "appropriate descriptor") + { + std::string descriptor = "()[LDummyGeneric;"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + + // Capturing lamdbas + THEN( + "There should be an entry for the lambda that returns a primitive and " + "the method it references should have an appropriate descriptor") + { + std::string descriptor = "()I"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor, 1); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + + const typet primitive_type = java_int_type(); + + fieldref_exprt fieldref{ + primitive_type, "staticPrimitive", "java::StaticLambdas"}; + + std::vector + expected_instructions{{"getstatic", {fieldref}}, {"ireturn", {}}}; + + require_parse_tree::require_instructions_match_expectation( + expected_instructions, lambda_method.instructions); + } + THEN( + "There should be an entry for the lambda that returns a reference type " + "and the method it references should have an appropriate descriptor") + { + std::string descriptor = "()Ljava/lang/Object;"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor, 1); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + + const reference_typet dummy_generic_reference_type = + java_reference_type(symbol_typet{"java::java.lang.Object"}); + + fieldref_exprt fieldref{dummy_generic_reference_type, + "staticReference", + "java::StaticLambdas"}; + + std::vector + expected_instructions{{"getstatic", {fieldref}}, {"areturn", {}}}; + + require_parse_tree::require_instructions_match_expectation( + expected_instructions, lambda_method.instructions); + } + THEN( + "There should be an entry for the lambda that returns a specialised " + "generic type and the method it references should have an appropriate " + "descriptor") + { + std::string descriptor = "()LDummyGeneric;"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor, 1); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const java_bytecode_parse_treet::methodt &lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + + const reference_typet dummy_generic_reference_type = + java_reference_type(symbol_typet{"java::DummyGeneric"}); + + fieldref_exprt fieldref{dummy_generic_reference_type, + "staticSpecalisedGeneric", + "java::StaticLambdas"}; + + std::vector + expected_instructions{{"getstatic", {fieldref}}, {"areturn", {}}}; + + require_parse_tree::require_instructions_match_expectation( + expected_instructions, lambda_method.instructions); + } + } + } +} diff --git a/unit/java_bytecode/java_bytecode_parser/lambda_examples/ArrayParameterLambda.class b/unit/java_bytecode/java_bytecode_parser/lambda_examples/ArrayParameterLambda.class new file mode 100644 index 0000000000000000000000000000000000000000..56886405324c913f2c575d1c43883475ceb96bdf GIT binary patch literal 257 zcmX^0Z`VEs1_l!bZgvJvb_Om+26oqq)a25VR7M7MjcCtkpRB~PME#t^ymWp4q^#8B z66^(-3?hz2MTwOGiA9OIsU@jJ5J^S`0d$A5 gF)%PPFabRWq*)jlm>F1rd{!WjiGdBsVh6K00L$J+;Q#;t literal 0 HcmV?d00001 diff --git a/unit/java_bytecode/java_bytecode_parser/lambda_examples/DummyGeneric.class b/unit/java_bytecode/java_bytecode_parser/lambda_examples/DummyGeneric.class new file mode 100644 index 0000000000000000000000000000000000000000..afbff9f82d17273772666e3ff3a8b8ecad8262cb GIT binary patch literal 441 zcmZXQy-ve06orrTOG9W&3T0&s40K_FC=vp}62XwDyOWsWDnBSqkos6mNDMpx4~4i1 zMIv;t?>(Q7ua5os_5K0i97h&Z9GEyXF=fynrN~kSCs^_qKFfHqm_5Wxk<^zAgHSFC zUN@CsP@{;5;mJbQK1ugv&Y)eFDV>c1S%~{4k3|*nI3wZ&Wx}&LuVkw?P^+KhieVVs zGfUcff&aBzXHcl=+gj3OM_0;piY6=5t>2OB63Bw_2{Q>V6W7u*rna*pbMLN W`^SJi3`vc8WK2rgjNXUSsmV7GwqRfY literal 0 HcmV?d00001 diff --git a/unit/java_bytecode/java_bytecode_parser/lambda_examples/LambdaInterfaces.java b/unit/java_bytecode/java_bytecode_parser/lambda_examples/LambdaInterfaces.java new file mode 100644 index 00000000000..388d8230df6 --- /dev/null +++ b/unit/java_bytecode/java_bytecode_parser/lambda_examples/LambdaInterfaces.java @@ -0,0 +1,39 @@ +interface SimpleLambda { + public void Execute(); +} + +interface ParameterLambda { + public void Execute(int primitive, Object reference, DummyGeneric specalisedGeneric); +} + +interface ArrayParameterLambda { + public void Execute(int[] primitive, Object[] reference, DummyGeneric[] specalisedGeneric); +} + +interface ReturningLambdaPrimitive { + public int Execute(); +} + +interface ReturningLambdaReference { + public Object Execute(); +} + +interface ReturningLambdaSpecalisedGeneric { + public DummyGeneric Execute(); +} + +interface ReturningLambdaPrimitiveArray { + public int[] Execute(); +} + +interface ReturningLambdaReferenceArray { + public Object[] Execute(); +} + +interface ReturningLambdaSpecalisedGenericArray { + public DummyGeneric[] Execute(); +} + +class DummyGeneric { + T field; +} diff --git a/unit/java_bytecode/java_bytecode_parser/lambda_examples/ParameterLambda.class b/unit/java_bytecode/java_bytecode_parser/lambda_examples/ParameterLambda.class new file mode 100644 index 0000000000000000000000000000000000000000..9d52eef3aa72bdb9061f76c21c3831047587bba9 GIT binary patch literal 246 zcmX^0Z`VEs1_l!bZgvJvb_Om+26oqq)a25VR7M6B4Nsq}#Ii*FoW#6zegCAa)Z`Lt zAD7bH+)DSx1DnGYoL^d$ zoa&aD12jk|xFoS8GubCGHz_5tSP$e3Mh58YI0&uW^rl?)N03~qQpu@1_6Wx{z+M> j$t7$I42%p+Kpy~U7DfhU1{NTn706>^U<0z)!7L5{4Q@U2 literal 0 HcmV?d00001 diff --git a/unit/java_bytecode/java_bytecode_parser/lambda_examples/SimpleLambda.class b/unit/java_bytecode/java_bytecode_parser/lambda_examples/SimpleLambda.class new file mode 100644 index 0000000000000000000000000000000000000000..ff1dae8ba4be47077bef82c1f7362bd259030c0e GIT binary patch literal 130 zcmX^0Z`VEs1_l!bPId-%b_Nbc26oqq)a25VR7M774b3n{2Cm@z(xT*4x6B-%kWg?* zVo7GQPhxITN@B5IR$^HqBLh!xW^O@FDnyWxK>#GGpOcuEuJ50em6}|_#=yYHzy!1f TNV5QqWnclZ7+8TM69XFnp>Z7o literal 0 HcmV?d00001 diff --git a/unit/java_bytecode/java_bytecode_parser/lambda_examples/StaticLambdas.class b/unit/java_bytecode/java_bytecode_parser/lambda_examples/StaticLambdas.class new file mode 100644 index 0000000000000000000000000000000000000000..a5d959f57812675dfa7d5f851dfec18ef5028547 GIT binary patch literal 4245 zcmbVP340sW6+NRk9$T7NvYed|lHk;K(!@!~ibH@H0;Wm`wi{^E0AqRLOd?BSG!B@3 z-`6hC7ASky6o@H{R0un?DP?V0TK45{@Nw=NX>83n@%LeWZ|-^ToqOMXZ|=SGbnx5K z0RSuUO%yHIU5_7NPYf9h3d+WiLtfB%F*qm)a$^|6UP0%_Z~=w|T^PefxLD97FNHC;Qs)v*U{*WPPs(7OE5ZfmFISlKj3 zld0Y2Ub8)8W(V3&@7ryq-3|>iJmsFel{PbW!RkN7%36+{*3ghTX=re8xZEeTzQ-QO zn(mN8TSr$-t{-E$E9+VVmea9;8b-k$%xA2WIoQ{4ip8EWea^@7reh9PrW;dRrE9sC z;|-CKFdfGnR^t`g^wf#|SQU4gW6`;+e-0Ie*{NP-&jG z5)FpVt)jmmxM+w;JF^2GtBqAG92Kb_@(SFff~ec_>ZQDvzm=tyWQ6^G^o z_Tz1()69E8Q=ax62~4*f+MfWkcD6Zs<++B7-*gYz3Jktf9N7S_n?q#XMKkQrWRRcPszp^;-k zBP%qtv~KSjXXSv)@>=TiqZKJ4F4Q}t#i(f<8{B&#T!-QT(;=t$YMNWS!iQ0Ph~iP; z82Ps9)~7S&Sc>-KhMcsu$ySk56&y?DpbeaiO$L@>xsFE-JSLqLI7Y|g(yzH{jdJFMdJd?mCilP`OC14T% zUGTFB>>z$l@PP!niJuqzLIPWfUljaO0$Ye*7W_&AXA!?D__YMK5x*|@9|`ml|5NZm zf`75S!S-(rQ>xC6?ghh?ABM3X0uB1f7g*^bS8_P;31B{{;u0F3*?owbt|2((*Bch@ zfL5rIX*e=Ksjj-=dEdJ7lT|gKA6i23qIeT;MR@~w+p8;M&!a=e-_g*-N)p(&ntGR8 zD|4Lqk?1c8CxbJJ2ALjaNDqj`xgK>%e!lT3w zHME5-hE^X{_(+cJfAaXhj*ks|f-em0lq~I+?EINz<#x%&pGp?~M6&Np$+{m)w)IGs zoguTQ%j{-s(Xe1NZtd*e+#ajl`&KZ?%$;W1%{jxo!tlOQ>FM}NLv1RT+cT7BjCW;u zlj_Wvg@VQJ%^R&_Dpc8?m#(Lp+5U`GX!aC4#?X3Phj)yzBUOsM!+GobO?pcnUU%$g zth{WenaT9nuEhfOH6uNAKI832!`#F3FrTlih4D#>!IBrC0wEV4?nzADMmswAtbk}Rl7vZgA@vPv1}S}gK>p2A)`rA9e< z(bLfO6GU(-pS5aGLl?2(5pWuQ#9lp8zVb7?#LHiP1d$>pX&6C`u4zR~t|6?|1nL6o zim2Cc<_Mxi#1t@!n4&;K5pe|)MKmgqETTz)sTHkh6|L!>miVbDKUZT4@eCf?Of+E@ z=3+LF`3N529B%qZepBYs>!Z-i7q9~hm?}r}S5I>yD$!3Has+eK{(zbh%y`_l?43Xp znM{^2b7qj4B$K%nX3p9du-Ql^Q?bmPU0yw+1ue$OW8O~4YaowFJ|S-kd8`N%@(l7= zJ|^VF$YZSu@@j(njgrU0ROU&DPLdM|23x%fwt3zp&c-<;$!k6rCZB@(kVb^YSn4J+ zRu?cP3FjGG>>FF6jInr@jm=V=vS(g+c|o8vyas&QBgz0^mKbYz0+ABqjk2c1NTRGQ zG43eqN{l_qXo>MhX_PQeWJ8IuNSP=x9x0P0#w6v`5}HL$D`BC?>7!A;f;kBOLayU~ zf*ClIKac0}*KsSymoWp@qJdYbI5VP=?=8t#XyP&MqD}-hKBtV*=}++`K4XsD%>ISc zyoH=waT}YUJMeQhLBGVW*aY2$U$Y7NE$(I$bT59#Cg^@Vz$WM+Jj^EO_xJ;wpu6xV MU-!?x?q6{4cZk;x7ytkO literal 0 HcmV?d00001 diff --git a/unit/java_bytecode/java_bytecode_parser/lambda_examples/StaticLambdas.java b/unit/java_bytecode/java_bytecode_parser/lambda_examples/StaticLambdas.java new file mode 100644 index 00000000000..697b2209f47 --- /dev/null +++ b/unit/java_bytecode/java_bytecode_parser/lambda_examples/StaticLambdas.java @@ -0,0 +1,37 @@ +public class StaticLambdas { + + static int staticPrimitive; + static Object staticReference; + static DummyGeneric staticSpecalisedGeneric; + + static SimpleLambda simpleLambda = () -> { /*NOP*/ }; + static ParameterLambda paramLambda = (int primitive, Object reference, DummyGeneric specalisedGeneric) -> {}; + static ArrayParameterLambda arrayParamLambda = (int[] primitive, Object[] reference, DummyGeneric[] specalisedGeneric) -> {}; + static ReturningLambdaPrimitive returnPrimitiveLambda = () -> { return 1; }; + static ReturningLambdaReference returnReferenceLambda = () -> { return null; }; + static ReturningLambdaSpecalisedGeneric returningSpecalisedGenericLambda = () -> { return null; }; + static ReturningLambdaPrimitiveArray returnPrimitiveArrayLambda = () -> { return null; }; + static ReturningLambdaReferenceArray returnReferenceArrayLambda = () -> { return null; }; + static ReturningLambdaSpecalisedGenericArray returningSpecalisedGenericArrayLambda = () -> { return null; }; + + static ReturningLambdaPrimitive returnPrimitiveLambdaCapture = () -> { return staticPrimitive; }; + static ReturningLambdaReference returnReferenceLambdaCapture = () -> { return staticReference; }; + static ReturningLambdaSpecalisedGeneric returningSpecalisedGenericLambdaCapture = () -> { return staticSpecalisedGeneric; }; + + + public static void testMethod() { + simpleLambda.Execute(); + paramLambda.Execute(4, null, null); + arrayParamLambda.Execute(null, null, null); + returnPrimitiveLambda.Execute(); + returnReferenceLambda.Execute(); + returningSpecalisedGenericLambda.Execute(); + returnPrimitiveArrayLambda.Execute(); + returnReferenceArrayLambda.Execute(); + returningSpecalisedGenericArrayLambda.Execute(); + + returnPrimitiveLambdaCapture.Execute(); + returnReferenceLambdaCapture.Execute(); + returningSpecalisedGenericLambdaCapture.Execute(); + } +} From db1f3b5b159281ceeaf21e939bbf2b02f791cf5e Mon Sep 17 00:00:00 2001 From: thk123 Date: Fri, 23 Feb 2018 18:03:37 +0000 Subject: [PATCH 16/18] Adding tests for local lambdas --- ...ava_bytecode_parse_lambda_method_table.cpp | 249 ++++++++++++++++++ .../lambda_examples/LocalLambdas.class | Bin 0 -> 4350 bytes .../lambda_examples/LocalLambdas.java | 59 +++++ 3 files changed, 308 insertions(+) create mode 100644 unit/java_bytecode/java_bytecode_parser/lambda_examples/LocalLambdas.class create mode 100644 unit/java_bytecode/java_bytecode_parser/lambda_examples/LocalLambdas.java diff --git a/unit/java_bytecode/java_bytecode_parser/java_bytecode_parse_lambda_method_table.cpp b/unit/java_bytecode/java_bytecode_parser/java_bytecode_parse_lambda_method_table.cpp index 91fc9c18df2..a8e2fa6adab 100644 --- a/unit/java_bytecode/java_bytecode_parser/java_bytecode_parse_lambda_method_table.cpp +++ b/unit/java_bytecode/java_bytecode_parser/java_bytecode_parse_lambda_method_table.cpp @@ -276,3 +276,252 @@ SCENARIO( } } } +SCENARIO( + "lambda_method_handle_map with local lambdas", + "[core][java_bytecode][java_bytecode_parse_lambda_method_handle]") +{ + null_message_handlert message_handler; + GIVEN("A method with local lambdas") + { + java_bytecode_parse_treet parse_tree; + java_bytecode_parse( + "./java_bytecode/java_bytecode_parser/lambda_examples/" + "LocalLambdas.class", + parse_tree, + message_handler); + WHEN("Parsing that class") + { + REQUIRE(parse_tree.loading_successful); + const java_bytecode_parse_treet::classt parsed_class = + parse_tree.parsed_class; + REQUIRE(parsed_class.attribute_bootstrapmethods_read); + REQUIRE(parsed_class.lambda_method_handle_map.size() == 12); + + // Simple lambdas + THEN( + "There should be an entry for the lambda that has no parameters or " + "returns and the method it references should have an appropriate " + "descriptor") + { + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, "()V"); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == "()V"); + } + + // Parameter lambdas + THEN( + "There should be an entry for the lambda that takes parameters and the " + "method it references should have an appropriate descriptor") + { + std::string descriptor = "(ILjava/lang/Object;LDummyGeneric;)V"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + THEN( + "There should be an entry for the lambda that takes array parameters " + "and the method it references should have an appropriate descriptor") + { + std::string descriptor = "([I[Ljava/lang/Object;[LDummyGeneric;)V"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + + // Return lambdas + THEN( + "There should be an entry for the lambda that returns a primitive and " + "the method it references should have an appropriate descriptor") + { + std::string descriptor = "()I"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + THEN( + "There should be an entry for the lambda that returns a reference type " + "and the method it references should have an appropriate descriptor") + { + std::string descriptor = "()Ljava/lang/Object;"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + THEN( + "There should be an entry for the lambda that returns a specialised " + "generic type and the method it references should have an appropriate " + "descriptor") + { + std::string descriptor = "()LDummyGeneric;"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + + // Array returning lambdas + THEN( + "There should be an entry for the lambda that returns an array of " + "primitives and the method it references should have an appropriate " + "descriptor") + { + std::string descriptor = "()[I"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + THEN( + "There should be an entry for the lambda that returns an array of " + "reference types and the method it references should have an " + "appropriate descriptor") + { + std::string descriptor = "()[Ljava/lang/Object;"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + THEN( + "There should be an entry for the lambda that returns an array of " + "specialised generic types and the method it references should have an " + "appropriate descriptor") + { + std::string descriptor = "()[LDummyGeneric;"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + + // Capturing lamdbas + THEN( + "There should be an entry for the lambda that returns a primitive " + "local variable and the method it references should have an " + "appropriate descriptor") + { + std::string descriptor = "()I"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor, 1); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + // Note here the descriptor of the implementation is different - the + // implementation requries the input to be passed in + REQUIRE(id2string(lambda_method.descriptor) == "(I)I"); + + std::vector + expected_instructions{{"iload_0", {}}, {"ireturn", {}}}; + + require_parse_tree::require_instructions_match_expectation( + expected_instructions, lambda_method.instructions); + } + THEN( + "There should be an entry for the lambda that returns a reference type " + "local variable and the method it references should have an " + "appropriate descriptor") + { + // Since it is a local variable, the corresponding method takes the + // captured variable as an input + std::string descriptor = "()Ljava/lang/Object;"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor, 1); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE( + id2string(lambda_method.descriptor) == + "(Ljava/lang/Object;)Ljava/lang/Object;"); + + std::vector + expected_instructions{{"aload_0", {}}, {"areturn", {}}}; + + require_parse_tree::require_instructions_match_expectation( + expected_instructions, lambda_method.instructions); + } + THEN( + "There should be an entry for the lambda that returns a specialised " + "generic type local variable and the method it references should have " + "an appropriate descriptor") + { + // Since it is a local variable, the corresponding method takes the + // captured variable as an input + std::string descriptor = "()LDummyGeneric;"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor, 1); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const java_bytecode_parse_treet::methodt &lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE( + id2string(lambda_method.descriptor) == + "(LDummyGeneric;)LDummyGeneric;"); + + // since just returning the parameter, nothing to put on the stack + std::vector + expected_instructions{{"aload_0", {}}, {"areturn", {}}}; + + require_parse_tree::require_instructions_match_expectation( + expected_instructions, lambda_method.instructions); + } + } + } +} diff --git a/unit/java_bytecode/java_bytecode_parser/lambda_examples/LocalLambdas.class b/unit/java_bytecode/java_bytecode_parser/lambda_examples/LocalLambdas.class new file mode 100644 index 0000000000000000000000000000000000000000..28f9f1d7990979df4b7781ce71c69559b98854de GIT binary patch literal 4350 zcmbVP`F9)D75+w+J+?Hq6`6!sY)Npi9FjN-gdm5c;Dn$?&Snz`Vd+?&B$LRJ7>z>= zEn8b!Xjxh)`@Sz>UnL+cWocRe7=GvUyED>QGvmbP)IOSf=iTqFbKjje-~C!U4&Xfe z9K%9flO#W$gaRXp2o5IEg2PEfQB0y0*C){iI|&sxB+-r=ljy+jlZfHwB;vR=j@xj1 z9Dl^0;`lS}h~qE#YaDmtt~maNzsGSm?up}G+!x0`@J|)@t9U>`+Xk~}+8Y(LqzCpW zhz^$~31us)UWxakg<4pf!TwJ)6w=i@K4#N#UiA8u^4G-c(DOHG-t^O{HKN~w;9 zUZhyhOG+iXVq1E7#IO&RCMw-+_*Vick_RxheEe-gg2+pq=W4d!$+vb za4KpF)GbGi{FKe2Nw4{4y&yhT(8KlMkQdP>3Kml2%#qpTpK^FmzWYRTTx=Yl72^$? zA;EkmJrS}Knr9C7a|MgT*uW4LqhHJl^c=gxQGj167zkSod7n}Ew}LY`FHWBKR|;0n zI*kI=@&a_r=BmqVL!9cFCINp@=7-#+`~Ec@oT( zN{6S)tbMk~lh$xSuT%`4?tW-~>UDeWD7s89P85tvpQ||3Gy_L%i>H?66`$f5U#0(_ z&~-a7g zFKM?pXxjLGCE1L#+3n|G3MDKEQ=Ij*L^{hiN;4PicP%V~OHp+U#T5 z+fc<)+K$lwyXdFy7RGXno|X}n!V5ST%NgflT3$iFZA3go->X>XLEK00YnZ`0eps!g z_YTH;J0n=fj5gyY#<89y&!NIdR?@>54l|xrEc?~SV~Dz4*uZ$C0o+I~LL?EBjms73 zp5>&{J&|Q^$+f9e*ujj9?}@sx zUI#;&Y`x|L+8NV6u0_~_-;u^B{SSV?f4SkV<^7Fp#_Ndr9d!PL%VUB+;r8PIf5MqK r#Gh~lO!6mO31$9-v)J&K!%cCMPC49BOyj7-ncNr+-9LD`x1jVttGFb^ literal 0 HcmV?d00001 diff --git a/unit/java_bytecode/java_bytecode_parser/lambda_examples/LocalLambdas.java b/unit/java_bytecode/java_bytecode_parser/lambda_examples/LocalLambdas.java new file mode 100644 index 00000000000..181662018ee --- /dev/null +++ b/unit/java_bytecode/java_bytecode_parser/lambda_examples/LocalLambdas.java @@ -0,0 +1,59 @@ +public class LocalLambdas { + public static void test() { + int localPrimitive = 5; + Object localReference = null; + DummyGeneric localSpecalisedGeneric = null; + + // Declare some local lambdas + SimpleLambda simpleLambda = () -> { /*NOP*/ }; + + ParameterLambda paramLambda = + (int primitive, Object reference, DummyGeneric specalisedGeneric) -> {}; + ArrayParameterLambda arrayParamLambda = + (int[] primitive, Object[] reference, DummyGeneric[] specalisedGeneric) -> {}; + + ReturningLambdaPrimitive returnPrimitiveLambda = () -> { + return 1; + }; + ReturningLambdaReference returnReferenceLambda = () -> { + return null; + }; + ReturningLambdaSpecalisedGeneric returningSpecalisedGenericLambda = () -> { + return null; + }; + + ReturningLambdaPrimitiveArray returnPrimitiveArrayLambda = () -> { + return null; + }; + ReturningLambdaReferenceArray returnReferenceArrayLambda = () -> { + return null; + }; + ReturningLambdaSpecalisedGenericArray returningSpecalisedGenericArrayLambda = () -> { + return null; + }; + + ReturningLambdaPrimitive returnPrimitiveLambdaCapture = () -> { + return localPrimitive; + }; + ReturningLambdaReference returnReferenceLambdaCapture = () -> { + return localReference; + }; + ReturningLambdaSpecalisedGeneric returningSpecalisedGenericLambdaCapture = () -> { + return localSpecalisedGeneric; + }; + + simpleLambda.Execute(); + paramLambda.Execute(4, null, null); + arrayParamLambda.Execute(null, null, null); + returnPrimitiveLambda.Execute(); + returnReferenceLambda.Execute(); + returningSpecalisedGenericLambda.Execute(); + returnPrimitiveArrayLambda.Execute(); + returnReferenceArrayLambda.Execute(); + returningSpecalisedGenericArrayLambda.Execute(); + + returnPrimitiveLambdaCapture.Execute(); + returnReferenceLambdaCapture.Execute(); + returningSpecalisedGenericLambdaCapture.Execute(); + } +} From 9b8a73eebb86746c5187fe79e53e97b3f7af2039 Mon Sep 17 00:00:00 2001 From: thk123 Date: Fri, 23 Feb 2018 18:05:20 +0000 Subject: [PATCH 17/18] Adding tests for lambdas as member variables --- ...ava_bytecode_parse_lambda_method_table.cpp | 272 ++++++++++++++++++ .../lambda_examples/MemberLambdas.class | Bin 0 -> 4426 bytes .../lambda_examples/MemberLambdas.java | 38 +++ 3 files changed, 310 insertions(+) create mode 100644 unit/java_bytecode/java_bytecode_parser/lambda_examples/MemberLambdas.class create mode 100644 unit/java_bytecode/java_bytecode_parser/lambda_examples/MemberLambdas.java diff --git a/unit/java_bytecode/java_bytecode_parser/java_bytecode_parse_lambda_method_table.cpp b/unit/java_bytecode/java_bytecode_parser/java_bytecode_parse_lambda_method_table.cpp index a8e2fa6adab..ef68d918bd3 100644 --- a/unit/java_bytecode/java_bytecode_parser/java_bytecode_parse_lambda_method_table.cpp +++ b/unit/java_bytecode/java_bytecode_parser/java_bytecode_parse_lambda_method_table.cpp @@ -525,3 +525,275 @@ SCENARIO( } } } +SCENARIO( + "lambda_method_handle_map with member lambdas", + "[core][java_bytecode][java_bytecode_parse_lambda_method_handle]") +{ + null_message_handlert message_handler; + GIVEN("A class that has lambdas as member variables") + { + java_bytecode_parse_treet parse_tree; + java_bytecode_parse( + "./java_bytecode/java_bytecode_parser/lambda_examples/" + "MemberLambdas.class", + parse_tree, + message_handler); + WHEN("Parsing that class") + { + REQUIRE(parse_tree.loading_successful); + const java_bytecode_parse_treet::classt parsed_class = + parse_tree.parsed_class; + REQUIRE(parsed_class.attribute_bootstrapmethods_read); + REQUIRE(parsed_class.lambda_method_handle_map.size() == 12); + + // Simple lambdas + THEN( + "There should be an entry for the lambda that has no parameters or " + "returns and the method it references should have an appropriate " + "descriptor") + { + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, "()V"); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == "()V"); + } + + // Parameter lambdas + THEN( + "There should be an entry for the lambda that takes parameters and the " + "method it references should have an appropriate descriptor") + { + std::string descriptor = "(ILjava/lang/Object;LDummyGeneric;)V"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + THEN( + "There should be an entry for the lambda that takes array parameters " + "and the method it references should have an appropriate descriptor") + { + std::string descriptor = "([I[Ljava/lang/Object;[LDummyGeneric;)V"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + + // Return lambdas + THEN( + "There should be an entry for the lambda that returns a primitive and " + "the method it references should have an appropriate descriptor") + { + std::string descriptor = "()I"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + THEN( + "There should be an entry for the lambda that returns a reference type " + "and the method it references should have an appropriate descriptor") + { + std::string descriptor = "()Ljava/lang/Object;"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + THEN( + "There should be an entry for the lambda that returns a specialised " + "generic type and the method it references should have an appropriate " + "descriptor") + { + std::string descriptor = "()LDummyGeneric;"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + + // Array returning lambdas + THEN( + "There should be an entry for the lambda that returns an array of " + "primitives and the method it references should have an appropriate " + "descriptor") + { + std::string descriptor = "()[I"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + THEN( + "There should be an entry for the lambda that returns an array of " + "reference types and the method it references should have an " + "appropriate descriptor") + { + std::string descriptor = "()[Ljava/lang/Object;"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + THEN( + "There should be an entry for the lambda that returns an array of " + "specialised generic types and the method it references should have an " + "appropriate descriptor") + { + std::string descriptor = "()[LDummyGeneric;"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == descriptor); + } + + // Capturing lamdbas + THEN( + "There should be an entry for the lambda that returns a primitive " + "local variable and the method it references should have an " + "appropriate descriptor") + { + std::string descriptor = "()I"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor, 1); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + // Note here the descriptor of the implementation is different - the + // implementation requries the input to be passed in + REQUIRE(id2string(lambda_method.descriptor) == "()I"); + REQUIRE_FALSE(lambda_method.is_static); + + const fieldref_exprt primitive_fieldref{ + java_int_type(), "memberPrimitive", "java::MemberLambdas"}; + + std::vector + expected_instructions{{"aload_0", {}}, // load this of stack + {"getfield", {primitive_fieldref}}, + {"ireturn", {}}}; + + require_parse_tree::require_instructions_match_expectation( + expected_instructions, lambda_method.instructions); + } + THEN( + "There should be an entry for the lambda that returns a reference type " + "local variable and the method it references should have an " + "appropriate descriptor") + { + // Since it is a local variable, the corresponding method takes the + // captured variable as an input + std::string descriptor = "()Ljava/lang/Object;"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor, 1); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == "()Ljava/lang/Object;"); + REQUIRE_FALSE(lambda_method.is_static); + + const reference_typet dummy_generic_reference_type = + java_reference_type(symbol_typet{"java::java.lang.Object"}); + + const fieldref_exprt reference_fieldref{dummy_generic_reference_type, + "memberReference", + "java::MemberLambdas"}; + + std::vector + expected_instructions{{"aload_0", {}}, // load this of stack + {"getfield", {reference_fieldref}}, + {"areturn", {}}}; + + require_parse_tree::require_instructions_match_expectation( + expected_instructions, lambda_method.instructions); + } + THEN( + "There should be an entry for the lambda that returns a specialised " + "generic type local variable and the method it references should have " + "an appropriate descriptor") + { + // Since it is a local variable, the corresponding method takes the + // captured variable as an input + std::string descriptor = "()LDummyGeneric;"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor, 1); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const java_bytecode_parse_treet::methodt &lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == "()LDummyGeneric;"); + REQUIRE_FALSE(lambda_method.is_static); + + const reference_typet dummy_generic_reference_type = + java_reference_type(symbol_typet{"java::DummyGeneric"}); + + const fieldref_exprt generic_reference_fieldref{ + dummy_generic_reference_type, + "memberSpecalisedGeneric", + "java::MemberLambdas"}; + + // since just returning the parameter, nothing to put on the stack + std::vector + expected_instructions{{"aload_0", {}}, // load this of stack + {"getfield", {generic_reference_fieldref}}, + {"areturn", {}}}; + + require_parse_tree::require_instructions_match_expectation( + expected_instructions, lambda_method.instructions); + } + } + } +} diff --git a/unit/java_bytecode/java_bytecode_parser/lambda_examples/MemberLambdas.class b/unit/java_bytecode/java_bytecode_parser/lambda_examples/MemberLambdas.class new file mode 100644 index 0000000000000000000000000000000000000000..04984e527203b6e1d6e172f4fe6b4261397dc893 GIT binary patch literal 4426 zcmbVPiGLhr8Ga_$G`nBAn+?4}@1|sv0-+RIN@^7XfxtGzq=ZVTI!UJKw0k)_DGgLD z2#A1!Cx-&wCn_ivwjkaYBHno4H-7{_pZA;1WOruM_%pv>=6$~Rd(ZE9X7>44PCW55@5?9*N^od^nDe;G=PT z3?Gl<6Zm8tpTei(_zXTP9-ouPWAgaCi7%M=qJc!t&P~|vi0kAW&pB!vFopu{sD03O z?ffLQsmx*PsMVLX^3#3SOdPf+y?z5LeCM&EJ!xf~l09{Wowr?Q(!kQpWiz?lY-LQ- z)-h)~Z+SB=V{NLL+*K8MDDToDignHkj+PJY@~1r|(DaSE1cL8rii zPV4aCrIsc8}q!CNT=?qn@8=H%;E*v2!Tbs?h2bG=Pz1EEGn-}h)CQ^5}kVq?Y z1+5PCtA-3A)dJ>v$N1`1O=lZlLzw})rsF6^}? zviigqc&f)O*O75W+2kE^N@Bf7UmHJkiR%&b?2@;~_6`-M476qSq3z1sH+F5^%7d8h zsX6kruQ55#z!@~DbHh|sq@B8C-p*>}MF!f_J^RDqt4((Bpo+;N;kR=wXcmfpVQsn# zYKPluhvysUPVXP8lPXl8EPQKmo^u)`ZL2#^Hyw3iu+vhMX%eB(wH0ykyV66^)@#>G z%Bfy4?ni8_Fyl_zgN{B5HM@PTETqEa805}gS}1rW&$WvF-j@2@~Iz(RUOs#SV_X zE2Hlz3}7cm-igH#o{Fc z9n^=3U#5O#U?rzCx73^ewSm=~A6++5`mDFE+oG^A(8`;W%ykI|Cb{wjpAc zpuewc7$o)yQw;xLpeGtJl6~Icj|R4+L);aqKB!1n{ipxMC8$li{Mo>^MYBYzBq^%u zEhfu`c8)ArZ$r7&3+-ccDyWn%;*eLQA_HqeCc)C^I->JI^ve9^kbfkFwlnb;g}>sT z3I{Q$Fa=xT1{_e>k5Pr|F{1D~3@coReG1oNTwzRZ)@x<<8kyaLVFMfIZ<3QgS~z0& z`EMr@lXcLV^a}1QKbrW3G&eHwZv!ovLgC0vk!wGc=eOHH)+&{3zTsX-SCM8Vo=V{xJeOZJ)tVZ|y|YF8|C{tvp5C|m^;tM# zl^`Y{PM`#|5iAvuAV>;mC*ZG}lj!IrIz!MhLCXcL5VTUzswbGY0ax<59c_H_Q-Z%{ z+L&lLf5EI{o-}{8Y-O5@sU2XJ4A<{kwBrqo@1O%i^iqEPxC&S67@45Aml#iaJKp;w z8q4T3K+tR&Mj2-|2___7t#NA^YYbdRZM=-NK58ptosSY_toKoS85?}mQN~6eEem{C z1iq_$-*AsB*x5=f!z!MLPVWDi+}qXMw>9i#EpO;L9N^3}HsVd#gqzV-)#Gu$$7F+C zU3Mn>*GDNmHnZ?ej}!O{+M<=!3)CDq^YYhm?g~4nsxr?zlRoM=Z+ memberSpecalisedGeneric; + + SimpleLambda simpleLambda = () -> { /*NOP*/ }; + ParameterLambda paramLambda = (int primitive, Object reference, DummyGeneric specalisedGeneric) -> {}; + ArrayParameterLambda arrayParamLambda = (int[] primitive, Object[] reference, DummyGeneric[] specalisedGeneric) -> {}; + ReturningLambdaPrimitive returnPrimitiveLambda = () -> { return 1; }; + ReturningLambdaReference returnReferenceLambda = () -> { return null; }; + ReturningLambdaSpecalisedGeneric returningSpecalisedGenericLambda = () -> { return null; }; + ReturningLambdaPrimitiveArray returnPrimitiveArrayLambda = () -> { return null; }; + ReturningLambdaReferenceArray returnReferenceArrayLambda = () -> { return null; }; + ReturningLambdaSpecalisedGenericArray returningSpecalisedGenericArrayLambda = () -> { return null; }; + + ReturningLambdaPrimitive returnPrimitiveLambdaCapture = () -> { return memberPrimitive; }; + ReturningLambdaReference returnReferenceLambdaCapture = () -> { return memberReference; }; + ReturningLambdaSpecalisedGeneric returningSpecalisedGenericLambdaCapture = () -> { return memberSpecalisedGeneric; }; + + + public void testMethod() { + simpleLambda.Execute(); + paramLambda.Execute(4, null, null); + arrayParamLambda.Execute(null, null, null); + returnPrimitiveLambda.Execute(); + returnReferenceLambda.Execute(); + returningSpecalisedGenericLambda.Execute(); + returnPrimitiveArrayLambda.Execute(); + returnReferenceArrayLambda.Execute(); + returningSpecalisedGenericArrayLambda.Execute(); + + returnPrimitiveLambdaCapture.Execute(); + returnReferenceLambdaCapture.Execute(); + returningSpecalisedGenericLambdaCapture.Execute(); + } +} + From 76d40141ae33ce79140eb1dd777cf3293d612bdb Mon Sep 17 00:00:00 2001 From: thk123 Date: Fri, 23 Feb 2018 18:06:56 +0000 Subject: [PATCH 18/18] Adding tests for inner classes that capture outer class variables --- ...ava_bytecode_parse_lambda_method_table.cpp | 136 ++++++++++++++++++ .../OuterMemberLambdas$Inner.class | Bin 0 -> 2097 bytes .../lambda_examples/OuterMemberLambdas.class | Bin 0 -> 534 bytes .../lambda_examples/OuterMemberLambdas.java | 22 +++ 4 files changed, 158 insertions(+) create mode 100644 unit/java_bytecode/java_bytecode_parser/lambda_examples/OuterMemberLambdas$Inner.class create mode 100644 unit/java_bytecode/java_bytecode_parser/lambda_examples/OuterMemberLambdas.class create mode 100644 unit/java_bytecode/java_bytecode_parser/lambda_examples/OuterMemberLambdas.java diff --git a/unit/java_bytecode/java_bytecode_parser/java_bytecode_parse_lambda_method_table.cpp b/unit/java_bytecode/java_bytecode_parser/java_bytecode_parse_lambda_method_table.cpp index ef68d918bd3..d1484f098a3 100644 --- a/unit/java_bytecode/java_bytecode_parser/java_bytecode_parse_lambda_method_table.cpp +++ b/unit/java_bytecode/java_bytecode_parser/java_bytecode_parse_lambda_method_table.cpp @@ -797,3 +797,139 @@ SCENARIO( } } } +SCENARIO( + "lambda_method_handle_map with member lambdas capturing outer class " + "variables", + "[core][java_bytecode][java_bytecode_parse_lambda_method_handle]") +{ + null_message_handlert message_handler; + GIVEN( + "An inner class with member variables as lambdas that capture outer " + "variables") + { + java_bytecode_parse_treet parse_tree; + java_bytecode_parse( + "./java_bytecode/java_bytecode_parser/lambda_examples/" + "OuterMemberLambdas$Inner.class", + parse_tree, + message_handler); + WHEN("Parsing that class") + { + REQUIRE(parse_tree.loading_successful); + const java_bytecode_parse_treet::classt parsed_class = + parse_tree.parsed_class; + REQUIRE(parsed_class.attribute_bootstrapmethods_read); + REQUIRE(parsed_class.lambda_method_handle_map.size() == 3); + + // Field ref for getting the outer class + const reference_typet outer_class_reference_type = + java_reference_type(symbol_typet{"java::OuterMemberLambdas"}); + const fieldref_exprt outer_fieldref{ + outer_class_reference_type, "this$0", "java::OuterMemberLambdas$Inner"}; + + THEN( + "There should be an entry for the lambda that returns a primitive " + "local variable and the method it references should have an " + "appropriate descriptor") + { + std::string descriptor = "()I"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + // Note here the descriptor of the implementation is different - the + // implementation requries the input to be passed in + REQUIRE(id2string(lambda_method.descriptor) == "()I"); + REQUIRE_FALSE(lambda_method.is_static); + + const fieldref_exprt primitive_fieldref{ + java_int_type(), "memberPrimitive", "java::OuterMemberLambdas"}; + + std::vector + expected_instructions{{"aload_0", {}}, // load this of stack + {"getfield", {outer_fieldref}}, + {"getfield", {primitive_fieldref}}, + {"ireturn", {}}}; + + require_parse_tree::require_instructions_match_expectation( + expected_instructions, lambda_method.instructions); + } + THEN( + "There should be an entry for the lambda that returns a reference type " + "local variable and the method it references should have an " + "appropriate descriptor") + { + // Since it is a local variable, the corresponding method takes the + // captured variable as an input + std::string descriptor = "()Ljava/lang/Object;"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const auto lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == "()Ljava/lang/Object;"); + REQUIRE_FALSE(lambda_method.is_static); + + const reference_typet dummy_generic_reference_type = + java_reference_type(symbol_typet{"java::java.lang.Object"}); + + const fieldref_exprt reference_fieldref{dummy_generic_reference_type, + "memberReference", + "java::OuterMemberLambdas"}; + + std::vector + expected_instructions{{"aload_0", {}}, // load this of stack + {"getfield", {outer_fieldref}}, + {"getfield", {reference_fieldref}}, + {"areturn", {}}}; + + require_parse_tree::require_instructions_match_expectation( + expected_instructions, lambda_method.instructions); + } + THEN( + "There should be an entry for the lambda that returns a specialised " + "generic type local variable and the method it references should have " + "an appropriate descriptor") + { + // Since it is a local variable, the corresponding method takes the + // captured variable as an input + std::string descriptor = "()LDummyGeneric;"; + const lambda_method_handlet &lambda_entry = + require_parse_tree::require_lambda_entry_for_descriptor( + parsed_class, descriptor); + + const irep_idt &lambda_impl_name = lambda_entry.lambda_method_name; + + const java_bytecode_parse_treet::methodt &lambda_method = + require_parse_tree::require_method(parsed_class, lambda_impl_name); + REQUIRE(id2string(lambda_method.descriptor) == "()LDummyGeneric;"); + REQUIRE_FALSE(lambda_method.is_static); + + const reference_typet dummy_generic_reference_type = + java_reference_type(symbol_typet{"java::DummyGeneric"}); + + const fieldref_exprt generic_reference_fieldref{ + dummy_generic_reference_type, + "memberSpecalisedGeneric", + "java::OuterMemberLambdas"}; + + // since just returning the parameter, nothing to put on the stack + std::vector + expected_instructions{{"aload_0", {}}, // load this of stack + {"getfield", {outer_fieldref}}, + {"getfield", {generic_reference_fieldref}}, + {"areturn", {}}}; + + require_parse_tree::require_instructions_match_expectation( + expected_instructions, lambda_method.instructions); + } + } + } +} diff --git a/unit/java_bytecode/java_bytecode_parser/lambda_examples/OuterMemberLambdas$Inner.class b/unit/java_bytecode/java_bytecode_parser/lambda_examples/OuterMemberLambdas$Inner.class new file mode 100644 index 0000000000000000000000000000000000000000..19b99a9819035be8bf129ff731207e69df859ded GIT binary patch literal 2097 zcmbVN?Q+vb6g_JkE4EcE93Ua3kOqg?h7w3=OB4YDBfDX^Z^sJ+Z(1>iEavnb$v4hB9dQn9F} z9~U9;sggb`B84xMw3NqHe3{2GzRKfke3QjB)Kqs}bvHEJ)No7Sq$~ZtYu|URuH{?1 zvTk;pZL??l8bj)3ww*Ql=H5z}Sk-m<9A;taVJ!lT=S9kdy)w#**`;=dVhC z$7u^3Dl2Zi6QsRl%U?>10!Fz~zuxb5_o9ptG;DE++oZ))bJtwxnD+Ly)>X^V$19Mc|Q;Ibm$8cQ3eKo0Qc%a644eM&0)6medfrmOa@kqyGd@C?D z8dsPwh7;dt4&UMX9Bcds0qw@Gvc(f;{U@Dll=Moxh7QVD`T6lJ(FcXB&`$)G--bJ> zey9HHnlUr6r2)CXOl)byGmCdLuPlOYa6J;t56A1S5Sk8=7Ly7uG8?2-K&)u^NypD< z=_sS3<1FTNlrSeSdvKtZz3V)a3*kj&wV7LH%Xi#8fqeI1rh(_1sXNZIeox?4c_@O& zL0G7_O}pKZUMaL(8ZvA6ENF}b3Vog;{>MJzt7nYAB_~pa`oH`i0Yq6 zRjV&?2zY_ClE##jQIe*l@jqyr!b$eUfJ;M`{|V&q0rG4hSKtq)7?b*y1721IW^pEB zeaKO8?R51y()-8?Jff7@M=q4c_mQU*Co@QMgqteT`9wgbhi*bLBc+NgqF9V5#_3o* zhyFY1!oV{DCJZ*jj}P#y)MWg7alG0v5XZCJlktati}7bi@l1O)usVksj_N%x@WDP! gk0#n^^4Km=KSR3(wu`vLrliaGgiT3T@Hwvh10;_G_W%F@ literal 0 HcmV?d00001 diff --git a/unit/java_bytecode/java_bytecode_parser/lambda_examples/OuterMemberLambdas.class b/unit/java_bytecode/java_bytecode_parser/lambda_examples/OuterMemberLambdas.class new file mode 100644 index 0000000000000000000000000000000000000000..da380075695df966c7d51116df4e833865b11d0a GIT binary patch literal 534 zcmZWm%T5A85UkxIAnJ)v%4gImM0Sret;ik z>>Y_wIZQuls=B9pK0e>x0i0vMh%A~8b{yn`qxiuB=>MR1^gxER&uGn^%DHR>Qcqd~Z%TgL7HE8nhfn0oKt(jZBJQDlfwFs%gyHid zXKG=nCR)ac;aZ1(E<3*}_jODYGVLxaRysKn_Hvvk%HDyZ_{%hv+c z|H}|)F7uA;s&Rk}{{`%Eu*wloK+*a&lpwIqaRz0MctBLyZZOiaIgmeng?O=)8rv0y c!Do|gDrN%e*kYf-wzUoBfE)1A7|TO^0gN$!B>(^b literal 0 HcmV?d00001 diff --git a/unit/java_bytecode/java_bytecode_parser/lambda_examples/OuterMemberLambdas.java b/unit/java_bytecode/java_bytecode_parser/lambda_examples/OuterMemberLambdas.java new file mode 100644 index 00000000000..e1255c0ae20 --- /dev/null +++ b/unit/java_bytecode/java_bytecode_parser/lambda_examples/OuterMemberLambdas.java @@ -0,0 +1,22 @@ +public class OuterMemberLambdas { + + int memberPrimitive; + Object memberReference; + DummyGeneric memberSpecalisedGeneric; + + public class Inner { + + ReturningLambdaPrimitive returnPrimitiveLambdaCapture = () -> { return memberPrimitive; }; + ReturningLambdaReference returnReferenceLambdaCapture = () -> { return memberReference; }; + ReturningLambdaSpecalisedGeneric returningSpecalisedGenericLambdaCapture = () -> { return memberSpecalisedGeneric; }; + + + public void testMethod() { + + returnPrimitiveLambdaCapture.Execute(); + returnReferenceLambdaCapture.Execute(); + returningSpecalisedGenericLambdaCapture.Execute(); + } + } +} +