From 5b92002b3b658a70f14576b5a841bdd4818eef7f Mon Sep 17 00:00:00 2001 From: thk123 Date: Fri, 20 Oct 2017 15:21:44 +0100 Subject: [PATCH 01/20] Adding utilities for checking types in unit tests --- unit/testing-utils/Makefile | 1 + unit/testing-utils/require_type.cpp | 51 +++++++++++++++++++++++++++++ unit/testing-utils/require_type.h | 32 ++++++++++++++++++ 3 files changed, 84 insertions(+) create mode 100644 unit/testing-utils/require_type.cpp create mode 100644 unit/testing-utils/require_type.h diff --git a/unit/testing-utils/Makefile b/unit/testing-utils/Makefile index db9dfb33ec4..b0978465a3f 100644 --- a/unit/testing-utils/Makefile +++ b/unit/testing-utils/Makefile @@ -2,6 +2,7 @@ SRC = \ c_to_expr.cpp \ load_java_class.cpp \ require_expr.cpp \ + require_type.cpp \ # Empty last line (please keep above list sorted!) INCLUDES = -I .. -I . -I ../../src diff --git a/unit/testing-utils/require_type.cpp b/unit/testing-utils/require_type.cpp new file mode 100644 index 00000000000..fbf3d6e6b90 --- /dev/null +++ b/unit/testing-utils/require_type.cpp @@ -0,0 +1,51 @@ +/*******************************************************************\ + + Module: Unit test utilities + + Author: DiffBlue Limited. All rights reserved. + +\*******************************************************************/ + +#include "require_type.h" + +#include +#include + +/// Checks a type is a pointer type optionally with a specific subtype +/// \param type: The type to check +/// \param subtype: An optional subtype. If provided, checks the subtype of the +/// pointer is this. +/// \return A cast to pointer_typet version of type +pointer_typet require_type::require_pointer( + const typet &type, + const optionalt &subtype) +{ + REQUIRE(type.id() == ID_pointer); + const pointer_typet &pointer = to_pointer_type(type); + + if(subtype) + { + // TODO: use base_type_eq + REQUIRE(pointer.subtype() == subtype.value()); + } + return pointer; +} + +/// Checks a struct like type has a component with a specific name +/// \param struct_type: The structure that should have the component +/// \param component_name: The name of the component +/// \return The component with the specified name +struct_union_typet::componentt require_type::require_component( + const struct_typet &struct_type, + const irep_idt &component_name) +{ + const auto &componet = std::find_if( + struct_type.components().begin(), + struct_type.components().end(), + [&component_name](const struct_union_typet::componentt &component) { + return component.get_name() == component_name; + }); + + REQUIRE(componet != struct_type.components().end()); + return *componet; +} diff --git a/unit/testing-utils/require_type.h b/unit/testing-utils/require_type.h new file mode 100644 index 00000000000..76e28551322 --- /dev/null +++ b/unit/testing-utils/require_type.h @@ -0,0 +1,32 @@ +/*******************************************************************\ + + Module: Unit test utilities + + Author: DiffBlue Limited. All rights reserved. + +\*******************************************************************/ + +/// \file +/// Helper functions for requiring specific types +/// If the type is of the wrong type, throw a CATCH exception +/// Also checks associated properties and returns a casted version of the +/// expression. + +#ifndef CPROVER_TESTING_UTILS_REQUIRE_TYPE_H +#define CPROVER_TESTING_UTILS_REQUIRE_TYPE_H + +#include +#include + +// NOLINTNEXTLINE(readability/namespace) +namespace require_type +{ +pointer_typet +require_pointer(const typet &type, const optionalt &subtype); + +struct_typet::componentt require_component( + const struct_typet &struct_type, + const irep_idt &component_name); +} + +#endif From 34c185e95ec01283de5b5c95f5b7dc363ee5ddb6 Mon Sep 17 00:00:00 2001 From: thk123 Date: Fri, 20 Oct 2017 15:22:59 +0100 Subject: [PATCH 02/20] Unit test reproducing the bug described in TG-1058 When parsing fields that are of the inner class type, but the outer class is generic, we wrongly parse this a pointer to the outer type. This test demonstrates this problem. --- .../GenericClass$InnerClass.class | Bin 0 -> 500 bytes .../GenericClass.class | Bin 0 -> 547 bytes .../GenericClass.java | 23 ++++++++ .../parse_generic_inner_class.cpp | 49 ++++++++++++++++++ 4 files changed, 72 insertions(+) create mode 100644 unit/java_bytecode/java_bytecode_parse_generics/GenericClass$InnerClass.class create mode 100644 unit/java_bytecode/java_bytecode_parse_generics/GenericClass.class create mode 100644 unit/java_bytecode/java_bytecode_parse_generics/GenericClass.java create mode 100644 unit/java_bytecode/java_bytecode_parse_generics/parse_generic_inner_class.cpp diff --git a/unit/java_bytecode/java_bytecode_parse_generics/GenericClass$InnerClass.class b/unit/java_bytecode/java_bytecode_parse_generics/GenericClass$InnerClass.class new file mode 100644 index 0000000000000000000000000000000000000000..5a263fcbd4b297bb627f93d05fd4780a0f8670a0 GIT binary patch literal 500 zcmY*V!AiqG5PjRk#KzbrRa>hd9(t%1S`Xe@dMPMEP{GjqCN8=qO(aPLKg)v%9{d15 zN}P>K6PDebxAW%B+xhzb_yll{!#XtVTF|j)qGMv8!N?zF);nWpc$Xp;sSIZk&$2OP zCNh@!l)>#+Qzvrnf%Wg+7D5 z27+?kN{`+;Nlw~T&6lTt-J?p;#P`S3VWrZ#OIB$pE~K*NmOnUL@Mmsg1BML~T@wck zoid?hfx%JiQN-hi(M_-rVa}lS)g0_{365#JI!&56j#5?;Z4;Yx*D1P1jTT@HPT#-= x@2I^J(qPeP6kH8k6t{m_u+bz${6dXvP;3`Gi*A=t4XxtbL8IW2FRI=KegHt(ZDIfb literal 0 HcmV?d00001 diff --git a/unit/java_bytecode/java_bytecode_parse_generics/GenericClass.class b/unit/java_bytecode/java_bytecode_parse_generics/GenericClass.class new file mode 100644 index 0000000000000000000000000000000000000000..6d42dbd719dd321860bdc096ca3f6351daf33cba GIT binary patch literal 547 zcmZXR%}&BV6ot=Fpn}M+f@or5;sRa3#+4-`hD2l1=z@m*P(~b5iYdj!$8u%j!Uyo7 zjHeBTRu^+;&YADtxw-T8{qYIl630atXqh-P(Kc};P`vYf6%0MOS_zc4LQ&=(weqF{ zZTnUc(r%2MZ6QmbFw!$$hU-8HoTRwE<5+`k3eNYnufu_W(K#OrWQWTs(<-+1)%|)g zQGp{T9-X?qbfq_zfsXXX%!W^zO_fuRu!CkAetrH;Xr#!v9pat7b7jxvi|l#QpY6_&}25^hq6>T F;0HjDevtqG literal 0 HcmV?d00001 diff --git a/unit/java_bytecode/java_bytecode_parse_generics/GenericClass.java b/unit/java_bytecode/java_bytecode_parse_generics/GenericClass.java new file mode 100644 index 00000000000..792aebeecea --- /dev/null +++ b/unit/java_bytecode/java_bytecode_parse_generics/GenericClass.java @@ -0,0 +1,23 @@ +public class GenericClass +{ + class InnerClass + { + } + + // class GenericInnerClass + // { + // V field; + // } + + // class SameGenericParamInnerClass + // { + // T field; + // } + + InnerClass field; + // GenericInnerClass field2; + // SameGenericParamInnerClass field3; + + // GenericInnerClass field4; + // SameGenericParamInnerClass field5; +} \ No newline at end of file diff --git a/unit/java_bytecode/java_bytecode_parse_generics/parse_generic_inner_class.cpp b/unit/java_bytecode/java_bytecode_parse_generics/parse_generic_inner_class.cpp new file mode 100644 index 00000000000..e3bb2aac1b9 --- /dev/null +++ b/unit/java_bytecode/java_bytecode_parse_generics/parse_generic_inner_class.cpp @@ -0,0 +1,49 @@ +/*******************************************************************\ + + Module: Unit tests for parsing generic classes + + Author: DiffBlue Limited. All rights reserved. + +\*******************************************************************/ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include + +SCENARIO( + "java_bytecode_parse_generic_inner_class", + "[core][java_bytecode][java_bytecode_parse_generics]") +{ + const symbol_tablet &new_symbol_table = load_java_class( + "GenericClass", "./java_bytecode/java_bytecode_parse_generics"); + + std::string class_prefix = "java::GenericClass"; + THEN("There should be a symbol for GenericClass") + { + REQUIRE(new_symbol_table.has_symbol(class_prefix)); + const symbolt &class_symbol = new_symbol_table.lookup_ref(class_prefix); + + const class_typet &class_type = + require_symbol::require_complete_class(class_symbol); + + THEN("The field component should be a pointer to GenericClass$InnerClass") + { + const struct_typet::componentt &field_component = + require_type::require_component(class_type, "field"); + + require_type::require_pointer( + field_component.type(), symbol_typet("java::GenericClass$InnerClass")); + } + } +} From d23a0cc7f1a859def75f75bcc0aa1be0b4eb7612 Mon Sep 17 00:00:00 2001 From: thk123 Date: Fri, 20 Oct 2017 17:55:44 +0100 Subject: [PATCH 03/20] Added function for getting the full class name of a type Skip over the signature when building the class name. --- src/java_bytecode/java_types.cpp | 34 ++++++ unit/java_bytecode/java_utils_test.cpp | 158 +++++++++++++++++++++++++ 2 files changed, 192 insertions(+) diff --git a/src/java_bytecode/java_types.cpp b/src/java_bytecode/java_types.cpp index 752c3c22dd8..3ac148f9019 100644 --- a/src/java_bytecode/java_types.cpp +++ b/src/java_bytecode/java_types.cpp @@ -167,6 +167,40 @@ exprt java_bytecode_promotion(const exprt &expr) return typecast_exprt(expr, new_type); } +/// Returns the full class name, skipping over the generics +/// \param src: a type descriptor or signature like LOuterClass.Inner; +/// \return The full name of the class like OuterClass.Inner +std::string gather_full_class_name(const std::string &src) +{ + PRECONDITION(src[0] == 'L'); + PRECONDITION(src[src.size() - 1] == ';'); + + std::string class_name = src.substr(1, src.size() - 2); + + std::size_t f_pos = class_name.find('<', 1); + + while(f_pos != std::string::npos) + { + std::size_t e_pos = find_closing_delimiter(class_name, f_pos, '<', '>'); + if(e_pos == std::string::npos) + { + throw unsupported_java_class_signature_exceptiont( + "Failed to find generic signature closing delimiter (or recursive " + "generic): " + + src); + } + + // erase generic information between brackets + class_name.erase(f_pos, e_pos - f_pos + 1); + + // Search the remainder of the string for generic signature + f_pos = class_name.find('<', e_pos + 1); + } + + std::replace(class_name.begin(), class_name.end(), '.', '$'); + std::replace(class_name.begin(), class_name.end(), '/', '.'); + return class_name; +} /// Transforms a string representation of a Java type into an internal type /// representation thereof. /// diff --git a/unit/java_bytecode/java_utils_test.cpp b/unit/java_bytecode/java_utils_test.cpp index 068a3984aca..f103b618f60 100644 --- a/unit/java_bytecode/java_utils_test.cpp +++ b/unit/java_bytecode/java_utils_test.cpp @@ -13,6 +13,7 @@ #include +#include #include SCENARIO("Test that the generic signature delimiter lookup works reliably", @@ -63,3 +64,160 @@ SCENARIO("Test that the generic signature delimiter lookup works reliably", } } } + +SCENARIO("gather_full_class_name") +{ + GIVEN("Descriptor: class") + { + std::string descriptor = "LClassName;"; + THEN("Should get ClassName back") + { + const std::string &class_name = gather_full_class_name(descriptor); + REQUIRE(class_name == "ClassName"); + } + } + GIVEN("Descriptor: A packaged class") + { + std::string descriptor = "Ljava/lang/Object;"; + THEN("Should get java.lang.Object back") + { + const std::string &class_name = gather_full_class_name(descriptor); + REQUIRE(class_name == "java.lang.Object"); + } + } + GIVEN("Descriptor: A inner class") + { + std::string descriptor = "LOuter$Inner;"; + THEN("Should get Outer$Inner") + { + const std::string &class_name = gather_full_class_name(descriptor); + REQUIRE(class_name == "Outer$Inner"); + } + } + GIVEN("Descriptor: a doubly nested inner class") + { + std::string descriptor = "LOuter$Inner$Inner2;"; + THEN("Should get Outer$Inner$Inner2") + { + const std::string &class_name = gather_full_class_name(descriptor); + REQUIRE(class_name == "Outer$Inner$Inner2"); + } + } + + GIVEN("Signature: An generic class") + { + std::string signature = "LClassName;"; + THEN("Should get ClassName back") + { + const std::string &class_name = gather_full_class_name(signature); + REQUIRE(class_name == "ClassName"); + } + } + GIVEN("Signature: An inner class in a generic class") + { + std::string signature = "LClassName.Inner;"; + THEN("Should get ClassName$Inner back") + { + const std::string &class_name = gather_full_class_name(signature); + REQUIRE(class_name == "ClassName$Inner"); + } + } + GIVEN("Signature: An generic inner class in a generic class") + { + std::string signature = "LClassName.Inner;"; + THEN("Should get ClassName$Inner back") + { + const std::string &class_name = gather_full_class_name(signature); + REQUIRE(class_name == "ClassName$Inner"); + } + } + GIVEN("Signature: A generic inner class in a non generic class") + { + std::string signature = "LClassName.Inner;"; + THEN("Should get ClassName$Inner back") + { + const std::string &class_name = gather_full_class_name(signature); + REQUIRE(class_name == "ClassName$Inner"); + } + } + GIVEN( + "Signature: A generic inner class in a non generic class in a non " + "generic " + "class") + { + std::string signature = "LClassName.Inner.Inner2;"; + THEN("Should get ClassName$Inner$Inner2 back") + { + const std::string &class_name = gather_full_class_name(signature); + REQUIRE(class_name == "ClassName$Inner$Inner2"); + } + } + GIVEN( + "Signature: A generic inner class in a generic class in a non generic " + "class") + { + std::string signature = "LClassName.Inner.Inner2;"; + THEN("Should get ClassName$Inner$Inner2 back") + { + const std::string &class_name = gather_full_class_name(signature); + REQUIRE(class_name == "ClassName$Inner$Inner2"); + } + } + GIVEN( + "Signature: A generic inner class in a generic class in a generic " + "class") + { + std::string signature = "LClassName.Inner.Inner2;"; + THEN("Should get ClassName$Inner$Inner2 back") + { + const std::string &class_name = gather_full_class_name(signature); + REQUIRE(class_name == "ClassName$Inner$Inner2"); + } + } + GIVEN( + "Signature: A non-generic inner class in a generic class in a non " + "generic " + "class") + { + std::string signature = "LClassName.Inner.Inner2;"; + THEN("Should get ClassName$Inner$Inner2 back") + { + const std::string &class_name = gather_full_class_name(signature); + REQUIRE(class_name == "ClassName$Inner$Inner2"); + } + } + GIVEN( + "Signature: A non-generic inner class in a generic class in a generic " + "class") + { + std::string signature = "LClassName.Inner.Inner2;"; + THEN("Should get ClassName$Inner$Inner2 back") + { + const std::string &class_name = gather_full_class_name(signature); + REQUIRE(class_name == "ClassName$Inner$Inner2"); + } + } + GIVEN( + "Signature: A non-generic inner class in a non-generic class in a " + "generic " + "class") + { + std::string signature = "LClassName.Inner.Inner2;"; + THEN("Should get ClassName$Inner$Inner2 back") + { + const std::string &class_name = gather_full_class_name(signature); + REQUIRE(class_name == "ClassName$Inner$Inner2"); + } + } + GIVEN( + "Signature: A generic inner class in a non-generic class in a generic " + "class") + { + std::string signature = "LClassName.Inner.Inner2;"; + THEN("Should get ClassName$Inner$Inner2 back") + { + const std::string &class_name = gather_full_class_name(signature); + REQUIRE(class_name == "ClassName$Inner$Inner2"); + } + } +} From effc1b2c7ac964bfce8c11e524bfe1a591c6f145 Mon Sep 17 00:00:00 2001 From: thk123 Date: Fri, 20 Oct 2017 18:28:26 +0100 Subject: [PATCH 04/20] Parse the whole class when generics are present Previously when a generic signature was parsed, inner classes were ignored. This resolves this by first building up a complete class name. This also resolves the fact that inner classes in signatures are denoted by . rather than $ and so replaces the . with $ to be consistent. --- src/java_bytecode/java_types.cpp | 194 +++++++++--------- .../GenericClass.class | Bin 547 -> 1074 bytes .../GenericClass.java | 34 +-- .../parse_generic_inner_class.cpp | 55 ++++- 4 files changed, 168 insertions(+), 115 deletions(-) diff --git a/src/java_bytecode/java_types.cpp b/src/java_bytecode/java_types.cpp index 3ac148f9019..1740ad2e5af 100644 --- a/src/java_bytecode/java_types.cpp +++ b/src/java_bytecode/java_types.cpp @@ -201,6 +201,97 @@ std::string gather_full_class_name(const std::string &src) std::replace(class_name.begin(), class_name.end(), '/', '.'); return class_name; } + +reference_typet +build_class_name(const std::string src, const std::string &class_name_prefix) +{ + PRECONDITION(src[0] == 'L'); + // ends on ; + if(src[src.size() - 1] != ';') + throw "invalid string for reference type"; + + std::string container_class = gather_full_class_name(src); + std::string identifier = "java::" + container_class; + symbol_typet symbol_type(identifier); + symbol_type.set(ID_C_base_name, container_class); + + // TODO(tkiley): The below code only parses the first generic argument list + + std::size_t f_pos = src.find('<', 1); + // get generic type information + if(f_pos != std::string::npos) + { + std::size_t e_pos = find_closing_delimiter(src, f_pos, '<', '>'); + if(e_pos == std::string::npos) + throw unsupported_java_class_signature_exceptiont("recursive generic"); + + // What needs to happen is the symbol_typet needs to use all parts + // of the name and all generic parts of the child names + // For now it might be sufficient to parse the rest of the name + + java_generic_typet result(symbol_type); + +#ifdef DEBUG + std::cout << "INFO: found generic type " + << src.substr(f_pos + 1, e_pos - f_pos - 1) << " in " << src + << " with container " << generic_container_class << "\n"; +#endif + + // parse contained types, can be either type variables, starting with T + // or instantiated types + size_t curr_start = f_pos + 1; + size_t curr_end; + do + { + // find next end of type name + curr_end = src.find(';', curr_start); + INVARIANT( + curr_end != std::string::npos, "Type not terminated with \';\'"); + const size_t end = curr_end - curr_start + 1; + const typet &t = + java_type_from_string(src.substr(curr_start, end), class_name_prefix); +#ifdef DEBUG + std::cout << "INFO: getting type " << src.substr(curr_start, end) << "\n" + << "INFO: type id " << id2string(t.id()) << "\n" + << "curr_start " << curr_start << " curr_end " << curr_end + << " e_pos " << e_pos << " src " << src << "\n"; +#endif + // is an uninstantiated (pure) generic type + if(is_java_generic_parameter(t)) + { + const java_generic_parametert &gen_type = to_java_generic_parameter(t); +#ifdef DEBUG + std::cout << " generic type var " << gen_type.id() << " bound " + << to_symbol_type(gen_type.subtype()).get_identifier() + << "\n"; +#endif + result.generic_type_variables().push_back(gen_type); + } + + /// TODO(mgudemann): implement / test the case of iterated generic + /// types + + // is a concrete type, i.e., instantiation of a generic type of the + // current type + else + { + java_generic_inst_parametert inst_type(to_symbol_type(t.subtype())); +#ifdef DEBUG + std::cout << " instantiation of generic type var " + << to_symbol_type(t.subtype()).get_identifier() << "\n"; +#endif + result.generic_type_variables().push_back(inst_type); + } + + curr_start = curr_end + 1; + } while(curr_start < e_pos); + + return result; + } + + return java_reference_type(symbol_type); +} + /// Transforms a string representation of a Java type into an internal type /// representation thereof. /// @@ -361,108 +452,7 @@ typet java_type_from_string( } case 'L': { - // ends on ; - if(src[src.size()-1]!=';') - return nil_typet(); - - std::size_t f_pos=src.find('<', 1); - // get generic type information - if(f_pos!=std::string::npos) - { - std::size_t e_pos=find_closing_delimiter(src, f_pos, '<', '>'); - if(e_pos==std::string::npos) - throw unsupported_java_class_signature_exceptiont( - "recursive generic"); - - // construct container type - std::string generic_container_class=src.substr(1, f_pos-1); - - for(unsigned i=0; iPkSc98kO*9ACu>Ee}(Jr@;0sik`QgAR0Lx?fbJ zdLIL>Pkhzy3yP`GeHT^1${0;IT+{@GR14<58ttD8HoY;NL@TFAU9iwH7l9fKBQ4mn zxJJ8OZ|+<0d=l`jVA~?gnOYe5jfISYWw?fS8iw^I@lohQLDljr-+n4|I=0wRj@uos zCp!|*%PhIzBC{ zUf5OsnTkxT$Hx5N(y$K}t<0=x-G+aBn>eglWO6jbD~H)-uVQVgA5uS>*`*!5(q3SfDu*(?!2}NSRPhhEobPd7;9LOy18JHT Aw*UYD delta 188 zcmY+7yA8rX5JcbI*_=84e~cp}B1lOI=z%UMf*PzVfeIi&BA@^QXb2$&i-cfjXGSx- z=j@mi_xJS%tSJJHiD&A`Ju^cnS9!VL#g}=y2F>`ei@0&~gHha*VW^8)f|x-GVN1@o s(vU7Mn3D`gTUe8grX&1D6`)H`yZ`45q}=cHkdc&StTxr7gw(3Y2Snx&Z2$lO diff --git a/unit/java_bytecode/java_bytecode_parse_generics/GenericClass.java b/unit/java_bytecode/java_bytecode_parse_generics/GenericClass.java index 792aebeecea..222105dad7d 100644 --- a/unit/java_bytecode/java_bytecode_parse_generics/GenericClass.java +++ b/unit/java_bytecode/java_bytecode_parse_generics/GenericClass.java @@ -4,20 +4,30 @@ class InnerClass { } - // class GenericInnerClass - // { - // V field; - // } + class GenericInnerClass + { + V field; + + class DoublyNestedInnerClass + { - // class SameGenericParamInnerClass - // { - // T field; - // } + } + + class DoublyNestedInnerGemericClass + { + T field; + } + } + + class SameGenericParamInnerClass + { + T field; + } InnerClass field; - // GenericInnerClass field2; - // SameGenericParamInnerClass field3; + GenericInnerClass field2; + SameGenericParamInnerClass field3; - // GenericInnerClass field4; - // SameGenericParamInnerClass field5; + GenericInnerClass field4; + SameGenericParamInnerClass field5; } \ No newline at end of file diff --git a/unit/java_bytecode/java_bytecode_parse_generics/parse_generic_inner_class.cpp b/unit/java_bytecode/java_bytecode_parse_generics/parse_generic_inner_class.cpp index e3bb2aac1b9..a0661fc7ad7 100644 --- a/unit/java_bytecode/java_bytecode_parse_generics/parse_generic_inner_class.cpp +++ b/unit/java_bytecode/java_bytecode_parse_generics/parse_generic_inner_class.cpp @@ -29,7 +29,7 @@ SCENARIO( "GenericClass", "./java_bytecode/java_bytecode_parse_generics"); std::string class_prefix = "java::GenericClass"; - THEN("There should be a symbol for GenericClass") + THEN("There should be a symbol for GenericClass with correct components") { REQUIRE(new_symbol_table.has_symbol(class_prefix)); const symbolt &class_symbol = new_symbol_table.lookup_ref(class_prefix); @@ -44,6 +44,59 @@ SCENARIO( require_type::require_pointer( field_component.type(), symbol_typet("java::GenericClass$InnerClass")); + + // TODO: then the generic type should be correctly stored + } + + THEN("The field component should be a pointer to GenericClass$InnerClass") + { + const struct_typet::componentt &field_component = + require_type::require_component(class_type, "field2"); + + require_type::require_pointer( + field_component.type(), + symbol_typet("java::GenericClass$GenericInnerClass")); + + // TODO: then the generic type should be correctly stored } + + THEN("The field component should be a pointer to GenericClass$InnerClass") + { + const struct_typet::componentt &field_component = + require_type::require_component(class_type, "field3"); + + require_type::require_pointer( + field_component.type(), + symbol_typet("java::GenericClass$SameGenericParamInnerClass")); + + // TODO: then the generic type should be correctly stored + } + + THEN("The field component should be a pointer to GenericClass$InnerClass") + { + const struct_typet::componentt &field_component = + require_type::require_component(class_type, "field4"); + + require_type::require_pointer( + field_component.type(), + symbol_typet("java::GenericClass$GenericInnerClass")); + + // TODO: then the generic type should be correctly stored + } + THEN("The field component should be a pointer to GenericClass$InnerClass") + { + const struct_typet::componentt &field_component = + require_type::require_component(class_type, "field5"); + + require_type::require_pointer( + field_component.type(), + symbol_typet("java::GenericClass$SameGenericParamInnerClass")); + + // TODO: then the generic type should be correctly stored + } + } + + THEN("There should be a symbol for the generic inner class") + { } } From ed545cb3ac085662465c5e30d574aac91f347e06 Mon Sep 17 00:00:00 2001 From: thk123 Date: Mon, 23 Oct 2017 15:39:50 +0100 Subject: [PATCH 05/20] Refactored out the code for erasing generic types --- src/java_bytecode/java_types.cpp | 37 +++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/src/java_bytecode/java_types.cpp b/src/java_bytecode/java_types.cpp index 1740ad2e5af..e8fc8e47119 100644 --- a/src/java_bytecode/java_types.cpp +++ b/src/java_bytecode/java_types.cpp @@ -167,16 +167,19 @@ exprt java_bytecode_promotion(const exprt &expr) return typecast_exprt(expr, new_type); } -/// Returns the full class name, skipping over the generics -/// \param src: a type descriptor or signature like LOuterClass.Inner; -/// \return The full name of the class like OuterClass.Inner -std::string gather_full_class_name(const std::string &src) +void add_generic_type_information( + java_generic_typet &generic_type, + const std::string ¶meters) { - PRECONDITION(src[0] == 'L'); - PRECONDITION(src[src.size() - 1] == ';'); - - std::string class_name = src.substr(1, src.size() - 2); +} +/// Take a signature string and remove everything in angle brackets allowing +/// for the type to be parsed normally. +/// \param src: The input string +/// \return The input string with everything between angle brackets removed +std::string erase_type_arguments(const std::string &src) +{ + std::string class_name = src; std::size_t f_pos = class_name.find('<', 1); while(f_pos != std::string::npos) @@ -196,6 +199,24 @@ std::string gather_full_class_name(const std::string &src) // Search the remainder of the string for generic signature f_pos = class_name.find('<', e_pos + 1); } + return class_name; +} + +/// Returns the full class name, skipping over the generics. +/// \param src: a type descriptor or signature +/// 1. Signature: Lcom/package/OuterClass.Inner; +/// 2. Descriptor: Lcom.pacakge.OuterClass$Inner; +/// \return The full name of the class like com.package.OuterClass.Inner (for +/// both examples). +std::string gather_full_class_name(const std::string &src) +{ + PRECONDITION(src.size() >= 2); + PRECONDITION(src[0] == 'L'); + PRECONDITION(src[src.size() - 1] == ';'); + + std::string class_name = src.substr(1, src.size() - 2); + + class_name = erase_type_arguments(class_name); std::replace(class_name.begin(), class_name.end(), '.', '$'); std::replace(class_name.begin(), class_name.end(), '/', '.'); From 72a041ba4b436d75e4982807921e7764a32bb4fb Mon Sep 17 00:00:00 2001 From: thk123 Date: Mon, 23 Oct 2017 15:42:51 +0100 Subject: [PATCH 06/20] Don't crash when found an invalid reference type This is consistent with the original behaviour which would return a nil_typet --- src/java_bytecode/java_types.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/java_bytecode/java_types.cpp b/src/java_bytecode/java_types.cpp index e8fc8e47119..1df84dd3d95 100644 --- a/src/java_bytecode/java_types.cpp +++ b/src/java_bytecode/java_types.cpp @@ -223,13 +223,20 @@ std::string gather_full_class_name(const std::string &src) return class_name; } -reference_typet +/// For parsing a class type reference +/// \param src: The input string +/// Either a signature: "Lpackage/class.innerclass; +/// Or a descriptor: "Lpackage.class$inner; +/// \param class_name_prefix: The name of the class to use to prefix any found +/// generic types +/// \return The reference type if parsed correctly, no value if parsing fails. +optionalt build_class_name(const std::string src, const std::string &class_name_prefix) { PRECONDITION(src[0] == 'L'); // ends on ; if(src[src.size() - 1] != ';') - throw "invalid string for reference type"; + return optionalt(); std::string container_class = gather_full_class_name(src); std::string identifier = "java::" + container_class; @@ -473,7 +480,12 @@ typet java_type_from_string( } case 'L': { - return build_class_name(src, class_name_prefix); + const optionalt &class_type = + build_class_name(src, class_name_prefix); + if(class_type) + return class_type.value(); + else + return nil_typet(); } case '*': case '+': From 958c00643b71becd7a4d9ff2e4da65a5f4f4b89c Mon Sep 17 00:00:00 2001 From: thk123 Date: Mon, 23 Oct 2017 17:32:03 +0100 Subject: [PATCH 07/20] Adding utilities for code_type structures --- unit/testing-utils/require_type.cpp | 29 +++++++++++++++++++++++++++++ unit/testing-utils/require_type.h | 4 ++++ 2 files changed, 33 insertions(+) diff --git a/unit/testing-utils/require_type.cpp b/unit/testing-utils/require_type.cpp index fbf3d6e6b90..882f39005c4 100644 --- a/unit/testing-utils/require_type.cpp +++ b/unit/testing-utils/require_type.cpp @@ -49,3 +49,32 @@ struct_union_typet::componentt require_type::require_component( REQUIRE(componet != struct_type.components().end()); return *componet; } + +/// Checks a type is a code_type (i.e. a function) +/// \param type: The type to check +/// \return The cast version of the type code_type +code_typet require_type::require_code(const typet &type) +{ + REQUIRE(type.id() == ID_code); + return to_code_type(type); +} + +/// Verify that a function has a parameter of a specific name. +/// \param function_type: The type of the function +/// \param param_name: The name of the parameter +/// \return: A reference to the parameter structure corresponding to this +/// parameter name. +code_typet::parametert require_type::require_parameter( + const code_typet &function_type, + const irep_idt ¶m_name) +{ + const auto param = std::find_if( + function_type.parameters().begin(), + function_type.parameters().end(), + [¶m_name](const code_typet::parametert param) { + return param.get_base_name() == param_name; + }); + + REQUIRE(param != function_type.parameters().end()); + return *param; +} diff --git a/unit/testing-utils/require_type.h b/unit/testing-utils/require_type.h index 76e28551322..488abb5da44 100644 --- a/unit/testing-utils/require_type.h +++ b/unit/testing-utils/require_type.h @@ -27,6 +27,10 @@ require_pointer(const typet &type, const optionalt &subtype); struct_typet::componentt require_component( const struct_typet &struct_type, const irep_idt &component_name); + +code_typet require_code(const typet &type); +code_typet::parametert +require_parameter(const code_typet &function_type, const irep_idt ¶m_name); } #endif From e952708db7be49e7344bb6a57f037d6f50509a70 Mon Sep 17 00:00:00 2001 From: thk123 Date: Mon, 23 Oct 2017 17:32:27 +0100 Subject: [PATCH 08/20] Adding tests for parsing methods of generic classes with inner classes --- .../GenericClass.class | Bin 1074 -> 2367 bytes .../GenericClass.java | 38 +++- .../parse_generic_inner_class.cpp | 203 +++++++++++++++++- 3 files changed, 239 insertions(+), 2 deletions(-) diff --git a/unit/java_bytecode/java_bytecode_parse_generics/GenericClass.class b/unit/java_bytecode/java_bytecode_parse_generics/GenericClass.class index 2f83bd3863f067cc5ee25f1a954ca6091204b159..b006128a4772d3746a937b301bb3a1420285f8e8 100644 GIT binary patch literal 2367 zcmbVMYflqF6g{)0EFhMb7I}yTij)d10tKP9Xh<+7jgQdqeJLv}X}6@bCjKk^Wa0;Z zfIrH3W_RiCbXy*N*qym^&pqefnLG36@2}qgw(w>M5$x&6>nP}WEiip#)XaCLZ8ogR zC!=B1-rJlXRE?`Efnnb;uXGdz#yqI+(6KKtf^@bQ4olcx&hz0!s8)yP&2`z-tM7Es?_pDH!R)QqmDjaK9xJty@?J~Lll8Luv zGL*?;-K(a^b-@))eCI!S_PdUbd-huB?B$BwHFt+za<+lZTRDsO;M^i-?hc#>_u$-S fg>J?>ctw5E>r8PwPr)Y%y{7#XA{C$h>+H0PJlWMmL@Pt8j$ z%1rjm15(a8iN(e23|brG6qzH5WSlI-79hyXzzZ~qi9v}$8BD88mgiIe0HUrG Aod5s; diff --git a/unit/java_bytecode/java_bytecode_parse_generics/GenericClass.java b/unit/java_bytecode/java_bytecode_parse_generics/GenericClass.java index 222105dad7d..e50f67fe79d 100644 --- a/unit/java_bytecode/java_bytecode_parse_generics/GenericClass.java +++ b/unit/java_bytecode/java_bytecode_parse_generics/GenericClass.java @@ -30,4 +30,40 @@ class SameGenericParamInnerClass GenericInnerClass field4; SameGenericParamInnerClass field5; -} \ No newline at end of file + + void method(InnerClass input) + { + + } + + void method2(InnerClass input, InnerClass input2) + { + + } + + + void method3(GenericInnerClass input) + { + + } + + void method4(GenericInnerClass input) + { + + } + + InnerClass method5() + { + return null; + } + + GenericInnerClass method6() + { + return null; + } + + GenericInnerClass method7() + { + return null; + } +} diff --git a/unit/java_bytecode/java_bytecode_parse_generics/parse_generic_inner_class.cpp b/unit/java_bytecode/java_bytecode_parse_generics/parse_generic_inner_class.cpp index a0661fc7ad7..1171fc4edf3 100644 --- a/unit/java_bytecode/java_bytecode_parse_generics/parse_generic_inner_class.cpp +++ b/unit/java_bytecode/java_bytecode_parse_generics/parse_generic_inner_class.cpp @@ -22,7 +22,7 @@ #include SCENARIO( - "java_bytecode_parse_generic_inner_class", + "Parse fields of inner classes on a generic class", "[core][java_bytecode][java_bytecode_parse_generics]") { const symbol_tablet &new_symbol_table = load_java_class( @@ -100,3 +100,204 @@ SCENARIO( { } } + +SCENARIO( + "Parse methods of generic classes using inner parameters", + "[core][java_bytecode][java_bytecode_parse_generics][caskjd]") +{ + const symbol_tablet &new_symbol_table = load_java_class( + "GenericClass", "./java_bytecode/java_bytecode_parse_generics"); + + std::string class_prefix = "java::GenericClass"; + + THEN("Method 1 should take a pointer to GenericClass$InnerClass") + { + const std::string func_name = ".method"; + const std::string func_descriptor = ":(LGenericClass$InnerClass;)V"; + const std::string process_func_name = + class_prefix + func_name + func_descriptor; + + REQUIRE(new_symbol_table.has_symbol(process_func_name)); + const symbolt &function_symbol = + new_symbol_table.lookup_ref(process_func_name); + + const code_typet &function_call = + require_type::require_code(function_symbol.type); + + const auto param_type = + require_type::require_parameter(function_call, "input"); + require_type::require_pointer( + param_type.type(), symbol_typet("java::GenericClass$InnerClass")); + + THEN("The pointer should be generic") + { + REQUIRE(is_java_generic_type(param_type.type())); + const auto &generic_variables = + to_java_generic_type(param_type.type()).generic_type_variables(); + REQUIRE(generic_variables.size() == 1); + const java_generic_parametert &generic_param = generic_variables[0]; + REQUIRE( + generic_param.type_variable() == symbol_typet("java::GenericClass::T")); + } + } + THEN("Method 2 should take two pointers to GenericClass$InnerClass") + { + const std::string func_name = ".method2"; + const std::string func_descriptor = + ":(LGenericClass$InnerClass;LGenericClass$InnerClass;)V"; + const std::string process_func_name = + class_prefix + func_name + func_descriptor; + + REQUIRE(new_symbol_table.has_symbol(process_func_name)); + const symbolt &function_symbol = + new_symbol_table.lookup_ref(process_func_name); + + const code_typet &function_call = + require_type::require_code(function_symbol.type); + + // Check param input + { + const auto param_type = + require_type::require_parameter(function_call, "input"); + require_type::require_pointer( + param_type.type(), symbol_typet("java::GenericClass$InnerClass")); + + THEN("The pointer should be generic") + { + REQUIRE(is_java_generic_type(param_type.type())); + const auto &generic_variables = + to_java_generic_type(param_type.type()).generic_type_variables(); + REQUIRE(generic_variables.size() == 1); + const java_generic_parametert &generic_param = generic_variables[0]; + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + } + + // Check param input2 + { + const auto param_type2 = + require_type::require_parameter(function_call, "input2"); + require_type::require_pointer( + param_type2.type(), symbol_typet("java::GenericClass$InnerClass")); + + THEN("The pointer should be generic") + { + REQUIRE(is_java_generic_type(param_type2.type())); + const auto &generic_variables = + to_java_generic_type(param_type2.type()).generic_type_variables(); + REQUIRE(generic_variables.size() == 1); + const java_generic_parametert &generic_param = generic_variables[0]; + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + } + } + THEN("Method 3 should take a pointer to GenericClass$GenericInnerClass") + { + const std::string func_name = ".method3"; + const std::string func_descriptor = ":(LGenericClass$GenericInnerClass;)V"; + const std::string process_func_name = + class_prefix + func_name + func_descriptor; + + REQUIRE(new_symbol_table.has_symbol(process_func_name)); + const symbolt &function_symbol = + new_symbol_table.lookup_ref(process_func_name); + + const code_typet &function_call = + require_type::require_code(function_symbol.type); + + const auto param_type = + require_type::require_parameter(function_call, "input"); + require_type::require_pointer( + param_type.type(), symbol_typet("java::GenericClass$GenericInnerClass")); + + THEN("The pointer should be generic") + { + REQUIRE(is_java_generic_type(param_type.type())); + const auto &generic_variables = + to_java_generic_type(param_type.type()).generic_type_variables(); + REQUIRE(generic_variables.size() == 1); + const java_generic_parametert &generic_param = generic_variables[0]; + REQUIRE(is_java_generic_inst_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + java_reference_type(symbol_typet("java::Foo"))); + } + } + THEN("Method 4 should take a pointer to GenericClass$GenericInnerClass") + { + const std::string func_name = ".method4"; + const std::string func_descriptor = ":(LGenericClass$GenericInnerClass;)V"; + const std::string process_func_name = + class_prefix + func_name + func_descriptor; + + REQUIRE(new_symbol_table.has_symbol(process_func_name)); + const symbolt &function_symbol = + new_symbol_table.lookup_ref(process_func_name); + + const code_typet &function_call = + require_type::require_code(function_symbol.type); + + const auto param_type = + require_type::require_parameter(function_call, "input"); + require_type::require_pointer( + param_type.type(), symbol_typet("java::GenericClass$GenericInnerClass")); + } + THEN("Method 5 should return a GenericClass$InnerClass") + { + const std::string func_name = ".method5"; + const std::string func_descriptor = ":()LGenericClass$InnerClass;"; + const std::string process_func_name = + class_prefix + func_name + func_descriptor; + + REQUIRE(new_symbol_table.has_symbol(process_func_name)); + const symbolt &function_symbol = + new_symbol_table.lookup_ref(process_func_name); + + const code_typet &function_call = + require_type::require_code(function_symbol.type); + + require_type::require_pointer( + function_call.return_type(), + symbol_typet("java::GenericClass$InnerClass")); + } + THEN("Method 6 should return a GenericClass$InnerClass") + { + const std::string func_name = ".method6"; + const std::string func_descriptor = ":()LGenericClass$GenericInnerClass;"; + const std::string process_func_name = + class_prefix + func_name + func_descriptor; + + REQUIRE(new_symbol_table.has_symbol(process_func_name)); + const symbolt &function_symbol = + new_symbol_table.lookup_ref(process_func_name); + + const code_typet &function_call = + require_type::require_code(function_symbol.type); + + require_type::require_pointer( + function_call.return_type(), + symbol_typet("java::GenericClass$GenericInnerClass")); + } + THEN("Method 7 should return a GenericClass$InnerClass") + { + const std::string func_name = ".method7"; + const std::string func_descriptor = ":()LGenericClass$GenericInnerClass;"; + const std::string process_func_name = + class_prefix + func_name + func_descriptor; + + REQUIRE(new_symbol_table.has_symbol(process_func_name)); + const symbolt &function_symbol = + new_symbol_table.lookup_ref(process_func_name); + + const code_typet &function_call = + require_type::require_code(function_symbol.type); + + require_type::require_pointer( + function_call.return_type(), + symbol_typet("java::GenericClass$GenericInnerClass")); + } +} From e1621a366309254c268eb2dffa8579770c751683 Mon Sep 17 00:00:00 2001 From: thk123 Date: Mon, 23 Oct 2017 17:34:21 +0100 Subject: [PATCH 09/20] Hide methods relating to getting multiple type variables for specific param It is not possible for a generic parameter to either have multiple names or have multiple instantiated types so the public interface should only return one value. --- src/java_bytecode/java_types.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/java_bytecode/java_types.h b/src/java_bytecode/java_types.h index fc1a4812d8c..a51a6c3db95 100644 --- a/src/java_bytecode/java_types.h +++ b/src/java_bytecode/java_types.h @@ -90,7 +90,6 @@ class java_generic_parametert:public reference_typet { public: typedef symbol_typet type_variablet; - typedef std::vector type_variablest; java_generic_parametert( const irep_idt &_type_var_name, @@ -108,6 +107,8 @@ class java_generic_parametert:public reference_typet return type_variables().front(); } +private: + typedef std::vector type_variablest; const type_variablest &type_variables() const { return (const type_variablest &)(find(ID_type_variables).get_sub()); From 35d974f96ee4c449fbfd8bdf3b0d94ec812d9cb3 Mon Sep 17 00:00:00 2001 From: thk123 Date: Mon, 23 Oct 2017 18:03:10 +0100 Subject: [PATCH 10/20] Add method for finding the matching ; corresponding to a reference L --- src/java_bytecode/java_types.cpp | 33 ++++++++++ unit/java_bytecode/java_utils_test.cpp | 85 ++++++++++++++++++++++++++ 2 files changed, 118 insertions(+) diff --git a/src/java_bytecode/java_types.cpp b/src/java_bytecode/java_types.cpp index 1df84dd3d95..4453d8664b1 100644 --- a/src/java_bytecode/java_types.cpp +++ b/src/java_bytecode/java_types.cpp @@ -320,6 +320,39 @@ build_class_name(const std::string src, const std::string &class_name_prefix) return java_reference_type(symbol_type); } +/// Finds the closing semi-colon ending a ClassTypeSignature that starts at +/// \p starting_point. +/// \param src: The input string to work on. +/// \param starting_point: The string position where the opening 'L' we want to +/// find the closing ';' for. +/// \return The string position corresponding to the matching ';'. For example: +/// LA;, we would return 2. For LA; we would return 7. +/// See unit/java_bytecode/java_util_tests.cpp for more examples. +size_t find_closing_semi_colon_for_reference_type( + const std::string src, + size_t starting_point = 0) +{ + PRECONDITION(src[starting_point] == 'L'); + size_t next_semi_colon = src.find(';', starting_point); + INVARIANT( + next_semi_colon != std::string::npos, + "There must be a semi-colon somewhere in the input"); + size_t next_angle_bracket = src.find('<', starting_point); + + while(next_angle_bracket < next_semi_colon) + { + size_t end_bracket = + find_closing_delimiter(src, next_angle_bracket, '<', '>'); + INVARIANT( + end_bracket != std::string::npos, "Must find matching angle bracket"); + + next_semi_colon = src.find(';', end_bracket + 1); + next_angle_bracket = src.find('<', end_bracket + 1); + } + + return next_semi_colon; +} + /// Transforms a string representation of a Java type into an internal type /// representation thereof. /// diff --git a/unit/java_bytecode/java_utils_test.cpp b/unit/java_bytecode/java_utils_test.cpp index f103b618f60..23b8b4927d1 100644 --- a/unit/java_bytecode/java_utils_test.cpp +++ b/unit/java_bytecode/java_utils_test.cpp @@ -221,3 +221,88 @@ SCENARIO("gather_full_class_name") } } } + +SCENARIO("find_closing_semi_colon_for_reference_type", "[core][java_util_test]") +{ + GIVEN("A simple reference type") + { + std::string descriptor = "LA;"; + // | + // 012 + REQUIRE(find_closing_semi_colon_for_reference_type(descriptor, 0) == 2); + } + GIVEN("A generic reference type") + { + std::string descriptor = "LA;"; + // | + // 01234567 + REQUIRE(find_closing_semi_colon_for_reference_type(descriptor, 0) == 7); + } + GIVEN("A generic reference type with multiple generic params") + { + std::string descriptor = "LA;"; + // | + // 01234567890 + REQUIRE(find_closing_semi_colon_for_reference_type(descriptor, 0) == 10); + } + GIVEN("A descriptor with multiple reference type") + { + std::string descriptor = "LA;LB;"; + // | + // 012 + REQUIRE(find_closing_semi_colon_for_reference_type(descriptor, 0) == 2); + } + GIVEN("A descriptor with multiple reference types parsing the second") + { + std::string descriptor = "LA;LB;"; + // | | + // 012345 + REQUIRE(find_closing_semi_colon_for_reference_type(descriptor, 3) == 5); + } + GIVEN("A descriptor inner class") + { + std::string descriptor = "LA$B;"; + // | + // 01234 + REQUIRE(find_closing_semi_colon_for_reference_type(descriptor, 0) == 4); + } + GIVEN("A signature inner class") + { + std::string descriptor = "LA.B;"; + // | + // 01234 + REQUIRE(find_closing_semi_colon_for_reference_type(descriptor, 0) == 4); + } + GIVEN("A inner class of a generic class") + { + std::string descriptor = "LA.B;"; + // | + // 0123456789 + REQUIRE(find_closing_semi_colon_for_reference_type(descriptor, 0) == 9); + } + GIVEN("A inner class of a instantiated generic class") + { + std::string descriptor = "LA.B;"; + // | + // 012345678901 + REQUIRE(find_closing_semi_colon_for_reference_type(descriptor, 0) == 11); + } + GIVEN( + "A signature with multiple references and an inner class of a generic " + "class") + { + std::string descriptor = "LA.B;LA.B;"; + // | + // 0123456789 + REQUIRE(find_closing_semi_colon_for_reference_type(descriptor, 0) == 9); + } + GIVEN( + "A signature with multiple references and an inner class of a generic " + "class") + { + std::string descriptor = "LA.B;LA.B;"; + // | | + // 01234567890123456789 + REQUIRE(find_closing_semi_colon_for_reference_type(descriptor, 10) == 19); + } +} From 2a5c280f7fa139e293d1e38db3c476a447f1a3aa Mon Sep 17 00:00:00 2001 From: thk123 Date: Mon, 23 Oct 2017 18:05:04 +0100 Subject: [PATCH 11/20] Correctly parse multiple input parameters for functions When there is a function on a generic type whose params are inner classes we would wrongly parse only up to the inner class dot for the name. This resulted in wrong parameters causing us to fall back on the descriptor unnecessarily. Instead we should find the semi-colon that matches the L for each parameter before moving on to the next parameter. --- src/java_bytecode/java_types.cpp | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/java_bytecode/java_types.cpp b/src/java_bytecode/java_types.cpp index 4453d8664b1..fb768450aee 100644 --- a/src/java_bytecode/java_types.cpp +++ b/src/java_bytecode/java_types.cpp @@ -437,14 +437,7 @@ typet java_type_from_string( // parameter is an object type or instantiated generic type if(src[i]=='L') { - size_t generic_open=src.find('<', i); - size_t end_pos=src.find(';', i); - // generic signature - if(generic_open!=std::string::npos && generic_open' - i=find_closing_delimiter(src, generic_open, '<', '>')+1; - else - i=src.find(';', i); // ends on ; + i = find_closing_semi_colon_for_reference_type(src, i); break; } From b8d43abf7a6e011014f91f3b0f98e7ae9b1e86df Mon Sep 17 00:00:00 2001 From: thk123 Date: Tue, 24 Oct 2017 12:24:10 +0100 Subject: [PATCH 12/20] Pulled out method for parsing a list of multiple types --- src/java_bytecode/java_types.cpp | 110 +++++++++++++++++++++---------- 1 file changed, 74 insertions(+), 36 deletions(-) diff --git a/src/java_bytecode/java_types.cpp b/src/java_bytecode/java_types.cpp index fb768450aee..fad596dba71 100644 --- a/src/java_bytecode/java_types.cpp +++ b/src/java_bytecode/java_types.cpp @@ -22,6 +22,10 @@ Author: Daniel Kroening, kroening@kroening.com #include #endif +size_t find_closing_semi_colon_for_reference_type( + const std::string src, + size_t starting_point = 0); + typet java_int_type() { return signedbv_typet(32); @@ -223,6 +227,67 @@ std::string gather_full_class_name(const std::string &src) return class_name; } +/// Given a substring of a descriptor or signature that contains one or more +/// types parse out the individual types. This is used for parsing the +/// parameters of a function or the generic arguments contained within angle +/// brackets. +/// \param src: The input string that is wrapped in either ( ) or < > +/// \param class_name_prefix: The name of the class to use to prefix any found +/// generic types +/// \param opening_bracket: For checking string is passed in as expected, the +/// opening bracket (i.e. '(' or '<'). +/// \param closing_bracket: For checking string is passed in as expected, the +/// closing bracket (i.e. ')' or '>'). +/// \return A vector of types that the string represents +std::vector parse_list_types( + const std::string src, + const std::string class_name_prefix, + const char opening_bracket, + const char closing_bracket) +{ + PRECONDITION(src.size() >= 2); + PRECONDITION(src[0] == opening_bracket); + PRECONDITION(src[src.size() - 1] == closing_bracket); + + // Loop over the types in the given string, parsing each one in turn + // and adding to the type_list + std::vector type_list; + for(std::size_t i = 1; i < src.size() - 1; i++) + { + size_t start = i; + while(i < src.size()) + { + // parameter is an object type or instantiated generic type + if(src[i] == 'L') + { + i = find_closing_semi_colon_for_reference_type(src, i); + break; + } + + // parameter is an array + else if(src[i] == '[') + i++; + + // parameter is a type variable + else if(src[i] == 'T') + i = src.find(';', i); // ends on ; + + // type is an atomic type (just one character) + else + { + break; + } + } + + std::string sub_str = src.substr(start, i - start + 1); + const typet &new_type = java_type_from_string(sub_str, class_name_prefix); + INVARIANT(new_type != nil_typet(), "Failed to parse type"); + + type_list.push_back(new_type); + } + return type_list; +} + /// For parsing a class type reference /// \param src: The input string /// Either a signature: "Lpackage/class.innerclass; @@ -330,7 +395,7 @@ build_class_name(const std::string src, const std::string &class_name_prefix) /// See unit/java_bytecode/java_util_tests.cpp for more examples. size_t find_closing_semi_colon_for_reference_type( const std::string src, - size_t starting_point = 0) + size_t starting_point) { PRECONDITION(src[starting_point] == 'L'); size_t next_semi_colon = src.find(';', starting_point); @@ -426,42 +491,15 @@ typet java_type_from_string( std::string(src, e_pos+1, std::string::npos), class_name_prefix); - for(std::size_t i=1; i param_types = + parse_list_types(src.substr(0, e_pos + 1), class_name_prefix, '(', ')'); - size_t start=i; - - while(i Date: Tue, 24 Oct 2017 14:01:36 +0100 Subject: [PATCH 13/20] Parse multiple nested generic type informations Use the logic to parse multiple parameters for generic type parameters. --- src/java_bytecode/java_types.cpp | 120 ++++++++---------- .../parse_generic_inner_class.cpp | 21 ++- 2 files changed, 71 insertions(+), 70 deletions(-) diff --git a/src/java_bytecode/java_types.cpp b/src/java_bytecode/java_types.cpp index fad596dba71..010330f39e8 100644 --- a/src/java_bytecode/java_types.cpp +++ b/src/java_bytecode/java_types.cpp @@ -22,6 +22,12 @@ Author: Daniel Kroening, kroening@kroening.com #include #endif +std::vector parse_list_types( + const std::string src, + const std::string class_name_prefix, + const char opening_bracket, + const char closing_bracket); + size_t find_closing_semi_colon_for_reference_type( const std::string src, size_t starting_point = 0); @@ -171,10 +177,50 @@ exprt java_bytecode_promotion(const exprt &expr) return typecast_exprt(expr, new_type); } +/// Take a list of generic arguments and parse them into the generic type. +/// \param generic_type [out]: The existing generic type to add the information +/// to +/// \param parameters: The string representing the generic arguments for a +/// signature. For example (including wrapping angle +/// brackets). +/// \param class_name_prefix: The name of the class to use to prefix any found +/// generic types void add_generic_type_information( java_generic_typet &generic_type, - const std::string ¶meters) + const std::string ¶meters, + const std::string &class_name_prefix) { + PRECONDITION(parameters.size() >= 2); + PRECONDITION(parameters[0] == '<'); + PRECONDITION(parameters[parameters.size() - 1] == '>'); + + // parse contained types, can be either type variables, starting with T + // or instantiated types + std::vector params = + parse_list_types(parameters, class_name_prefix, '<', '>'); + + CHECK_RETURN(!params.empty()); // We should have at least one generic param + + // take these types - they should either be java_generic_parameters, in which + // case they can be directly added to the generic_type + // otherwise they should be wrapped in a java_generic_inst_parametert + + std::transform( + params.begin(), + params.end(), + std::back_inserter(generic_type.generic_type_variables()), + [](const typet &type) -> java_generic_parametert { + if(is_java_generic_parameter(type)) + { + return to_java_generic_parameter(type); + } + else + { + INVARIANT( + is_reference(type), "All generic parameters should be references"); + return java_generic_inst_parametert(to_symbol_type(type.subtype())); + } + }); } /// Take a signature string and remove everything in angle brackets allowing @@ -308,77 +354,23 @@ build_class_name(const std::string src, const std::string &class_name_prefix) symbol_typet symbol_type(identifier); symbol_type.set(ID_C_base_name, container_class); - // TODO(tkiley): The below code only parses the first generic argument list - std::size_t f_pos = src.find('<', 1); - // get generic type information if(f_pos != std::string::npos) { - std::size_t e_pos = find_closing_delimiter(src, f_pos, '<', '>'); - if(e_pos == std::string::npos) - throw unsupported_java_class_signature_exceptiont("recursive generic"); - - // What needs to happen is the symbol_typet needs to use all parts - // of the name and all generic parts of the child names - // For now it might be sufficient to parse the rest of the name - java_generic_typet result(symbol_type); - -#ifdef DEBUG - std::cout << "INFO: found generic type " - << src.substr(f_pos + 1, e_pos - f_pos - 1) << " in " << src - << " with container " << generic_container_class << "\n"; -#endif - - // parse contained types, can be either type variables, starting with T - // or instantiated types - size_t curr_start = f_pos + 1; - size_t curr_end; + // get generic type information do { - // find next end of type name - curr_end = src.find(';', curr_start); - INVARIANT( - curr_end != std::string::npos, "Type not terminated with \';\'"); - const size_t end = curr_end - curr_start + 1; - const typet &t = - java_type_from_string(src.substr(curr_start, end), class_name_prefix); -#ifdef DEBUG - std::cout << "INFO: getting type " << src.substr(curr_start, end) << "\n" - << "INFO: type id " << id2string(t.id()) << "\n" - << "curr_start " << curr_start << " curr_end " << curr_end - << " e_pos " << e_pos << " src " << src << "\n"; -#endif - // is an uninstantiated (pure) generic type - if(is_java_generic_parameter(t)) - { - const java_generic_parametert &gen_type = to_java_generic_parameter(t); -#ifdef DEBUG - std::cout << " generic type var " << gen_type.id() << " bound " - << to_symbol_type(gen_type.subtype()).get_identifier() - << "\n"; -#endif - result.generic_type_variables().push_back(gen_type); - } - - /// TODO(mgudemann): implement / test the case of iterated generic - /// types - - // is a concrete type, i.e., instantiation of a generic type of the - // current type - else - { - java_generic_inst_parametert inst_type(to_symbol_type(t.subtype())); -#ifdef DEBUG - std::cout << " instantiation of generic type var " - << to_symbol_type(t.subtype()).get_identifier() << "\n"; -#endif - result.generic_type_variables().push_back(inst_type); - } + std::size_t e_pos = find_closing_delimiter(src, f_pos, '<', '>'); + if(e_pos == std::string::npos) + throw unsupported_java_class_signature_exceptiont("recursive generic"); - curr_start = curr_end + 1; - } while(curr_start < e_pos); + add_generic_type_information( + result, src.substr(f_pos, e_pos - f_pos + 1), class_name_prefix); + // Look for the next generic type info (if it exists) + f_pos = src.find('<', e_pos + 1); + } while(f_pos != std::string::npos); return result; } diff --git a/unit/java_bytecode/java_bytecode_parse_generics/parse_generic_inner_class.cpp b/unit/java_bytecode/java_bytecode_parse_generics/parse_generic_inner_class.cpp index 1171fc4edf3..44a48f1b90e 100644 --- a/unit/java_bytecode/java_bytecode_parse_generics/parse_generic_inner_class.cpp +++ b/unit/java_bytecode/java_bytecode_parse_generics/parse_generic_inner_class.cpp @@ -219,12 +219,21 @@ SCENARIO( REQUIRE(is_java_generic_type(param_type.type())); const auto &generic_variables = to_java_generic_type(param_type.type()).generic_type_variables(); - REQUIRE(generic_variables.size() == 1); - const java_generic_parametert &generic_param = generic_variables[0]; - REQUIRE(is_java_generic_inst_parameter(generic_param)); - REQUIRE( - generic_param.type_variable() == - java_reference_type(symbol_typet("java::Foo"))); + REQUIRE(generic_variables.size() == 2); + { + const java_generic_parametert &generic_param = generic_variables[0]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + { + const java_generic_parametert &generic_param = generic_variables[1]; + REQUIRE(is_java_generic_inst_parameter(generic_param)); + REQUIRE( + generic_param == + java_generic_inst_parametert(symbol_typet("java::Foo"))); + } } } THEN("Method 4 should take a pointer to GenericClass$GenericInnerClass") From ee39620bbfea1ba3bf28d60e441105c28c40d88e Mon Sep 17 00:00:00 2001 From: thk123 Date: Tue, 24 Oct 2017 14:26:25 +0100 Subject: [PATCH 14/20] Extended tests to verify the generic information is being parsed correctly. --- .../GenericClass.class | Bin 2367 -> 2163 bytes .../GenericClass.java | 5 +- .../parse_generic_inner_class.cpp | 167 ++++++++++++++---- 3 files changed, 137 insertions(+), 35 deletions(-) diff --git a/unit/java_bytecode/java_bytecode_parse_generics/GenericClass.class b/unit/java_bytecode/java_bytecode_parse_generics/GenericClass.class index b006128a4772d3746a937b301bb3a1420285f8e8..7b85a00a3d10b5a4c133521239836322c43a90f4 100644 GIT binary patch literal 2163 zcmbVOYflqF6g{)hE+Cedf;>bhRSH6@Je4*TLxM4Be54KEm$Jf=c1ucY;=j^QCVub- z_@j(xW|!_xmqPKw&dj~{%sF%K-JR^8zrTJ1*g|dyeb_d!V`A6DD}kAkRkPpOuHA4d zpR9&ed+&06P_-^E1%?B|zBKV#V9bXG0TX)yLxC+2`{LNu6M^ZX4_0ic0Fo0JEIFsH z)oe9vfq4y=FPC$Lj0TP?!VQ514Vj9Zg~V$b#3PH~^2Nh?Jy#&yquvx)?P;Gpo7|Om zT&GzO=u5903G^S-PngF@(Q)k$t=h5OC|k!>dd7?Oid8+b8jiGmXMgj|VQmwdAW0{6 z@VASr4HFttr6kW#vzupimU%9%#lv-$hl@GxMXM>WsKX*ACJ%NN;6l8y7>8* z>a9k_K6K##OmU1Wr@$kwB{>0@ z#ti3K%`-;}c#L^UOS;c|3_cO6q@tSif<6^tc%tYfX%O>BWh_C2m)8Oji%9x1HZ7Tb zL}ppZEO^0)j1*j=prNt|P`aRpN)Y8qrzrFk&wS~w8LoHDPHm^!L+>acy!Q;5^8A2>1sYk+wbn literal 2367 zcmbVMYflqF6g{)0EFhMb7I}yTij)d10tKP9Xh<+7jgQdqeJLv}X}6@bCjKk^Wa0;Z zfIrH3W_RiCbXy*N*qym^&pqefnLG36@2}qgw(w>M5$x&6>nP}WEiip#)XaCLZ8ogR zC!=B1-rJlXRE?`Efnnb;uXGdz#yqI+(6KKtf^@bQ4olcx&hz0!s8)yP&2`z-tM7Es?_pDH!R)QqmDjaK9xJty@?J~Lll8Luv zGL*?;-K(a^b-@))eCI!S_PdUbd-huB?B$BwHFt+za<+lZTRDsO;M^i-?hc#>_u$-S fg>J?>ctw5 InnerClass field; GenericInnerClass field2; - SameGenericParamInnerClass field3; - - GenericInnerClass field4; - SameGenericParamInnerClass field5; + GenericInnerClass field3; void method(InnerClass input) { diff --git a/unit/java_bytecode/java_bytecode_parse_generics/parse_generic_inner_class.cpp b/unit/java_bytecode/java_bytecode_parse_generics/parse_generic_inner_class.cpp index 44a48f1b90e..1d0b428d611 100644 --- a/unit/java_bytecode/java_bytecode_parse_generics/parse_generic_inner_class.cpp +++ b/unit/java_bytecode/java_bytecode_parse_generics/parse_generic_inner_class.cpp @@ -45,7 +45,17 @@ SCENARIO( require_type::require_pointer( field_component.type(), symbol_typet("java::GenericClass$InnerClass")); - // TODO: then the generic type should be correctly stored + THEN("The pointer should be generic") + { + REQUIRE(is_java_generic_type(field_component.type())); + const auto &generic_variables = + to_java_generic_type(field_component.type()).generic_type_variables(); + REQUIRE(generic_variables.size() == 1); + const java_generic_parametert &generic_param = generic_variables[0]; + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } } THEN("The field component should be a pointer to GenericClass$InnerClass") @@ -57,7 +67,27 @@ SCENARIO( field_component.type(), symbol_typet("java::GenericClass$GenericInnerClass")); - // TODO: then the generic type should be correctly stored + THEN("The pointer should be generic") + { + REQUIRE(is_java_generic_type(field_component.type())); + const auto &generic_variables = + to_java_generic_type(field_component.type()).generic_type_variables(); + REQUIRE(generic_variables.size() == 2); + { + const java_generic_parametert &generic_param = generic_variables[0]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + { + const java_generic_parametert &generic_param = generic_variables[1]; + REQUIRE(is_java_generic_inst_parameter(generic_param)); + REQUIRE( + generic_param == + java_generic_inst_parametert(symbol_typet("java::Foo"))); + } + } } THEN("The field component should be a pointer to GenericClass$InnerClass") @@ -65,40 +95,35 @@ SCENARIO( const struct_typet::componentt &field_component = require_type::require_component(class_type, "field3"); - require_type::require_pointer( - field_component.type(), - symbol_typet("java::GenericClass$SameGenericParamInnerClass")); - - // TODO: then the generic type should be correctly stored - } - - THEN("The field component should be a pointer to GenericClass$InnerClass") - { - const struct_typet::componentt &field_component = - require_type::require_component(class_type, "field4"); - require_type::require_pointer( field_component.type(), symbol_typet("java::GenericClass$GenericInnerClass")); - // TODO: then the generic type should be correctly stored - } - THEN("The field component should be a pointer to GenericClass$InnerClass") - { - const struct_typet::componentt &field_component = - require_type::require_component(class_type, "field5"); - - require_type::require_pointer( - field_component.type(), - symbol_typet("java::GenericClass$SameGenericParamInnerClass")); - - // TODO: then the generic type should be correctly stored + THEN("The pointer should be generic") + { + REQUIRE(is_java_generic_type(field_component.type())); + const auto &generic_variables = + to_java_generic_type(field_component.type()).generic_type_variables(); + REQUIRE(generic_variables.size() == 2); + { + const java_generic_parametert &generic_param = generic_variables[0]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + { + const java_generic_parametert &generic_param = generic_variables[1]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + } } } - THEN("There should be a symbol for the generic inner class") - { - } + // TODO: add fields of doubly nested generic classes } SCENARIO( @@ -254,6 +279,28 @@ SCENARIO( require_type::require_parameter(function_call, "input"); require_type::require_pointer( param_type.type(), symbol_typet("java::GenericClass$GenericInnerClass")); + + THEN("The pointer should be generic") + { + REQUIRE(is_java_generic_type(param_type.type())); + const auto &generic_variables = + to_java_generic_type(param_type.type()).generic_type_variables(); + REQUIRE(generic_variables.size() == 2); + { + const java_generic_parametert &generic_param = generic_variables[0]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + { + const java_generic_parametert &generic_param = generic_variables[1]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + } } THEN("Method 5 should return a GenericClass$InnerClass") { @@ -272,8 +319,20 @@ SCENARIO( require_type::require_pointer( function_call.return_type(), symbol_typet("java::GenericClass$InnerClass")); + + THEN("The pointer should be generic") + { + REQUIRE(is_java_generic_type(function_call.return_type())); + const auto &generic_variables = + to_java_generic_type(function_call.return_type()) + .generic_type_variables(); + REQUIRE(generic_variables.size() == 1); + const java_generic_parametert &generic_param = generic_variables[0]; + REQUIRE( + generic_param.type_variable() == symbol_typet("java::GenericClass::T")); + } } - THEN("Method 6 should return a GenericClass$InnerClass") + THEN("Method 6 should return a GenericClass$GenericInnerClass") { const std::string func_name = ".method6"; const std::string func_descriptor = ":()LGenericClass$GenericInnerClass;"; @@ -290,8 +349,31 @@ SCENARIO( require_type::require_pointer( function_call.return_type(), symbol_typet("java::GenericClass$GenericInnerClass")); + + THEN("The pointer should be generic") + { + REQUIRE(is_java_generic_type(function_call.return_type())); + const auto &generic_variables = + to_java_generic_type(function_call.return_type()) + .generic_type_variables(); + REQUIRE(generic_variables.size() == 2); + { + const java_generic_parametert &generic_param = generic_variables[0]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + { + const java_generic_parametert &generic_param = generic_variables[1]; + REQUIRE(is_java_generic_inst_parameter(generic_param)); + REQUIRE( + generic_param == + java_generic_inst_parametert(symbol_typet("java::Foo"))); + } + } } - THEN("Method 7 should return a GenericClass$InnerClass") + THEN("Method 7 should return a GenericClass$GenericInnerClass") { const std::string func_name = ".method7"; const std::string func_descriptor = ":()LGenericClass$GenericInnerClass;"; @@ -308,5 +390,28 @@ SCENARIO( require_type::require_pointer( function_call.return_type(), symbol_typet("java::GenericClass$GenericInnerClass")); + + THEN("The pointer should be generic") + { + REQUIRE(is_java_generic_type(function_call.return_type())); + const auto &generic_variables = + to_java_generic_type(function_call.return_type()) + .generic_type_variables(); + REQUIRE(generic_variables.size() == 2); + { + const java_generic_parametert &generic_param = generic_variables[0]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + { + const java_generic_parametert &generic_param = generic_variables[1]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + } } } From 4a62b070b90ce44bdac063b1f0a0cc29ad861015 Mon Sep 17 00:00:00 2001 From: thk123 Date: Tue, 24 Oct 2017 14:37:29 +0100 Subject: [PATCH 15/20] Simplifying method names for unit test --- .../GenericClass.class | Bin 2163 -> 2175 bytes .../GenericClass.java | 6 +++--- .../parse_generic_inner_class.cpp | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/unit/java_bytecode/java_bytecode_parse_generics/GenericClass.class b/unit/java_bytecode/java_bytecode_parse_generics/GenericClass.class index 7b85a00a3d10b5a4c133521239836322c43a90f4..21d7734bc0b9c58f8fb4df955c9493675b958094 100644 GIT binary patch delta 84 zcmew?@Lym<0J9)>QEEwiZfZ$Jeu^O@gS3X`;glgpS@k!6e+8I*uB2bdHlpI|oT UN77@=$e=U%G_(3 input) } - InnerClass method5() + InnerClass ret_method1() { return null; } - GenericInnerClass method6() + GenericInnerClass ret_method2() { return null; } - GenericInnerClass method7() + GenericInnerClass ret_method3() { return null; } diff --git a/unit/java_bytecode/java_bytecode_parse_generics/parse_generic_inner_class.cpp b/unit/java_bytecode/java_bytecode_parse_generics/parse_generic_inner_class.cpp index 1d0b428d611..23e88e06510 100644 --- a/unit/java_bytecode/java_bytecode_parse_generics/parse_generic_inner_class.cpp +++ b/unit/java_bytecode/java_bytecode_parse_generics/parse_generic_inner_class.cpp @@ -302,9 +302,9 @@ SCENARIO( } } } - THEN("Method 5 should return a GenericClass$InnerClass") + THEN("Ret Method 1 should return a GenericClass$InnerClass") { - const std::string func_name = ".method5"; + const std::string func_name = ".ret_method1"; const std::string func_descriptor = ":()LGenericClass$InnerClass;"; const std::string process_func_name = class_prefix + func_name + func_descriptor; @@ -332,9 +332,9 @@ SCENARIO( generic_param.type_variable() == symbol_typet("java::GenericClass::T")); } } - THEN("Method 6 should return a GenericClass$GenericInnerClass") + THEN("Ret method 2 should return a GenericClass$GenericInnerClass") { - const std::string func_name = ".method6"; + const std::string func_name = ".ret_method2"; const std::string func_descriptor = ":()LGenericClass$GenericInnerClass;"; const std::string process_func_name = class_prefix + func_name + func_descriptor; @@ -373,9 +373,9 @@ SCENARIO( } } } - THEN("Method 7 should return a GenericClass$GenericInnerClass") + THEN("Ret method 3 should return a GenericClass$GenericInnerClass") { - const std::string func_name = ".method7"; + const std::string func_name = ".ret_method3"; const std::string func_descriptor = ":()LGenericClass$GenericInnerClass;"; const std::string process_func_name = class_prefix + func_name + func_descriptor; From 592282693f93f690cf11f86686c614f4ab3d59ae Mon Sep 17 00:00:00 2001 From: thk123 Date: Tue, 24 Oct 2017 15:01:26 +0100 Subject: [PATCH 16/20] Adding doubly nested generic tests --- .../GenericClass.class | Bin 2175 -> 4622 bytes .../GenericClass.java | 45 +- .../parse_generic_inner_class.cpp | 528 +++++++++++++++++- 3 files changed, 567 insertions(+), 6 deletions(-) diff --git a/unit/java_bytecode/java_bytecode_parse_generics/GenericClass.class b/unit/java_bytecode/java_bytecode_parse_generics/GenericClass.class index 21d7734bc0b9c58f8fb4df955c9493675b958094..3b60626bea696f3bef865c8814faf32b424b7d66 100644 GIT binary patch literal 4622 zcmbW4Z&wpX6vm$kgjJ*{(xRdwXsUrisPb15qZowRM!+W0*8brvVT-XL@r0!3_^sMk zJ*O}F0s5hO`pj;!JCn_3H@rw@XXf7DbMMT(VfpvJzyATSjKg6J;5!4~8~DM%TY<@f zd1~!gRjXbu?VEM;^ktRLxr%vqCNSbntTO{Y3XHW(y$u6D2@HF4f%vawt8y%GBi}N% zX$j9HEpV|=KB=0GX5A8)((E$DVmh1BjE8LCBY|nnGU1Db;Cao&f%)t*`R!UQou#>h zUJ{s#(~ohU`-?XgH3dIBtEBk_Rb*{NO4kI5!j4` zvyVLlwrb!Pf!jVS^_~T?nqHK|Re$FHS_AZ(zY6QT=rw^?k>K4=LqoS~AyclF8(D#Y z5)}0nnx9`T+P=?X63-Fm*u>*IM{esW}l5}G4TW@bmNa7 z+79EIrF}HA9#5^tyBb^WR#Mwndrk%yFIPV_8v--BnRCKk?WU!z;V{{CE4sA`?Ws0o zs}b;22l(VPif>m5hJAWZXZfqk4t`O$a+s~#^YUz0hoZ17un^7HzJA4%jkckC`eO@f zd5PK9oqLBI)b&a{s{&imklkzD{?S$|fxWY-1EVCUwv0_H>WqH#RC!(QZ(U$Nnn3rg zHN1Ppm+DsI(CLSV0ymShTE(gj!M{-*yS{oBP;l&yyB!YxQ)P#zI*fWB8yyr4cFlZ5 zcRORvnGNx{!`UBTP+beW=DLIxOep4DmBts{}NyG3bkgvWNnw_ zAGG$XpnaSv2^hg$+~ZrLsv>ZulP&N-wMg`O1D(<=TNCdWT;Q5!pzf?_J3iNdnia_O z>!;dd1~I;20|q~rFam*3_!Pq^zc1s8J0Iixsxlwv{8L0ljs|#uB*$58WsWo83(PZgiF?gY zj2|B=X`?2b%@&GgSg`RD>g_pDxi?A+=Uh^>Sj3kt8H+P{dOn#2TV~qX^vOuUhZKz2 zS$I~upkp;kD@Xbfg_ig~QAmbMff>r%49alK&2WV>tKl;1=aLzBW!5OOj>loyJPDMM zwHK7lbysGCGU;%c=jW1{bY(J>d4g0Aypoll*$8f-3b+9tx800L@ XXM^Jfju$x&bNq}e6C7`Gybb&ho&OlJ literal 2175 zcmbVOYflqF6g{)hE-%YVK^`KHDg~jHr%IcOA;Fk5K0?D+vy>H^>@AjsD{{H$6U=#U1bYaWHwuu)eUJ6VeST*~t?b@DG z`Dl4o?VZd0Ue&s|5Euvz`@+O4f#Coe22AV-^o6!S;)`Qfj|C=50oZ3thmgEL?}2mT zT8*Y>3(RP^Lb;qTW;JlqCtMep)sX4fT}Zs5K@?dGS19e*>-i$#ih4s}xubLPZt_&Y zah*m{pewUSGJC-^o*416{~t^d5*LLXLsY&VQo{IAW6q` z@VE2J1EU(!Pf6aPW;agjEc0|mi%07$FPCuK^JYU}PKU+HEU~8IWTm%sbd>Z)wWO~` zg4a4hOip8oMI)DCZx(c}Yx*wnIUS`q+qOQUuqm+G*;&8+@?^hfH$JOktqDwJmb7%D z)#69n5{)IR(Xn*%EeEd2S0LL0x+PsrgC$+*@ObdSXA0$=(wX(u%2h4*B=_Oy%&s)@ z#kh;3;Go|0D)zo3e*l-`!k++HNicx>c)-aL9Ss6Qt=s~OQAGNtDK(=ZV;vV5*8t&Y z!5j>ZL-cmVp%CgBZH9hiQ*Q>{0YE#zgoYW9$uA!8Nh`6XFLqT^Q#(={p4; zaxcjVzyv0_PHCQLTEHXBP+HP`;$!fMQO)~QV=5R>5r)S;y(A4|id4o@M5w%GiI~HD zAY;>#*~erSeVJJmjLAsB1qvE|79mO(RHzhDN;*xUCwLl2x6N?1ZHBTN?J^v`%5ae~ zncHR7|4U}{N@j^N%Xk)KQ>0&Z*AUF534T-xv diff --git a/unit/java_bytecode/java_bytecode_parse_generics/GenericClass.java b/unit/java_bytecode/java_bytecode_parse_generics/GenericClass.java index a14cd0d68a2..8c13ed4f0db 100644 --- a/unit/java_bytecode/java_bytecode_parse_generics/GenericClass.java +++ b/unit/java_bytecode/java_bytecode_parse_generics/GenericClass.java @@ -13,7 +13,7 @@ class DoublyNestedInnerClass } - class DoublyNestedInnerGemericClass + class DoublyNestedInnerGenericClass { T field; } @@ -28,6 +28,12 @@ class SameGenericParamInnerClass GenericInnerClass field2; GenericInnerClass field3; + GenericInnerClass.DoublyNestedInnerClass field4; + GenericInnerClass.DoublyNestedInnerClass field5; + + GenericInnerClass.DoublyNestedInnerGenericClass field6; + GenericInnerClass.DoublyNestedInnerGenericClass field7; + void method(InnerClass input) { @@ -49,6 +55,26 @@ void method4(GenericInnerClass input) } + void method5(GenericInnerClass.DoublyNestedInnerClass input) + { + + } + + void method6(GenericInnerClass.DoublyNestedInnerClass input) + { + + } + + void method7(GenericInnerClass.DoublyNestedInnerGenericClass input) + { + + } + + void method8(GenericInnerClass.DoublyNestedInnerGenericClass input) + { + + } + InnerClass ret_method1() { return null; @@ -63,4 +89,21 @@ GenericInnerClass ret_method3() { return null; } + + GenericInnerClass.DoublyNestedInnerClass ret_method4() + { + return null; + } + GenericInnerClass.DoublyNestedInnerClass ret_method5() + { + return null; + } + GenericInnerClass.DoublyNestedInnerGenericClass ret_method6() + { + return null; + } + GenericInnerClass.DoublyNestedInnerGenericClass ret_method7() + { + return null; + } } diff --git a/unit/java_bytecode/java_bytecode_parse_generics/parse_generic_inner_class.cpp b/unit/java_bytecode/java_bytecode_parse_generics/parse_generic_inner_class.cpp index 23e88e06510..acb09972a88 100644 --- a/unit/java_bytecode/java_bytecode_parse_generics/parse_generic_inner_class.cpp +++ b/unit/java_bytecode/java_bytecode_parse_generics/parse_generic_inner_class.cpp @@ -90,14 +90,52 @@ SCENARIO( } } - THEN("The field component should be a pointer to GenericClass$InnerClass") + THEN( + "The field component should be a pointer to " + "GenericClass$GenericInnerClass$DoublyNestedInnerClass") { const struct_typet::componentt &field_component = - require_type::require_component(class_type, "field3"); + require_type::require_component(class_type, "field4"); require_type::require_pointer( field_component.type(), - symbol_typet("java::GenericClass$GenericInnerClass")); + symbol_typet( + "java::GenericClass$GenericInnerClass$DoublyNestedInnerClass")); + + THEN("The pointer should be generic") + { + REQUIRE(is_java_generic_type(field_component.type())); + const auto &generic_variables = + to_java_generic_type(field_component.type()).generic_type_variables(); + REQUIRE(generic_variables.size() == 2); + { + const java_generic_parametert &generic_param = generic_variables[0]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + { + const java_generic_parametert &generic_param = generic_variables[1]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param == + java_generic_inst_parametert(symbol_typet("java::Foo"))); + } + } + } + + THEN( + "The field component should be a pointer to " + "GenericClass$GenericInnerClass$DoublyNestedInnerClass") + { + const struct_typet::componentt &field_component = + require_type::require_component(class_type, "field5"); + + require_type::require_pointer( + field_component.type(), + symbol_typet( + "java::GenericClass$GenericInnerClass$DoublyNestedInnerClass")); THEN("The pointer should be generic") { @@ -121,9 +159,93 @@ SCENARIO( } } } - } - // TODO: add fields of doubly nested generic classes + THEN( + "The field component should be a pointer to " + "GenericClass$GenericInnerClass$DoublyNestedInnerGenericClass") + { + const struct_typet::componentt &field_component = + require_type::require_component(class_type, "field6"); + + require_type::require_pointer( + field_component.type(), + symbol_typet( + "java::GenericClass$GenericInnerClass$" + "DoublyNestedInnerGenericClass")); + + THEN("The pointer should be generic") + { + REQUIRE(is_java_generic_type(field_component.type())); + const auto &generic_variables = + to_java_generic_type(field_component.type()).generic_type_variables(); + REQUIRE(generic_variables.size() == 3); + { + const java_generic_parametert &generic_param = generic_variables[0]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + { + const java_generic_parametert &generic_param = generic_variables[1]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param == + java_generic_inst_parametert(symbol_typet("java::Foo"))); + } + { + const java_generic_parametert &generic_param = generic_variables[2]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param == + java_generic_inst_parametert(symbol_typet("java::Foo"))); + } + } + } + + THEN( + "The field component should be a pointer to " + "GenericClass$GenericInnerClass$DoublyNestedInnerGenericClass") + { + const struct_typet::componentt &field_component = + require_type::require_component(class_type, "field7"); + + require_type::require_pointer( + field_component.type(), + symbol_typet( + "java::GenericClass$GenericInnerClass$" + "DoublyNestedInnerGenericClass")); + + THEN("The pointer should be generic") + { + REQUIRE(is_java_generic_type(field_component.type())); + const auto &generic_variables = + to_java_generic_type(field_component.type()).generic_type_variables(); + REQUIRE(generic_variables.size() == 3); + { + const java_generic_parametert &generic_param = generic_variables[0]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + { + const java_generic_parametert &generic_param = generic_variables[1]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + { + const java_generic_parametert &generic_param = generic_variables[2]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + } + } + } } SCENARIO( @@ -302,6 +424,206 @@ SCENARIO( } } } + THEN( + "Method 5 should take a pointer to " + "GenericClass$GenericInnerClass$DoublyNestedInnerClass") + { + const std::string func_name = ".method5"; + const std::string func_descriptor = + ":(LGenericClass$GenericInnerClass$DoublyNestedInnerClass;)V"; + const std::string process_func_name = + class_prefix + func_name + func_descriptor; + + REQUIRE(new_symbol_table.has_symbol(process_func_name)); + const symbolt &function_symbol = + new_symbol_table.lookup_ref(process_func_name); + + const code_typet &function_call = + require_type::require_code(function_symbol.type); + + const auto param_type = + require_type::require_parameter(function_call, "input"); + require_type::require_pointer( + param_type.type(), + symbol_typet( + "java::GenericClass$GenericInnerClass$DoublyNestedInnerClass")); + + THEN("The pointer should be generic") + { + REQUIRE(is_java_generic_type(param_type.type())); + const auto &generic_variables = + to_java_generic_type(param_type.type()).generic_type_variables(); + REQUIRE(generic_variables.size() == 2); + { + const java_generic_parametert &generic_param = generic_variables[0]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + { + const java_generic_parametert &generic_param = generic_variables[1]; + REQUIRE(is_java_generic_inst_parameter(generic_param)); + REQUIRE( + generic_param == + java_generic_inst_parametert(symbol_typet("java::Foo"))); + } + } + } + THEN( + "Method 6 should take a pointer to " + "GenericClass$GenericInnerClass$DoublyNestedInnerClass") + { + const std::string func_name = ".method6"; + const std::string func_descriptor = + ":(LGenericClass$GenericInnerClass$DoublyNestedInnerClass;)V"; + const std::string process_func_name = + class_prefix + func_name + func_descriptor; + + REQUIRE(new_symbol_table.has_symbol(process_func_name)); + const symbolt &function_symbol = + new_symbol_table.lookup_ref(process_func_name); + + const code_typet &function_call = + require_type::require_code(function_symbol.type); + + const auto param_type = + require_type::require_parameter(function_call, "input"); + require_type::require_pointer( + param_type.type(), + symbol_typet( + "java::GenericClass$GenericInnerClass$DoublyNestedInnerClass")); + + THEN("The pointer should be generic") + { + REQUIRE(is_java_generic_type(param_type.type())); + const auto &generic_variables = + to_java_generic_type(param_type.type()).generic_type_variables(); + REQUIRE(generic_variables.size() == 2); + { + const java_generic_parametert &generic_param = generic_variables[0]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + { + const java_generic_parametert &generic_param = generic_variables[1]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + } + } + THEN( + "Method 7 should take a pointer to " + "GenericClass$GenericInnerClass$DoublyNestedInnerGenericClass") + { + const std::string func_name = ".method7"; + const std::string func_descriptor = + ":(LGenericClass$GenericInnerClass$DoublyNestedInnerGenericClass;)V"; + const std::string process_func_name = + class_prefix + func_name + func_descriptor; + + REQUIRE(new_symbol_table.has_symbol(process_func_name)); + const symbolt &function_symbol = + new_symbol_table.lookup_ref(process_func_name); + + const code_typet &function_call = + require_type::require_code(function_symbol.type); + + const auto param_type = + require_type::require_parameter(function_call, "input"); + require_type::require_pointer( + param_type.type(), + symbol_typet( + "java::GenericClass$GenericInnerClass$" + "DoublyNestedInnerGenericClass")); + + THEN("The pointer should be generic") + { + REQUIRE(is_java_generic_type(param_type.type())); + const auto &generic_variables = + to_java_generic_type(param_type.type()).generic_type_variables(); + REQUIRE(generic_variables.size() == 3); + { + const java_generic_parametert &generic_param = generic_variables[0]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + { + const java_generic_parametert &generic_param = generic_variables[1]; + REQUIRE(is_java_generic_inst_parameter(generic_param)); + REQUIRE( + generic_param == + java_generic_inst_parametert(symbol_typet("java::Foo"))); + } + { + const java_generic_parametert &generic_param = generic_variables[2]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param == + java_generic_inst_parametert(symbol_typet("java::Foo"))); + } + } + } + THEN( + "Method 8 should take a pointer to " + "GenericClass$GenericInnerClass$DoublyNestedInnerGenericClass") + { + const std::string func_name = ".method8"; + const std::string func_descriptor = + ":(LGenericClass$GenericInnerClass$DoublyNestedInnerGenericClass;)V"; + const std::string process_func_name = + class_prefix + func_name + func_descriptor; + + REQUIRE(new_symbol_table.has_symbol(process_func_name)); + const symbolt &function_symbol = + new_symbol_table.lookup_ref(process_func_name); + + const code_typet &function_call = + require_type::require_code(function_symbol.type); + + const auto param_type = + require_type::require_parameter(function_call, "input"); + require_type::require_pointer( + param_type.type(), + symbol_typet( + "java::GenericClass$GenericInnerClass$" + "DoublyNestedInnerGenericClass")); + + THEN("The pointer should be generic") + { + REQUIRE(is_java_generic_type(param_type.type())); + const auto &generic_variables = + to_java_generic_type(param_type.type()).generic_type_variables(); + REQUIRE(generic_variables.size() == 3); + { + const java_generic_parametert &generic_param = generic_variables[0]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + { + const java_generic_parametert &generic_param = generic_variables[1]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + { + const java_generic_parametert &generic_param = generic_variables[2]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + } + } THEN("Ret Method 1 should return a GenericClass$InnerClass") { const std::string func_name = ".ret_method1"; @@ -414,4 +736,200 @@ SCENARIO( } } } + THEN( + "Ret method 4 should return a " + "GenericClass$GenericInnerClass$DoublyNestedInnerClass") + { + const std::string func_name = ".ret_method4"; + const std::string func_descriptor = + ":()LGenericClass$GenericInnerClass$DoublyNestedInnerClass;"; + const std::string process_func_name = + class_prefix + func_name + func_descriptor; + + REQUIRE(new_symbol_table.has_symbol(process_func_name)); + const symbolt &function_symbol = + new_symbol_table.lookup_ref(process_func_name); + + const code_typet &function_call = + require_type::require_code(function_symbol.type); + + require_type::require_pointer( + function_call.return_type(), + symbol_typet( + "java::GenericClass$GenericInnerClass$DoublyNestedInnerClass")); + + THEN("The pointer should be generic") + { + REQUIRE(is_java_generic_type(function_call.return_type())); + const auto &generic_variables = + to_java_generic_type(function_call.return_type()) + .generic_type_variables(); + REQUIRE(generic_variables.size() == 2); + { + const java_generic_parametert &generic_param = generic_variables[0]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + { + const java_generic_parametert &generic_param = generic_variables[1]; + REQUIRE(is_java_generic_inst_parameter(generic_param)); + REQUIRE( + generic_param == + java_generic_inst_parametert(symbol_typet("java::Foo"))); + } + } + } + THEN( + "Ret method 5 should return a " + "GenericClass$GenericInnerClass$DoublyNestedInnerClass") + { + const std::string func_name = ".ret_method5"; + const std::string func_descriptor = + ":()LGenericClass$GenericInnerClass$DoublyNestedInnerClass;"; + const std::string process_func_name = + class_prefix + func_name + func_descriptor; + + REQUIRE(new_symbol_table.has_symbol(process_func_name)); + const symbolt &function_symbol = + new_symbol_table.lookup_ref(process_func_name); + + const code_typet &function_call = + require_type::require_code(function_symbol.type); + + require_type::require_pointer( + function_call.return_type(), + symbol_typet( + "java::GenericClass$GenericInnerClass$DoublyNestedInnerClass")); + + THEN("The pointer should be generic") + { + REQUIRE(is_java_generic_type(function_call.return_type())); + const auto &generic_variables = + to_java_generic_type(function_call.return_type()) + .generic_type_variables(); + REQUIRE(generic_variables.size() == 2); + { + const java_generic_parametert &generic_param = generic_variables[0]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + { + const java_generic_parametert &generic_param = generic_variables[1]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + } + } + THEN( + "Ret method 6 should return a " + "GenericClass$GenericInnerClass$DoublyNestedInnerGenericClass") + { + const std::string func_name = ".ret_method6"; + const std::string func_descriptor = + ":()LGenericClass$GenericInnerClass$DoublyNestedInnerGenericClass;"; + const std::string process_func_name = + class_prefix + func_name + func_descriptor; + + REQUIRE(new_symbol_table.has_symbol(process_func_name)); + const symbolt &function_symbol = + new_symbol_table.lookup_ref(process_func_name); + + const code_typet &function_call = + require_type::require_code(function_symbol.type); + + require_type::require_pointer( + function_call.return_type(), + symbol_typet( + "java::GenericClass$GenericInnerClass$" + "DoublyNestedInnerGenericClass")); + + THEN("The pointer should be generic") + { + REQUIRE(is_java_generic_type(function_call.return_type())); + const auto &generic_variables = + to_java_generic_type(function_call.return_type()) + .generic_type_variables(); + REQUIRE(generic_variables.size() == 3); + { + const java_generic_parametert &generic_param = generic_variables[0]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + { + const java_generic_parametert &generic_param = generic_variables[1]; + REQUIRE(is_java_generic_inst_parameter(generic_param)); + REQUIRE( + generic_param == + java_generic_inst_parametert(symbol_typet("java::Foo"))); + } + { + const java_generic_parametert &generic_param = generic_variables[2]; + REQUIRE(is_java_generic_inst_parameter(generic_param)); + REQUIRE( + generic_param == + java_generic_inst_parametert(symbol_typet("java::Foo"))); + } + } + } + THEN( + "Ret method 7 should return a " + "GenericClass$GenericInnerClass$DoublyNestedInnerGenericClass") + { + const std::string func_name = ".ret_method7"; + const std::string func_descriptor = + ":()LGenericClass$GenericInnerClass$DoublyNestedInnerGenericClass;"; + const std::string process_func_name = + class_prefix + func_name + func_descriptor; + + REQUIRE(new_symbol_table.has_symbol(process_func_name)); + const symbolt &function_symbol = + new_symbol_table.lookup_ref(process_func_name); + + const code_typet &function_call = + require_type::require_code(function_symbol.type); + + require_type::require_pointer( + function_call.return_type(), + symbol_typet( + "java::GenericClass$GenericInnerClass$" + "DoublyNestedInnerGenericClass")); + + THEN("The pointer should be generic") + { + REQUIRE(is_java_generic_type(function_call.return_type())); + const auto &generic_variables = + to_java_generic_type(function_call.return_type()) + .generic_type_variables(); + REQUIRE(generic_variables.size() == 3); + { + const java_generic_parametert &generic_param = generic_variables[0]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + { + const java_generic_parametert &generic_param = generic_variables[1]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + { + const java_generic_parametert &generic_param = generic_variables[2]; + REQUIRE(is_java_generic_parameter(generic_param)); + REQUIRE( + generic_param.type_variable() == + symbol_typet("java::GenericClass::T")); + } + } + } } From 9f87a806555910eb68d878eea6d1d6e63915ab4b Mon Sep 17 00:00:00 2001 From: thk123 Date: Tue, 24 Oct 2017 18:12:50 +0100 Subject: [PATCH 17/20] Assertion on unmatched ; for parsing reference types --- src/java_bytecode/java_types.cpp | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/java_bytecode/java_types.cpp b/src/java_bytecode/java_types.cpp index 010330f39e8..2743ad9dba2 100644 --- a/src/java_bytecode/java_types.cpp +++ b/src/java_bytecode/java_types.cpp @@ -341,13 +341,13 @@ std::vector parse_list_types( /// \param class_name_prefix: The name of the class to use to prefix any found /// generic types /// \return The reference type if parsed correctly, no value if parsing fails. -optionalt -build_class_name(const std::string src, const std::string &class_name_prefix) +reference_typet +build_class_name(const std::string &src, const std::string &class_name_prefix) { PRECONDITION(src[0] == 'L'); - // ends on ; - if(src[src.size() - 1] != ';') - return optionalt(); + + // All reference types must end on a ; + PRECONDITION(src[src.size() - 1] == ';'); std::string container_class = gather_full_class_name(src); std::string identifier = "java::" + container_class; @@ -536,12 +536,7 @@ typet java_type_from_string( } case 'L': { - const optionalt &class_type = - build_class_name(src, class_name_prefix); - if(class_type) - return class_type.value(); - else - return nil_typet(); + return build_class_name(src, class_name_prefix); } case '*': case '+': From 0b3058f947b385c8b2d4c07d52575ee3190a4dc4 Mon Sep 17 00:00:00 2001 From: thk123 Date: Tue, 24 Oct 2017 18:14:11 +0100 Subject: [PATCH 18/20] Tightened up exception for unknown handling We don't have a test that exercises this exception so modified the message to not mislead and say to do with recursive generic types. --- src/java_bytecode/java_types.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/java_bytecode/java_types.cpp b/src/java_bytecode/java_types.cpp index 2743ad9dba2..92b2ab27dcb 100644 --- a/src/java_bytecode/java_types.cpp +++ b/src/java_bytecode/java_types.cpp @@ -363,7 +363,8 @@ build_class_name(const std::string &src, const std::string &class_name_prefix) { std::size_t e_pos = find_closing_delimiter(src, f_pos, '<', '>'); if(e_pos == std::string::npos) - throw unsupported_java_class_signature_exceptiont("recursive generic"); + throw unsupported_java_class_signature_exceptiont( + "Parsing type with unmatched generic bracket: " + src); add_generic_type_information( result, src.substr(f_pos, e_pos - f_pos + 1), class_name_prefix); From 3beab8b270421a217273855356385b1e2026d0d9 Mon Sep 17 00:00:00 2001 From: thk123 Date: Tue, 24 Oct 2017 18:14:31 +0100 Subject: [PATCH 19/20] Fixed error in find_closing_delimiter Assumed that the bracket was always an angle bracket --- src/java_bytecode/java_utils.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/java_bytecode/java_utils.cpp b/src/java_bytecode/java_utils.cpp index 8f366c324e3..e3b20a51224 100644 --- a/src/java_bytecode/java_utils.cpp +++ b/src/java_bytecode/java_utils.cpp @@ -214,9 +214,9 @@ size_t find_closing_delimiter( while(c_pos<=end_pos) { - if(src[c_pos]=='<') + if(src[c_pos] == open_char) depth++; - else if(src[c_pos]=='>') + else if(src[c_pos] == close_char) { if(depth==0) return c_pos; From f892f4a9ceafacfc9e3907a5d2db0f38d0c6dedc Mon Sep 17 00:00:00 2001 From: thk123 Date: Wed, 25 Oct 2017 10:40:07 +0100 Subject: [PATCH 20/20] Added tests for bracket matcher to include different types of brackets --- unit/java_bytecode/java_utils_test.cpp | 35 ++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/unit/java_bytecode/java_utils_test.cpp b/unit/java_bytecode/java_utils_test.cpp index 23b8b4927d1..b568b8c6d01 100644 --- a/unit/java_bytecode/java_utils_test.cpp +++ b/unit/java_bytecode/java_utils_test.cpp @@ -63,6 +63,41 @@ SCENARIO("Test that the generic signature delimiter lookup works reliably", REQUIRE(find_closing_delimiter(generic_sigs[6], 9, '<', '>')==17); } } + GIVEN("Some bracketed functions") + { + std::vector bracket_sigs{ + // Valid inputs + "(Entry)", + "Something(Else)", + "(Nested(Bracket))", + // Invalid inputs + "(", + "(Integer>", + }; + WHEN("We check if the closing tag is recognised correctly") + { + // TEST VALID CASES + + // (Entry) + REQUIRE(find_closing_delimiter(bracket_sigs[0], 0, '(', ')') == 6); + // Something(Else) + REQUIRE(find_closing_delimiter(bracket_sigs[1], 9, '(', ')') == 14); + // (Nested(Bracket)) + REQUIRE(find_closing_delimiter(bracket_sigs[2], 0, '(', ')') == 16); + REQUIRE(find_closing_delimiter(bracket_sigs[2], 7, '(', ')') == 15); + + // TEST INVALID CASES + + // ( + REQUIRE( + find_closing_delimiter(bracket_sigs[3], 0, '(', ')') == + std::string::npos); + // (Integer> + REQUIRE( + find_closing_delimiter(bracket_sigs[4], 0, '(', ')') == + std::string::npos); + } + } } SCENARIO("gather_full_class_name")