diff --git a/regression/cbmc-java/generics_symtab1/generics$bound_element.class b/regression/cbmc-java/generics_symtab1/generics$bound_element.class new file mode 100644 index 00000000000..8076c290e12 Binary files /dev/null and b/regression/cbmc-java/generics_symtab1/generics$bound_element.class differ diff --git a/regression/cbmc-java/generics_symtab1/generics$element.class b/regression/cbmc-java/generics_symtab1/generics$element.class new file mode 100644 index 00000000000..34cb36e36b4 Binary files /dev/null and b/regression/cbmc-java/generics_symtab1/generics$element.class differ diff --git a/regression/cbmc-java/generics_symtab1/generics.class b/regression/cbmc-java/generics_symtab1/generics.class new file mode 100644 index 00000000000..4fde1b4d6c4 Binary files /dev/null and b/regression/cbmc-java/generics_symtab1/generics.class differ diff --git a/regression/cbmc-java/generics_symtab1/generics.java b/regression/cbmc-java/generics_symtab1/generics.java new file mode 100644 index 00000000000..0a0af41c63a --- /dev/null +++ b/regression/cbmc-java/generics_symtab1/generics.java @@ -0,0 +1,31 @@ +public class generics { + + class element { + E elem; + } + + class bound_element { + NUM elem; + NUM f() { + return elem; + } + void g(NUM e) { + elem=e; + } + } + + bound_element belem; + + Integer f(int n) { + + element e=new element<>(); + e.elem=n; + bound_element be=new bound_element<>(); + belem=new bound_element<>(); + be.elem=new Integer(n+1); + if(n>0) + return e.elem; + else + return be.elem; + } +} diff --git a/regression/cbmc-java/generics_symtab1/test.desc b/regression/cbmc-java/generics_symtab1/test.desc new file mode 100644 index 00000000000..fe6b3693bf3 --- /dev/null +++ b/regression/cbmc-java/generics_symtab1/test.desc @@ -0,0 +1,8 @@ +CORE +generics.class +--show-symbol-table +^EXIT=0$ +^SIGNAL=0$ +^Type........: struct generics\$bound_element\ \{ +__CPROVER_string \@class_identifier; boolean \@lock; struct java.lang.Object \@java.lang.Object; struct java.lang.Integer \*elem; struct generics \*this\$0; \}$ +-- diff --git a/src/java_bytecode/Makefile b/src/java_bytecode/Makefile index 06a7d55757e..61e0e474b42 100644 --- a/src/java_bytecode/Makefile +++ b/src/java_bytecode/Makefile @@ -25,6 +25,7 @@ SRC = bytecode_info.cpp \ java_string_library_preprocess.cpp \ java_types.cpp \ java_utils.cpp \ + generate_java_generic_type.cpp \ mz_zip_archive.cpp \ select_pointer_type.cpp \ # Empty last line diff --git a/src/java_bytecode/generate_java_generic_type.cpp b/src/java_bytecode/generate_java_generic_type.cpp new file mode 100644 index 00000000000..6c6cbc4e1c8 --- /dev/null +++ b/src/java_bytecode/generate_java_generic_type.cpp @@ -0,0 +1,171 @@ +/*******************************************************************\ + + Module: Generate Java Generic Type - Instantiate a generic class with + concrete type information. + + Author: DiffBlue Limited. All rights reserved. + +\*******************************************************************/ +#include "generate_java_generic_type.h" +#include +#include +#include + + +generate_java_generic_typet::generate_java_generic_typet( + message_handlert &message_handler): + message_handler(message_handler) +{} + +/// Generate a concrete instantiation of a generic type. +/// \param existing_generic_type The type to be concretised +/// \param symbol_table The symbol table that the concrete type will be +/// inserted into. +/// \return The symbol as it was retrieved from the symbol table after +/// it has been inserted into. +symbolt generate_java_generic_typet::operator()( + const java_generic_typet &existing_generic_type, + symbol_tablet &symbol_table) const +{ + namespacet ns(symbol_table); + + const typet &pointer_subtype=ns.follow(existing_generic_type.subtype()); + + INVARIANT( + pointer_subtype.id()==ID_struct, "Only pointers to classes in java"); + + const java_class_typet &replacement_type= + to_java_class_type(pointer_subtype); + const irep_idt new_tag=build_generic_tag( + existing_generic_type, replacement_type); + struct_union_typet::componentst replacement_components= + replacement_type.components(); + + // Small auxiliary function, to perform the inplace + // modification of the generic fields. + auto replace_type_for_generic_field= + [&](struct_union_typet::componentt &component) + { + if(is_java_generic_parameter(component.type())) + { + auto replacement_type_param= + to_java_generics_class_type(replacement_type); + + auto component_identifier= + to_java_generic_parameter(component.type()).type_variable() + .get_identifier(); + + optionalt results=java_generics_get_index_for_subtype( + replacement_type_param, component_identifier); + + INVARIANT( + results.has_value(), + "generic component type not found"); + + if(results) + { + component.type()= + existing_generic_type.generic_type_variables()[*results]; + } + } + return component; + }; + + std::size_t pre_modification_size=to_java_class_type( + ns.follow(existing_generic_type.subtype())).components().size(); + + std::for_each( + replacement_components.begin(), + replacement_components.end(), + replace_type_for_generic_field); + + std::size_t after_modification_size= + replacement_type.components().size(); + + INVARIANT( + pre_modification_size==after_modification_size, + "All components in the original class should be in the new class"); + + const auto expected_symbol="java::"+id2string(new_tag); + + generate_class_stub( + new_tag, + symbol_table, + message_handler, + replacement_components); + auto symbol=symbol_table.lookup(expected_symbol); + INVARIANT(symbol, "New class not created"); + return *symbol; +} + +/// Build a unique tag for the generic to be instantiated. +/// \param existing_generic_type The type we want to concretise +/// \param original_class +/// \return A tag for the new generic we want a unique tag for. +irep_idt generate_java_generic_typet::build_generic_tag( + const java_generic_typet &existing_generic_type, + const java_class_typet &original_class) const +{ + std::ostringstream new_tag_buffer; + new_tag_buffer << original_class.get_tag(); + new_tag_buffer << "<"; + bool first=true; + for(const typet ¶m : existing_generic_type.generic_type_variables()) + { + if(!first) + new_tag_buffer << ","; + first=false; + + INVARIANT( + is_java_generic_inst_parameter(param), + "Only create full concretized generic types"); + new_tag_buffer << param.subtype().get(ID_identifier); + } + + new_tag_buffer << ">"; + + return new_tag_buffer.str(); +} + + +/// Activate the generic instantiation code. +/// \param message_handler +/// \param symbol_table The symbol table so far. +void +instantiate_generics( + message_handlert &message_handler, + symbol_tablet &symbol_table) +{ + generate_java_generic_typet instantiate_generic_type(message_handler); + // check out the symbols in the symbol table at this point to see if we + // have a a generic type in. + for(const auto &symbol : symbol_table.symbols) + { + if(symbol.second.type.id()==ID_struct) + { + auto symbol_struct=to_struct_type(symbol.second.type); + auto &components=symbol_struct.components(); + + for(const auto &component : components) + { + if(is_java_generic_type(component.type())) + { + const auto &type_vars=to_java_generic_type(component.type()). + generic_type_variables(); + + // Before we can instantiate a generic component, we need + // its type variables to be instantiated parameters + if(all_of(type_vars.cbegin(), type_vars.cend(), + [](const typet &type) + { + return is_java_generic_inst_parameter(type); + })) + { + instantiate_generic_type( + to_java_generic_type(component.type()), symbol_table); + } + } + } + } + } +} diff --git a/src/java_bytecode/generate_java_generic_type.h b/src/java_bytecode/generate_java_generic_type.h new file mode 100644 index 00000000000..6ba48378a31 --- /dev/null +++ b/src/java_bytecode/generate_java_generic_type.h @@ -0,0 +1,38 @@ +/*******************************************************************\ + + Module: Generate Java Generic Type - Instantiate a generic class with + concrete type information. + + Author: DiffBlue Limited. All rights reserved. + +\*******************************************************************/ +#ifndef CPROVER_JAVA_BYTECODE_GENERATE_JAVA_GENERIC_TYPE_H +#define CPROVER_JAVA_BYTECODE_GENERATE_JAVA_GENERIC_TYPE_H + +#include +#include +#include +#include + +class generate_java_generic_typet +{ +public: + generate_java_generic_typet( + message_handlert &message_handler); + + symbolt operator()( + const java_generic_typet &existing_generic_type, + symbol_tablet &symbol_table) const; +private: + irep_idt build_generic_tag( + const java_generic_typet &existing_generic_type, + const java_class_typet &original_class) const; + + message_handlert &message_handler; +}; + +void instantiate_generics( + message_handlert &message_handler, + symbol_tablet &symbol_table); + +#endif // CPROVER_JAVA_BYTECODE_GENERATE_JAVA_GENERIC_TYPE_H diff --git a/src/java_bytecode/java_bytecode_convert_class.cpp b/src/java_bytecode/java_bytecode_convert_class.cpp index 8f038c92440..9270e1fa0cd 100644 --- a/src/java_bytecode/java_bytecode_convert_class.cpp +++ b/src/java_bytecode/java_bytecode_convert_class.cpp @@ -63,7 +63,8 @@ class java_bytecode_convert_classt:public messaget generate_class_stub( parse_tree.parsed_class.name, symbol_table, - get_message_handler()); + get_message_handler(), + struct_union_typet::componentst{}); } typedef java_bytecode_parse_treet::classt classt; diff --git a/src/java_bytecode/java_bytecode_instrument.cpp b/src/java_bytecode/java_bytecode_instrument.cpp index 31c18fab721..66f62e6ff78 100644 --- a/src/java_bytecode/java_bytecode_instrument.cpp +++ b/src/java_bytecode/java_bytecode_instrument.cpp @@ -100,7 +100,8 @@ codet java_bytecode_instrumentt::throw_exception( generate_class_stub( exc_name, symbol_table, - get_message_handler()); + get_message_handler(), + struct_union_typet::componentst{}); } pointer_typet exc_ptr_type= diff --git a/src/java_bytecode/java_bytecode_language.cpp b/src/java_bytecode/java_bytecode_language.cpp index cd6210bdb8f..a4b15d8b963 100644 --- a/src/java_bytecode/java_bytecode_language.cpp +++ b/src/java_bytecode/java_bytecode_language.cpp @@ -30,6 +30,7 @@ Author: Daniel Kroening, kroening@kroening.com #include "java_class_loader.h" #include "java_utils.h" #include +#include #include "expr2java.h" @@ -238,11 +239,21 @@ bool java_bytecode_languaget::typecheck( get_message_handler()); // now typecheck all - if(java_bytecode_typecheck( - symbol_table, get_message_handler(), string_refinement_enabled)) - return true; + bool res=java_bytecode_typecheck( + symbol_table, get_message_handler(), string_refinement_enabled); + // NOTE (FOTIS): There is some unintuitive logic here, where + // java_bytecode_check will return TRUE if typechecking failed, and FALSE + // if everything went well... + if(res) + { + // there is no point in continuing to concretise + // the generic types if typechecking failed. + return res; + } - return false; + instantiate_generics(get_message_handler(), symbol_table); + + return res; } bool java_bytecode_languaget::generate_support_functions( diff --git a/src/java_bytecode/java_types.h b/src/java_bytecode/java_types.h index e4c370157e0..b2ba6b7777d 100644 --- a/src/java_bytecode/java_types.h +++ b/src/java_bytecode/java_types.h @@ -11,6 +11,8 @@ Author: Daniel Kroening, kroening@kroening.com #define CPROVER_JAVA_BYTECODE_JAVA_TYPES_H #include +#include + #include #include #include @@ -379,4 +381,30 @@ inline typet java_type_from_string_with_exception( } } +/// Get the index in the subtypes array for a given component. +/// \param t The type we search for the subtypes in. +/// \param identifier The string identifier of the type of the component. +/// \return Optional with the size if the identifier was found. +inline const optionalt java_generics_get_index_for_subtype( + const java_generics_class_typet &t, + const irep_idt &identifier) +{ + const std::vector &gen_types=t.generic_types(); + + const auto iter = std::find_if( + gen_types.cbegin(), + gen_types.cend(), + [&identifier](const java_generic_parametert &ref) + { + return ref.type_variable().get_identifier()==identifier; + }); + + if(iter==gen_types.cend()) + { + return {}; + } + + return std::distance(gen_types.cbegin(), iter); +} + #endif // CPROVER_JAVA_BYTECODE_JAVA_TYPES_H diff --git a/src/java_bytecode/java_utils.cpp b/src/java_bytecode/java_utils.cpp index 6521f9903d6..8f366c324e3 100644 --- a/src/java_bytecode/java_utils.cpp +++ b/src/java_bytecode/java_utils.cpp @@ -67,7 +67,8 @@ const std::string java_class_to_package(const std::string &canonical_classname) void generate_class_stub( const irep_idt &class_name, symbol_tablet &symbol_table, - message_handlert &message_handler) + message_handlert &message_handler, + const struct_union_typet::componentst &componentst) { class_typet class_type; @@ -100,6 +101,7 @@ void generate_class_stub( { // create the class identifier etc java_root_class(res.first); + java_add_components_to_class(res.first, componentst); } } @@ -230,3 +232,22 @@ size_t find_closing_delimiter( // did not find corresponding closing '>' return std::string::npos; } + +/// Add the components in components_to_add to the class denoted by +/// class symbol. +/// \param class_symbol The symbol representing the class we want to modify. +/// \param components_to_add The vector with the components we want to add. +void java_add_components_to_class( + symbolt &class_symbol, + const struct_union_typet::componentst &components_to_add) +{ + PRECONDITION(class_symbol.is_type); + PRECONDITION(class_symbol.type.id()==ID_struct); + struct_typet &struct_type=to_struct_type(class_symbol.type); + struct_typet::componentst &components=struct_type.components(); + + for(const struct_union_typet::componentt &component : components_to_add) + { + components.push_back(component); + } +} diff --git a/src/java_bytecode/java_utils.h b/src/java_bytecode/java_utils.h index 9428494db93..774bb350572 100644 --- a/src/java_bytecode/java_utils.h +++ b/src/java_bytecode/java_utils.h @@ -18,7 +18,8 @@ bool java_is_array_type(const typet &type); void generate_class_stub( const irep_idt &class_name, symbol_tablet &symbol_table, - message_handlert &message_handler); + message_handlert &message_handler, + const struct_union_typet::componentst &componentst); /// Returns the number of JVM local variables (slots) taken by a local variable /// that, when translated to goto, has type \p t. @@ -66,6 +67,14 @@ irep_idt resolve_friendly_method_name( /// \param type: expected result type (typically expr.type().subtype()) dereference_exprt checked_dereference(const exprt &expr, const typet &type); +/// Add the components in components_to_add to the class denoted +/// by class symbol. +/// \param class_symbol The symbol representing the class we want to modify. +/// \param components_to_add The vector with the components we want to add. +void java_add_components_to_class( + symbolt &class_symbol, + const struct_union_typet::componentst &components_to_add); + size_t find_closing_delimiter( const std::string &src, size_t position, diff --git a/unit/java_bytecode/generate_concrete_generic_type/generate_java_generic_type.cpp b/unit/java_bytecode/generate_concrete_generic_type/generate_java_generic_type.cpp new file mode 100644 index 00000000000..7ea813cc4d9 --- /dev/null +++ b/unit/java_bytecode/generate_concrete_generic_type/generate_java_generic_type.cpp @@ -0,0 +1,162 @@ +/*******************************************************************\ + + Module: Unit tests for generating new type with generic parameters + substitued for concrete types + + Author: DiffBlue Limited. All rights reserved. + +\*******************************************************************/ + +#include +#include + +#include + +#include + + +SCENARIO( + "generate_java_generic_type_from_file", + "[core][java_bytecode][generate_java_generic_type]") +{ + auto expected_symbol= + "java::generic_two_fields$bound_element"; + + GIVEN("A generic java type with two generic fields and a concrete " + "substitution") + { + symbol_tablet new_symbol_table= + load_java_class("generic_two_fields", + "./java_bytecode/generate_concrete_generic_type"); + + REQUIRE(new_symbol_table.has_symbol(expected_symbol)); + THEN("The class should contain two instantiated fields.") + { + const auto &class_symbol=new_symbol_table.lookup( + "java::generic_two_fields$bound_element"); + const typet &symbol_type=class_symbol->type; + + REQUIRE(symbol_type.id()==ID_struct); + class_typet class_type=to_class_type(symbol_type); + REQUIRE(class_type.is_class()); + + REQUIRE(class_type.has_component("first")); + const auto &first_component=class_type.get_component("first"); + REQUIRE(is_java_generic_inst_parameter(first_component.type())); + REQUIRE(first_component.type().id()==ID_pointer); + REQUIRE(first_component.type().subtype().id()==ID_symbol); + REQUIRE(to_symbol_type( + first_component.type().subtype()).get_identifier()== + "java::java.lang.Integer"); + REQUIRE(class_type.has_component("second")); + const auto &second_component=class_type.get_component("second"); + REQUIRE(is_java_generic_inst_parameter(second_component.type())); + REQUIRE(second_component.type().id()==ID_pointer); + REQUIRE(second_component.type().subtype().id()==ID_symbol); + REQUIRE(to_symbol_type( + second_component.type().subtype()).get_identifier()== + "java::java.lang.Integer"); + } + } +} + + +SCENARIO( + "generate_java_generic_type_from_file_two_params", + "[core][java_bytecode][generate_java_generic_type]") +{ + auto expected_symbol= + "java::generic_two_parameters$KeyValuePair" + ""; + + GIVEN("A generic java type with two generic parameters, like a Hashtable") + { + symbol_tablet new_symbol_table= + load_java_class("generic_two_parameters", + "./java_bytecode/generate_concrete_generic_type"); + + REQUIRE(new_symbol_table.has_symbol( + "java::generic_two_parameters$KeyValuePair")); + THEN("The class should have two subtypes in the vector of the types of " + "the generic components.") + { + const auto &class_symbol=new_symbol_table.lookup( + expected_symbol); + const typet &symbol_type=class_symbol->type; + + REQUIRE(symbol_type.id()==ID_struct); + class_typet class_type=to_class_type(symbol_type); + REQUIRE(class_type.is_class()); + + REQUIRE(class_type.has_component("key")); + const auto &first_component=class_type.get_component("key"); + REQUIRE(is_java_generic_inst_parameter(first_component.type())); + REQUIRE(class_type.has_component("value")); + const auto &second_component=class_type.get_component("value"); + REQUIRE(is_java_generic_inst_parameter(second_component.type())); + } + } +} + + +SCENARIO( + "generate_java_generic_type_from_file_uninstantiated_param", + "[core][java_bytecode][generate_java_generic_type]") +{ + GIVEN("A generic java type with a field that refers to another generic with" + " an uninstantiated parameter.") + { + symbol_tablet new_symbol_table= + load_java_class("generic_unknown_field", + "./java_bytecode/generate_concrete_generic_type"); + + // It's illegal to create an instantiation of a field that refers + // to a (still) uninstantiated generic class, so this is checking that + // this hasn't happened. + REQUIRE_FALSE(new_symbol_table.has_symbol + ("java::generic_unknown_field$element")); + } +} + + +SCENARIO( + "generate_java_generic_type_from_file_two_instances", + "[core][java_bytecode][generate_java_generic_type]") +{ + // After we have loaded the class and converted the generics, + // the presence of these two symbols in the symbol table is + // proof enough that we did the work we needed to do correctly. + auto first_expected_symbol= + "java::generic_two_instances$element"; + auto second_expected_symbol= + "java::generic_two_instances$element"; + + GIVEN("A generic java type with a field that refers to another generic with" + " an uninstantiated parameter.") + { + symbol_tablet new_symbol_table= + load_java_class("generic_two_instances", + "./java_bytecode/generate_concrete_generic_type"); + + REQUIRE(new_symbol_table.has_symbol(first_expected_symbol)); + auto first_symbol=new_symbol_table.lookup(first_expected_symbol); + REQUIRE(first_symbol->type.id()==ID_struct); + auto first_symbol_type= + to_struct_type(first_symbol->type).components()[3].type(); + REQUIRE(first_symbol_type.id()==ID_pointer); + REQUIRE(first_symbol_type.subtype().id()==ID_symbol); + REQUIRE(to_symbol_type(first_symbol_type.subtype()).get_identifier()== + "java::java.lang.Boolean"); + + REQUIRE(new_symbol_table.has_symbol(second_expected_symbol)); + auto second_symbol=new_symbol_table.lookup(second_expected_symbol); + REQUIRE(second_symbol->type.id()==ID_struct); + auto second_symbol_type= + to_struct_type(second_symbol->type).components()[3].type(); + REQUIRE(second_symbol_type.id()==ID_pointer); + REQUIRE(second_symbol_type.subtype().id()==ID_symbol); + REQUIRE(to_symbol_type(second_symbol_type.subtype()).get_identifier()== + "java::java.lang.Integer"); + } +} diff --git a/unit/java_bytecode/generate_concrete_generic_type/generic_two_fields$bound_element.class b/unit/java_bytecode/generate_concrete_generic_type/generic_two_fields$bound_element.class new file mode 100644 index 00000000000..0e61e55d0de Binary files /dev/null and b/unit/java_bytecode/generate_concrete_generic_type/generic_two_fields$bound_element.class differ diff --git a/unit/java_bytecode/generate_concrete_generic_type/generic_two_fields.class b/unit/java_bytecode/generate_concrete_generic_type/generic_two_fields.class new file mode 100644 index 00000000000..5d14a465aaf Binary files /dev/null and b/unit/java_bytecode/generate_concrete_generic_type/generic_two_fields.class differ diff --git a/unit/java_bytecode/generate_concrete_generic_type/generic_two_fields.java b/unit/java_bytecode/generate_concrete_generic_type/generic_two_fields.java new file mode 100644 index 00000000000..e8e6dc80d9e --- /dev/null +++ b/unit/java_bytecode/generate_concrete_generic_type/generic_two_fields.java @@ -0,0 +1,11 @@ +public class generic_two_fields { + + // For this to work we need to compile with -g, otherwise the symbols we are looking for + // are entering the symbol table as anonlocal::1 and anonlocal::2 and nothing works. + class bound_element { + NUM first; + NUM second; + } + + bound_element belem; +} diff --git a/unit/java_bytecode/generate_concrete_generic_type/generic_two_instances$element.class b/unit/java_bytecode/generate_concrete_generic_type/generic_two_instances$element.class new file mode 100644 index 00000000000..3229b26a832 Binary files /dev/null and b/unit/java_bytecode/generate_concrete_generic_type/generic_two_instances$element.class differ diff --git a/unit/java_bytecode/generate_concrete_generic_type/generic_two_instances.class b/unit/java_bytecode/generate_concrete_generic_type/generic_two_instances.class new file mode 100644 index 00000000000..61d336211cd Binary files /dev/null and b/unit/java_bytecode/generate_concrete_generic_type/generic_two_instances.class differ diff --git a/unit/java_bytecode/generate_concrete_generic_type/generic_two_instances.java b/unit/java_bytecode/generate_concrete_generic_type/generic_two_instances.java new file mode 100644 index 00000000000..48144b5ecaf --- /dev/null +++ b/unit/java_bytecode/generate_concrete_generic_type/generic_two_instances.java @@ -0,0 +1,8 @@ +class generic_two_instances { + class element { + T elem; + } + + element bool_element; + element int_element; +} diff --git a/unit/java_bytecode/generate_concrete_generic_type/generic_two_parameters$KeyValuePair.class b/unit/java_bytecode/generate_concrete_generic_type/generic_two_parameters$KeyValuePair.class new file mode 100644 index 00000000000..8c4e5674b68 Binary files /dev/null and b/unit/java_bytecode/generate_concrete_generic_type/generic_two_parameters$KeyValuePair.class differ diff --git a/unit/java_bytecode/generate_concrete_generic_type/generic_two_parameters.class b/unit/java_bytecode/generate_concrete_generic_type/generic_two_parameters.class new file mode 100644 index 00000000000..2527db38675 Binary files /dev/null and b/unit/java_bytecode/generate_concrete_generic_type/generic_two_parameters.class differ diff --git a/unit/java_bytecode/generate_concrete_generic_type/generic_two_parameters.java b/unit/java_bytecode/generate_concrete_generic_type/generic_two_parameters.java new file mode 100644 index 00000000000..0f5bed55482 --- /dev/null +++ b/unit/java_bytecode/generate_concrete_generic_type/generic_two_parameters.java @@ -0,0 +1,22 @@ +class generic_two_parameters { + class KeyValuePair { + K key; + V value; + } + + KeyValuePair bomb; + + public String func() { + KeyValuePair e = new KeyValuePair(); + e.key = "Hello"; + e.value = 5; + if (e.value >= 4) + { + return e.key; + } + else + { + return "Oops"; + } + } +} diff --git a/unit/java_bytecode/generate_concrete_generic_type/generic_unknown_field$G.class b/unit/java_bytecode/generate_concrete_generic_type/generic_unknown_field$G.class new file mode 100644 index 00000000000..39888ab1dbc Binary files /dev/null and b/unit/java_bytecode/generate_concrete_generic_type/generic_unknown_field$G.class differ diff --git a/unit/java_bytecode/generate_concrete_generic_type/generic_unknown_field$element.class b/unit/java_bytecode/generate_concrete_generic_type/generic_unknown_field$element.class new file mode 100644 index 00000000000..39b647093cb Binary files /dev/null and b/unit/java_bytecode/generate_concrete_generic_type/generic_unknown_field$element.class differ diff --git a/unit/java_bytecode/generate_concrete_generic_type/generic_unknown_field.class b/unit/java_bytecode/generate_concrete_generic_type/generic_unknown_field.class new file mode 100644 index 00000000000..73db45376b9 Binary files /dev/null and b/unit/java_bytecode/generate_concrete_generic_type/generic_unknown_field.class differ diff --git a/unit/java_bytecode/generate_concrete_generic_type/generic_unknown_field.java b/unit/java_bytecode/generate_concrete_generic_type/generic_unknown_field.java new file mode 100644 index 00000000000..3b918d9664f --- /dev/null +++ b/unit/java_bytecode/generate_concrete_generic_type/generic_unknown_field.java @@ -0,0 +1,9 @@ +class generic_unknown_field { + class G { + element ref; + } + + class element { + T elem; + } +} diff --git a/unit/java_bytecode/java_bytecode_parse_generics/generics05$igeneric05.class b/unit/java_bytecode/java_bytecode_parse_generics/generics05$igeneric05.class new file mode 100644 index 00000000000..179141a38e2 Binary files /dev/null and b/unit/java_bytecode/java_bytecode_parse_generics/generics05$igeneric05.class differ