| // Copyright 2015 The Bazel Authors. All rights reserved. | 
 | // | 
 | // Licensed under the Apache License, Version 2.0 (the "License"); | 
 | // you may not use this file except in compliance with the License. | 
 | // You may obtain a copy of the License at | 
 | // | 
 | //    http://www.apache.org/licenses/LICENSE-2.0 | 
 | // | 
 | // Unless required by applicable law or agreed to in writing, software | 
 | // distributed under the License is distributed on an "AS IS" BASIS, | 
 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | // See the License for the specific language governing permissions and | 
 | // limitations under the License. | 
 | // | 
 | // classfile.cc -- classfile parsing and stripping. | 
 | // | 
 |  | 
 | // TODO(adonovan) don't pass pointers by reference; this is not | 
 | // compatible with Google C++ style. | 
 |  | 
 | // See README.txt for details. | 
 | // | 
 | // For definition of JVM class file format, see: | 
 | // Java SE 8 Edition: | 
 | // http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4 | 
 |  | 
 | #define __STDC_FORMAT_MACROS 1 | 
 | #define __STDC_LIMIT_MACROS 1 | 
 | #include <inttypes.h> // for PRIx32 | 
 | #include <stddef.h> | 
 | #include <stdio.h> | 
 | #include <stdlib.h> | 
 | #include <string.h> | 
 |  | 
 | #include <set> | 
 | #include <sstream> | 
 | #include <string> | 
 | #include <vector> | 
 |  | 
 | #include "third_party/ijar/common.h" | 
 |  | 
 | namespace { | 
 | // Converts a value to string. | 
 | // Workaround for mingw where std::to_string is not implemented. | 
 | // See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52015. | 
 | template <typename T> | 
 | std::string ToString(const T& value) { | 
 |   std::ostringstream oss; | 
 |   oss << value; | 
 |   return oss.str(); | 
 | } | 
 | }  // namespace | 
 |  | 
 | namespace devtools_ijar { | 
 |  | 
 | // See Table 4.3 in JVM Spec. | 
 | enum CONSTANT { | 
 |   CONSTANT_Class              = 7, | 
 |   CONSTANT_FieldRef           = 9, | 
 |   CONSTANT_Methodref          = 10, | 
 |   CONSTANT_Interfacemethodref = 11, | 
 |   CONSTANT_String             = 8, | 
 |   CONSTANT_Integer            = 3, | 
 |   CONSTANT_Float              = 4, | 
 |   CONSTANT_Long               = 5, | 
 |   CONSTANT_Double             = 6, | 
 |   CONSTANT_NameAndType        = 12, | 
 |   CONSTANT_Utf8               = 1, | 
 |   CONSTANT_MethodHandle       = 15, | 
 |   CONSTANT_MethodType         = 16, | 
 |   CONSTANT_InvokeDynamic      = 18 | 
 | }; | 
 |  | 
 | // See Tables 4.1, 4.4, 4.5 in JVM Spec. | 
 | enum ACCESS { | 
 |   ACC_PUBLIC = 0x0001, | 
 |   ACC_PRIVATE = 0x0002, | 
 |   ACC_PROTECTED = 0x0004, | 
 |   ACC_STATIC = 0x0008, | 
 |   ACC_FINAL = 0x0010, | 
 |   ACC_SYNCHRONIZED = 0x0020, | 
 |   ACC_BRIDGE = 0x0040, | 
 |   ACC_VOLATILE = 0x0040, | 
 |   ACC_TRANSIENT = 0x0080, | 
 |   ACC_INTERFACE = 0x0200, | 
 |   ACC_ABSTRACT = 0x0400, | 
 |   ACC_SYNTHETIC = 0x1000 | 
 | }; | 
 |  | 
 | // See Table 4.7.20-A in Java 8 JVM Spec. | 
 | enum TARGET_TYPE { | 
 |   // Targets for type parameter declarations (ElementType.TYPE_PARAMETER): | 
 |   CLASS_TYPE_PARAMETER        = 0x00, | 
 |   METHOD_TYPE_PARAMETER       = 0x01, | 
 |  | 
 |   // Targets for type uses that may be externally visible in classes and members | 
 |   // (ElementType.TYPE_USE): | 
 |   CLASS_EXTENDS               = 0x10, | 
 |   CLASS_TYPE_PARAMETER_BOUND  = 0x11, | 
 |   METHOD_TYPE_PARAMETER_BOUND = 0x12, | 
 |   FIELD                       = 0x13, | 
 |   METHOD_RETURN               = 0x14, | 
 |   METHOD_RECEIVER             = 0x15, | 
 |   METHOD_FORMAL_PARAMETER     = 0x16, | 
 |   THROWS                      = 0x17, | 
 |  | 
 |   // TARGET_TYPE >= 0x40 is reserved for type uses that occur only within code | 
 |   // blocks. Ijar doesn't need to know about these. | 
 | }; | 
 |  | 
 | struct Constant; | 
 |  | 
 | // TODO(adonovan) these globals are unfortunate | 
 | static std::vector<Constant*>        const_pool_in; // input constant pool | 
 | static std::vector<Constant*>        const_pool_out; // output constant_pool | 
 | static std::set<std::string>         used_class_names; | 
 | static Constant *                    class_name; | 
 |  | 
 | // Returns the Constant object, given an index into the input constant pool. | 
 | // Note: constant(0) == NULL; this invariant is exploited by the | 
 | // InnerClassesAttribute, inter alia. | 
 | inline Constant *constant(int idx) { | 
 |   if (idx < 0 || (unsigned)idx >= const_pool_in.size()) { | 
 |     fprintf(stderr, "Illegal constant pool index: %d\n", idx); | 
 |     abort(); | 
 |   } | 
 |   return const_pool_in[idx]; | 
 | } | 
 |  | 
 | /********************************************************************** | 
 |  *                                                                    * | 
 |  *                             Constants                              * | 
 |  *                                                                    * | 
 |  **********************************************************************/ | 
 |  | 
 | // See sec.4.4 of JVM spec. | 
 | struct Constant { | 
 |  | 
 |   Constant(u1 tag) : | 
 |       slot_(0), | 
 |       tag_(tag) {} | 
 |  | 
 |   virtual ~Constant() {} | 
 |  | 
 |   // For UTF-8 string constants, returns the encoded string. | 
 |   // Otherwise, returns an undefined string value suitable for debugging. | 
 |   virtual std::string Display() = 0; | 
 |  | 
 |   virtual void Write(u1 *&p) = 0; | 
 |  | 
 |   // Called by slot() when a constant has been identified as required | 
 |   // in the output classfile's constant pool.  This is a hook allowing | 
 |   // constants to register their dependency on other constants, by | 
 |   // calling slot() on them in turn. | 
 |   virtual void Keep() {} | 
 |  | 
 |   bool Kept() { | 
 |     return slot_ != 0; | 
 |   } | 
 |  | 
 |   // Returns the index of this constant in the output class's constant | 
 |   // pool, assigning a slot if not already done. | 
 |   u2 slot() { | 
 |     if (slot_ == 0) { | 
 |       Keep(); | 
 |       slot_ = const_pool_out.size(); // BugBot's "narrowing" warning | 
 |                                      // is bogus.  The number of | 
 |                                      // output constants can't exceed | 
 |                                      // the number of input constants. | 
 |       if (slot_ == 0) { | 
 |         fprintf(stderr, "Constant::slot() called before output phase.\n"); | 
 |         abort(); | 
 |       } | 
 |       const_pool_out.push_back(this); | 
 |       if (tag_ == CONSTANT_Long || tag_ == CONSTANT_Double) { | 
 |         const_pool_out.push_back(NULL); | 
 |       } | 
 |     } | 
 |     return slot_; | 
 |   } | 
 |  | 
 |   u2 slot_; // zero => "this constant is unreachable garbage" | 
 |   u1 tag_; | 
 | }; | 
 |  | 
 | // Extracts class names from a signature and puts them into the global | 
 | // variable used_class_names. | 
 | // | 
 | // desc: the descriptor class names should be extracted from. | 
 | // p: the position where the extraction should tart. | 
 | void ExtractClassNames(const std::string& desc, size_t* p); | 
 |  | 
 | // See sec.4.4.1 of JVM spec. | 
 | struct Constant_Class : Constant | 
 | { | 
 |   Constant_Class(u2 name_index) : | 
 |       Constant(CONSTANT_Class), | 
 |       name_index_(name_index) {} | 
 |  | 
 |   void Write(u1 *&p) { | 
 |     put_u1(p, tag_); | 
 |     put_u2be(p, constant(name_index_)->slot()); | 
 |   } | 
 |  | 
 |   std::string Display() { | 
 |     return constant(name_index_)->Display(); | 
 |   } | 
 |  | 
 |   void Keep() { constant(name_index_)->slot(); } | 
 |  | 
 |   u2 name_index_; | 
 | }; | 
 |  | 
 | // See sec.4.4.2 of JVM spec. | 
 | struct Constant_FMIref : Constant | 
 | { | 
 |   Constant_FMIref(u1 tag, | 
 |                   u2 class_index, | 
 |                   u2 name_type_index) : | 
 |       Constant(tag), | 
 |       class_index_(class_index), | 
 |       name_type_index_(name_type_index) {} | 
 |  | 
 |   void Write(u1 *&p) { | 
 |     put_u1(p, tag_); | 
 |     put_u2be(p, constant(class_index_)->slot()); | 
 |     put_u2be(p, constant(name_type_index_)->slot()); | 
 |   } | 
 |  | 
 |   std::string Display() { | 
 |     return constant(class_index_)->Display() + "::" + | 
 |         constant(name_type_index_)->Display(); | 
 |   } | 
 |  | 
 |   void Keep() { | 
 |     constant(class_index_)->slot(); | 
 |     constant(name_type_index_)->slot(); | 
 |   } | 
 |  | 
 |   u2 class_index_; | 
 |   u2 name_type_index_; | 
 | }; | 
 |  | 
 | // See sec.4.4.3 of JVM spec. | 
 | struct Constant_String : Constant | 
 | { | 
 |   Constant_String(u2 string_index) : | 
 |       Constant(CONSTANT_String), | 
 |       string_index_(string_index) {} | 
 |  | 
 |   void Write(u1 *&p) { | 
 |     put_u1(p, tag_); | 
 |     put_u2be(p, constant(string_index_)->slot()); | 
 |   } | 
 |  | 
 |   std::string Display() { | 
 |     return "\"" + constant(string_index_)->Display() + "\""; | 
 |   } | 
 |  | 
 |   void Keep() { constant(string_index_)->slot(); } | 
 |  | 
 |   u2 string_index_; | 
 | }; | 
 |  | 
 | // See sec.4.4.4 of JVM spec. | 
 | struct Constant_IntegerOrFloat : Constant | 
 | { | 
 |   Constant_IntegerOrFloat(u1 tag, u4 bytes) : | 
 |       Constant(tag), | 
 |       bytes_(bytes) {} | 
 |  | 
 |   void Write(u1 *&p) { | 
 |     put_u1(p, tag_); | 
 |     put_u4be(p, bytes_); | 
 |   } | 
 |  | 
 |   std::string Display() { return "int/float"; } | 
 |  | 
 |   u4 bytes_; | 
 | }; | 
 |  | 
 | // See sec.4.4.5 of JVM spec. | 
 | struct Constant_LongOrDouble : Constant_IntegerOrFloat | 
 | { | 
 |   Constant_LongOrDouble(u1 tag, u4 high_bytes, u4 low_bytes) : | 
 |       Constant_IntegerOrFloat(tag, high_bytes), | 
 |       low_bytes_(low_bytes) {} | 
 |  | 
 |   void Write(u1 *&p) { | 
 |     put_u1(p, tag_); | 
 |     put_u4be(p, bytes_); | 
 |     put_u4be(p, low_bytes_); | 
 |   } | 
 |  | 
 |   std::string Display() { return "long/double"; } | 
 |  | 
 |   u4 low_bytes_; | 
 | }; | 
 |  | 
 | // See sec.4.4.6 of JVM spec. | 
 | struct Constant_NameAndType : Constant | 
 | { | 
 |   Constant_NameAndType(u2 name_index, u2 descr_index) : | 
 |       Constant(CONSTANT_NameAndType), | 
 |       name_index_(name_index), | 
 |       descr_index_(descr_index) {} | 
 |  | 
 |   void Write(u1 *&p) { | 
 |     put_u1(p, tag_); | 
 |     put_u2be(p, constant(name_index_)->slot()); | 
 |     put_u2be(p, constant(descr_index_)->slot()); | 
 |   } | 
 |  | 
 |   std::string Display() { | 
 |     return constant(name_index_)->Display() + "::" + | 
 |         constant(descr_index_)->Display(); | 
 |   } | 
 |  | 
 |   void Keep() { | 
 |     constant(name_index_)->slot(); | 
 |     constant(descr_index_)->slot(); | 
 |   } | 
 |  | 
 |   u2 name_index_; | 
 |   u2 descr_index_; | 
 | }; | 
 |  | 
 | // See sec.4.4.7 of JVM spec. | 
 | struct Constant_Utf8 : Constant | 
 | { | 
 |   Constant_Utf8(u4 length, const u1 *utf8) : | 
 |       Constant(CONSTANT_Utf8), | 
 |       length_(length), | 
 |       utf8_(utf8) {} | 
 |  | 
 |   void Write(u1 *&p) { | 
 |     put_u1(p, tag_); | 
 |     put_u2be(p, length_); | 
 |     put_n(p, utf8_, length_); | 
 |   } | 
 |  | 
 |   std::string Display() { | 
 |     return std::string((const char*) utf8_, length_); | 
 |   } | 
 |  | 
 |   u4 length_; | 
 |   const u1 *utf8_; | 
 | }; | 
 |  | 
 | // See sec.4.4.8 of JVM spec. | 
 | struct Constant_MethodHandle : Constant | 
 | { | 
 |   Constant_MethodHandle(u1 reference_kind, u2 reference_index) : | 
 |       Constant(CONSTANT_MethodHandle), | 
 |       reference_kind_(reference_kind), | 
 |       reference_index_(reference_index) {} | 
 |  | 
 |   void Write(u1 *&p) { | 
 |     put_u1(p, tag_); | 
 |     put_u1(p, reference_kind_); | 
 |     put_u2be(p, reference_index_); | 
 |   } | 
 |  | 
 |   std::string Display() { | 
 |     return "Constant_MethodHandle::" + ToString(reference_kind_) + "::" | 
 |         + constant(reference_index_)->Display(); | 
 |   } | 
 |  | 
 |   u1 reference_kind_; | 
 |   u2 reference_index_; | 
 | }; | 
 |  | 
 | // See sec.4.4.9 of JVM spec. | 
 | struct Constant_MethodType : Constant | 
 | { | 
 |   Constant_MethodType(u2 descriptor_index) : | 
 |       Constant(CONSTANT_MethodType), | 
 |       descriptor_index_(descriptor_index) {} | 
 |  | 
 |   void Write(u1 *&p) { | 
 |     put_u1(p, tag_); | 
 |     put_u2be(p, descriptor_index_); | 
 |   } | 
 |  | 
 |   std::string Display() { | 
 |     return  "Constant_MethodType::" + constant(descriptor_index_)->Display(); | 
 |   } | 
 |  | 
 |   u2 descriptor_index_; | 
 | }; | 
 |  | 
 | // See sec.4.4.10 of JVM spec. | 
 | struct Constant_InvokeDynamic : Constant | 
 | { | 
 |   Constant_InvokeDynamic(u2 bootstrap_method_attr_index, u2 name_and_type_index) : | 
 |       Constant(CONSTANT_InvokeDynamic), | 
 |       bootstrap_method_attr_index_(bootstrap_method_attr_index), | 
 |       name_and_type_index_(name_and_type_index) {} | 
 |  | 
 |   void Write(u1 *&p) { | 
 |     put_u1(p, tag_); | 
 |     put_u2be(p, bootstrap_method_attr_index_); | 
 |     put_u2be(p, name_and_type_index_); | 
 |   } | 
 |  | 
 |   std::string Display() { | 
 |     return  "Constant_InvokeDynamic::" | 
 |         + ToString(bootstrap_method_attr_index_) + "::" | 
 |         + constant(name_and_type_index_)->Display(); | 
 |   } | 
 |  | 
 |   u2 bootstrap_method_attr_index_; | 
 |   u2 name_and_type_index_; | 
 | }; | 
 |  | 
 | /********************************************************************** | 
 |  *                                                                    * | 
 |  *                             Attributes                             * | 
 |  *                                                                    * | 
 |  **********************************************************************/ | 
 |  | 
 | // See sec.4.7 of JVM spec. | 
 | struct Attribute { | 
 |  | 
 |   virtual ~Attribute() {} | 
 |   virtual void Write(u1 *&p) = 0; | 
 |   virtual void ExtractClassNames() {} | 
 |  | 
 |   void WriteProlog(u1 *&p, u2 length) { | 
 |     put_u2be(p, attribute_name_->slot()); | 
 |     put_u4be(p, length); | 
 |   } | 
 |  | 
 |   Constant *attribute_name_; | 
 | }; | 
 |  | 
 | // See sec.4.7.5 of JVM spec. | 
 | struct ExceptionsAttribute : Attribute { | 
 |  | 
 |   static ExceptionsAttribute* Read(const u1 *&p, Constant *attribute_name) { | 
 |     ExceptionsAttribute *attr = new ExceptionsAttribute; | 
 |     attr->attribute_name_ = attribute_name; | 
 |     u2 number_of_exceptions = get_u2be(p); | 
 |     for (int ii = 0; ii < number_of_exceptions; ++ii) { | 
 |       attr->exceptions_.push_back(constant(get_u2be(p))); | 
 |     } | 
 |     return attr; | 
 |   } | 
 |  | 
 |   void Write(u1 *&p) { | 
 |     WriteProlog(p, exceptions_.size() * 2 + 2); | 
 |     put_u2be(p, exceptions_.size()); | 
 |     for (size_t ii = 0; ii < exceptions_.size(); ++ii) { | 
 |       put_u2be(p, exceptions_[ii]->slot()); | 
 |     } | 
 |   } | 
 |  | 
 |   std::vector<Constant*> exceptions_; | 
 | }; | 
 |  | 
 | // See sec.4.7.6 of JVM spec. | 
 | struct InnerClassesAttribute : Attribute { | 
 |  | 
 |   struct Entry { | 
 |     Constant *inner_class_info; | 
 |     Constant *outer_class_info; | 
 |     Constant *inner_name; | 
 |     u2 inner_class_access_flags; | 
 |   }; | 
 |  | 
 |   virtual ~InnerClassesAttribute() { | 
 |     for (size_t i = 0; i < entries_.size(); i++) { | 
 |       delete entries_[i]; | 
 |     } | 
 |   } | 
 |  | 
 |   static InnerClassesAttribute* Read(const u1 *&p, Constant *attribute_name) { | 
 |     InnerClassesAttribute *attr = new InnerClassesAttribute; | 
 |     attr->attribute_name_ = attribute_name; | 
 |  | 
 |     u2 number_of_classes = get_u2be(p); | 
 |     for (int ii = 0; ii < number_of_classes; ++ii) { | 
 |       Entry *entry = new Entry; | 
 |       entry->inner_class_info = constant(get_u2be(p)); | 
 |       entry->outer_class_info = constant(get_u2be(p)); | 
 |       entry->inner_name = constant(get_u2be(p)); | 
 |       entry->inner_class_access_flags = get_u2be(p); | 
 |  | 
 |       attr->entries_.push_back(entry); | 
 |     } | 
 |     return attr; | 
 |   } | 
 |  | 
 |   void Write(u1 *&p) { | 
 |     std::set<int> kept_entries; | 
 |     // We keep an entry if the constant referring to the inner class is already | 
 |     // kept. Then we mark its outer class and its class name as kept, too, then | 
 |     // iterate until a fixed point is reached. | 
 |     int entry_count; | 
 |     int iteration = 0; | 
 |  | 
 |     do { | 
 |       entry_count = kept_entries.size(); | 
 |       for (int i_entry = 0; i_entry < static_cast<int>(entries_.size()); | 
 |            ++i_entry) { | 
 |         Entry* entry = entries_[i_entry]; | 
 |         if (entry->inner_class_info->Kept() || | 
 |             used_class_names.find(entry->inner_class_info->Display()) != | 
 |                 used_class_names.end() || | 
 |             entry->outer_class_info == class_name) { | 
 |           if (entry->inner_name == NULL) { | 
 |             // JVMS 4.7.6: inner_name_index is zero iff the class is anonymous | 
 |             continue; | 
 |           } | 
 |  | 
 |           kept_entries.insert(i_entry); | 
 |  | 
 |           // JVMS 4.7.6: outer_class_info_index is zero for top-level classes | 
 |           if (entry->outer_class_info != NULL) { | 
 |             entry->outer_class_info->slot(); | 
 |           } | 
 |  | 
 |           entry->inner_name->slot(); | 
 |         } | 
 |       } | 
 |       iteration += 1; | 
 |     } while (entry_count != static_cast<int>(kept_entries.size())); | 
 |  | 
 |     if (kept_entries.size() == 0) { | 
 |       return; | 
 |     } | 
 |  | 
 |     WriteProlog(p, 2 + kept_entries.size() * 8); | 
 |     put_u2be(p, kept_entries.size()); | 
 |  | 
 |     for (std::set<int>::iterator it = kept_entries.begin(); | 
 |          it != kept_entries.end(); | 
 |          ++it) { | 
 |       Entry *entry = entries_[*it]; | 
 |       put_u2be(p, entry->inner_class_info == NULL | 
 |                ? 0 | 
 |                : entry->inner_class_info->slot()); | 
 |       put_u2be(p, entry->outer_class_info == NULL | 
 |                ? 0 | 
 |                : entry->outer_class_info->slot()); | 
 |       put_u2be(p, entry->inner_name == NULL | 
 |                ? 0 | 
 |                : entry->inner_name->slot()); | 
 |       put_u2be(p, entry->inner_class_access_flags); | 
 |     } | 
 |   } | 
 |  | 
 |   std::vector<Entry*> entries_; | 
 | }; | 
 |  | 
 | // See sec.4.7.7 of JVM spec. | 
 | // We preserve EnclosingMethod attributes to be able to identify local and | 
 | // anonymous classes. These classes will be stripped of most content, as they | 
 | // represent implementation details that shoudn't leak into the ijars. Omitting | 
 | // EnclosingMethod attributes can lead to type-checking failures in the presence | 
 | // of generics (see b/9070939). | 
 | struct EnclosingMethodAttribute : Attribute { | 
 |  | 
 |   static EnclosingMethodAttribute* Read(const u1 *&p, | 
 |                                         Constant *attribute_name) { | 
 |     EnclosingMethodAttribute *attr = new EnclosingMethodAttribute; | 
 |     attr->attribute_name_ = attribute_name; | 
 |     attr->class_ = constant(get_u2be(p)); | 
 |     attr->method_ = constant(get_u2be(p)); | 
 |     return attr; | 
 |   } | 
 |  | 
 |   void Write(u1 *&p) { | 
 |     WriteProlog(p, 4); | 
 |     put_u2be(p, class_->slot()); | 
 |     put_u2be(p, method_ == NULL ? 0 : method_->slot()); | 
 |   } | 
 |  | 
 |   Constant *class_; | 
 |   Constant *method_; | 
 | }; | 
 |  | 
 | // See sec.4.7.16.1 of JVM spec. | 
 | // Used by AnnotationDefault and other attributes. | 
 | struct ElementValue { | 
 |   virtual ~ElementValue() {} | 
 |   virtual void Write(u1 *&p) = 0; | 
 |   virtual void ExtractClassNames() {} | 
 |   static ElementValue* Read(const u1 *&p); | 
 |   u1 tag_; | 
 |   u4 length_; | 
 | }; | 
 |  | 
 | struct BaseTypeElementValue : ElementValue { | 
 |   void Write(u1 *&p) { | 
 |     put_u1(p, tag_); | 
 |     put_u2be(p, const_value_->slot()); | 
 |   } | 
 |   static BaseTypeElementValue *Read(const u1 *&p) { | 
 |     BaseTypeElementValue *value = new BaseTypeElementValue; | 
 |     value->const_value_ = constant(get_u2be(p)); | 
 |     return value; | 
 |   } | 
 |   Constant *const_value_; | 
 | }; | 
 |  | 
 | struct EnumTypeElementValue : ElementValue { | 
 |   void Write(u1 *&p) { | 
 |     put_u1(p, tag_); | 
 |     put_u2be(p, type_name_->slot()); | 
 |     put_u2be(p, const_name_->slot()); | 
 |   } | 
 |   static EnumTypeElementValue *Read(const u1 *&p) { | 
 |     EnumTypeElementValue *value = new EnumTypeElementValue; | 
 |     value->type_name_ = constant(get_u2be(p)); | 
 |     value->const_name_ = constant(get_u2be(p)); | 
 |     return value; | 
 |   } | 
 |   Constant *type_name_; | 
 |   Constant *const_name_; | 
 | }; | 
 |  | 
 | struct ClassTypeElementValue : ElementValue { | 
 |   void Write(u1 *&p) { | 
 |     put_u1(p, tag_); | 
 |     put_u2be(p, class_info_->slot()); | 
 |   } | 
 |  | 
 |   virtual void ExtractClassNames() { | 
 |     size_t idx = 0; | 
 |     devtools_ijar::ExtractClassNames(class_info_->Display(), &idx); | 
 |   } | 
 |  | 
 |   static ClassTypeElementValue *Read(const u1 *&p) { | 
 |     ClassTypeElementValue *value = new ClassTypeElementValue; | 
 |     value->class_info_ = constant(get_u2be(p)); | 
 |     return value; | 
 |   } | 
 |   Constant *class_info_; | 
 | }; | 
 |  | 
 | struct ArrayTypeElementValue : ElementValue { | 
 |   virtual ~ArrayTypeElementValue() { | 
 |     for (const auto *value : values_) { | 
 |       delete value; | 
 |     } | 
 |   } | 
 |  | 
 |   virtual void ExtractClassNames() { | 
 |     for (auto *value : values_) { | 
 |       value->ExtractClassNames(); | 
 |     } | 
 |   } | 
 |  | 
 |   void Write(u1 *&p) { | 
 |     put_u1(p, tag_); | 
 |     put_u2be(p, values_.size()); | 
 |     for (auto *value : values_) { | 
 |       value->Write(p); | 
 |     } | 
 |   } | 
 |   static ArrayTypeElementValue *Read(const u1 *&p) { | 
 |     ArrayTypeElementValue *value = new ArrayTypeElementValue; | 
 |     u2 num_values = get_u2be(p); | 
 |     for (int ii = 0; ii < num_values; ++ii) { | 
 |       value->values_.push_back(ElementValue::Read(p)); | 
 |     } | 
 |     return value; | 
 |   } | 
 |   std::vector<ElementValue*> values_; | 
 | }; | 
 |  | 
 | // See sec.4.7.16 of JVM spec. | 
 | struct Annotation { | 
 |   virtual ~Annotation() { | 
 |     for (size_t i = 0; i < element_value_pairs_.size(); i++) { | 
 |       delete element_value_pairs_[i]->element_value_; | 
 |       delete element_value_pairs_[i]; | 
 |     } | 
 |   } | 
 |  | 
 |   void ExtractClassNames() { | 
 |     for (size_t i = 0; i < element_value_pairs_.size(); i++) { | 
 |       element_value_pairs_[i]->element_value_->ExtractClassNames(); | 
 |     } | 
 |   } | 
 |  | 
 |   void Write(u1 *&p) { | 
 |     put_u2be(p, type_->slot()); | 
 |     put_u2be(p, element_value_pairs_.size()); | 
 |     for (size_t ii = 0; ii < element_value_pairs_.size(); ++ii) { | 
 |       put_u2be(p, element_value_pairs_[ii]->element_name_->slot()); | 
 |       element_value_pairs_[ii]->element_value_->Write(p); | 
 |     } | 
 |   } | 
 |   static Annotation *Read(const u1 *&p) { | 
 |     Annotation *value = new Annotation; | 
 |     value->type_ = constant(get_u2be(p)); | 
 |     u2 num_element_value_pairs = get_u2be(p); | 
 |     for (int ii = 0; ii < num_element_value_pairs; ++ii) { | 
 |       ElementValuePair *pair = new ElementValuePair; | 
 |       pair->element_name_ = constant(get_u2be(p)); | 
 |       pair->element_value_ = ElementValue::Read(p); | 
 |       value->element_value_pairs_.push_back(pair); | 
 |     } | 
 |     return value; | 
 |   } | 
 |   Constant *type_; | 
 |   struct ElementValuePair { | 
 |     Constant *element_name_; | 
 |     ElementValue *element_value_; | 
 |   }; | 
 |   std::vector<ElementValuePair*> element_value_pairs_; | 
 | }; | 
 |  | 
 | // See sec 4.7.20 of Java 8 JVM Spec | 
 | // | 
 | // Each entry in the annotations table represents a single run-time visible | 
 | // annotation on a type used in a declaration or expression. The type_annotation | 
 | // structure has the following format: | 
 | // | 
 | // type_annotation { | 
 | //   u1 target_type; | 
 | //   union { | 
 | //     type_parameter_target; | 
 | //     supertype_target; | 
 | //     type_parameter_bound_target; | 
 | //     empty_target; | 
 | //     method_formal_parameter_target; | 
 | //     throws_target; | 
 | //     localvar_target; | 
 | //     catch_target; | 
 | //     offset_target; | 
 | //     type_argument_target; | 
 | //   } target_info; | 
 | //   type_path target_path; | 
 | //   u2        type_index; | 
 | //   u2        num_element_value_pairs; | 
 | //   { | 
 | //     u2            element_name_index; | 
 | //     element_value value; | 
 | //   } | 
 | //   element_value_pairs[num_element_value_pairs]; | 
 | // } | 
 | // | 
 | struct TypeAnnotation { | 
 |   virtual ~TypeAnnotation() { | 
 |     delete target_info_; | 
 |     delete type_path_; | 
 |     delete annotation_; | 
 |   } | 
 |  | 
 |   void ExtractClassNames() { | 
 |     annotation_->ExtractClassNames(); | 
 |   } | 
 |  | 
 |   void Write(u1 *&p) { | 
 |     put_u1(p, target_type_); | 
 |     target_info_->Write(p); | 
 |     type_path_->Write(p); | 
 |     annotation_->Write(p); | 
 |   } | 
 |  | 
 |   static TypeAnnotation *Read(const u1 *&p) { | 
 |     TypeAnnotation *value = new TypeAnnotation; | 
 |     value->target_type_ = get_u1(p); | 
 |     value->target_info_ = ReadTargetInfo(p, value->target_type_); | 
 |     value->type_path_ = TypePath::Read(p); | 
 |     value->annotation_ = Annotation::Read(p); | 
 |     return value; | 
 |   } | 
 |  | 
 |   struct TargetInfo { | 
 |     virtual ~TargetInfo() {} | 
 |     virtual void Write(u1 *&p) = 0; | 
 |   }; | 
 |  | 
 |   struct TypeParameterTargetInfo : TargetInfo { | 
 |     void Write(u1 *&p) { | 
 |       put_u1(p, type_parameter_index_); | 
 |     } | 
 |     static TypeParameterTargetInfo *Read(const u1 *&p) { | 
 |       TypeParameterTargetInfo *value = new TypeParameterTargetInfo; | 
 |       value->type_parameter_index_ = get_u1(p); | 
 |       return value; | 
 |     } | 
 |     u1 type_parameter_index_; | 
 |   }; | 
 |  | 
 |   struct ClassExtendsInfo : TargetInfo { | 
 |     void Write(u1 *&p) { | 
 |       put_u2be(p, supertype_index_); | 
 |     } | 
 |     static ClassExtendsInfo *Read(const u1 *&p) { | 
 |       ClassExtendsInfo *value = new ClassExtendsInfo; | 
 |       value->supertype_index_ = get_u2be(p); | 
 |       return value; | 
 |     } | 
 |     u2 supertype_index_; | 
 |   }; | 
 |  | 
 |   struct TypeParameterBoundInfo : TargetInfo { | 
 |     void Write(u1 *&p) { | 
 |       put_u1(p, type_parameter_index_); | 
 |       put_u1(p, bound_index_); | 
 |     } | 
 |     static TypeParameterBoundInfo *Read(const u1 *&p) { | 
 |       TypeParameterBoundInfo *value = new TypeParameterBoundInfo; | 
 |       value->type_parameter_index_ = get_u1(p); | 
 |       value->bound_index_ = get_u1(p); | 
 |       return value; | 
 |     } | 
 |     u1 type_parameter_index_; | 
 |     u1 bound_index_; | 
 |   }; | 
 |  | 
 |   struct EmptyInfo : TargetInfo { | 
 |     void Write(u1 *&p) {} | 
 |     static EmptyInfo *Read(const u1 *&p) { | 
 |       return new EmptyInfo; | 
 |     } | 
 |   }; | 
 |  | 
 |   struct MethodFormalParameterInfo : TargetInfo { | 
 |     void Write(u1 *&p) { | 
 |       put_u1(p, method_formal_parameter_index_); | 
 |     } | 
 |     static MethodFormalParameterInfo *Read(const u1 *&p) { | 
 |       MethodFormalParameterInfo *value = new MethodFormalParameterInfo; | 
 |       value->method_formal_parameter_index_ = get_u1(p); | 
 |       return value; | 
 |     } | 
 |     u1 method_formal_parameter_index_; | 
 |   }; | 
 |  | 
 |   struct ThrowsTypeInfo : TargetInfo { | 
 |     void Write(u1 *&p) { | 
 |       put_u2be(p, throws_type_index_); | 
 |     } | 
 |     static ThrowsTypeInfo *Read(const u1 *&p) { | 
 |       ThrowsTypeInfo *value = new ThrowsTypeInfo; | 
 |       value->throws_type_index_ = get_u2be(p); | 
 |       return value; | 
 |     } | 
 |     u2 throws_type_index_; | 
 |   }; | 
 |  | 
 |   static TargetInfo *ReadTargetInfo(const u1 *&p, u1 target_type) { | 
 |     switch (target_type) { | 
 |       case CLASS_TYPE_PARAMETER: | 
 |       case METHOD_TYPE_PARAMETER: | 
 |         return TypeParameterTargetInfo::Read(p); | 
 |       case CLASS_EXTENDS: | 
 |         return ClassExtendsInfo::Read(p); | 
 |       case CLASS_TYPE_PARAMETER_BOUND: | 
 |       case METHOD_TYPE_PARAMETER_BOUND: | 
 |         return TypeParameterBoundInfo::Read(p); | 
 |       case FIELD: | 
 |       case METHOD_RETURN: | 
 |       case METHOD_RECEIVER: | 
 |         return new EmptyInfo; | 
 |       case METHOD_FORMAL_PARAMETER: | 
 |         return MethodFormalParameterInfo::Read(p); | 
 |       case THROWS: | 
 |         return ThrowsTypeInfo::Read(p); | 
 |       default: | 
 |         fprintf(stderr, "Illegal type annotation target type: %d\n", | 
 |                 target_type); | 
 |         abort(); | 
 |     } | 
 |   } | 
 |  | 
 |   struct TypePath { | 
 |     void Write(u1 *&p) { | 
 |       put_u1(p, path_.size()); | 
 |       for (TypePathEntry entry : path_) { | 
 |         put_u1(p, entry.type_path_kind_); | 
 |         put_u1(p, entry.type_argument_index_); | 
 |       } | 
 |     } | 
 |     static TypePath *Read(const u1 *&p) { | 
 |       TypePath *value = new TypePath; | 
 |       u1 path_length = get_u1(p); | 
 |       for (int ii = 0; ii < path_length; ++ii) { | 
 |         TypePathEntry entry; | 
 |         entry.type_path_kind_ = get_u1(p); | 
 |         entry.type_argument_index_ = get_u1(p); | 
 |         value->path_.push_back(entry); | 
 |       } | 
 |       return value; | 
 |     } | 
 |  | 
 |     struct TypePathEntry { | 
 |       u1 type_path_kind_; | 
 |       u1 type_argument_index_; | 
 |     }; | 
 |     std::vector<TypePathEntry> path_; | 
 |   }; | 
 |  | 
 |   u1 target_type_; | 
 |   TargetInfo *target_info_; | 
 |   TypePath *type_path_; | 
 |   Annotation *annotation_; | 
 | }; | 
 |  | 
 | struct AnnotationTypeElementValue : ElementValue { | 
 |   virtual ~AnnotationTypeElementValue() { | 
 |     delete annotation_; | 
 |   } | 
 |  | 
 |   void Write(u1 *&p) { | 
 |     put_u1(p, tag_); | 
 |     annotation_->Write(p); | 
 |   } | 
 |   static AnnotationTypeElementValue *Read(const u1 *&p) { | 
 |     AnnotationTypeElementValue *value = new AnnotationTypeElementValue; | 
 |     value->annotation_ = Annotation::Read(p); | 
 |     return value; | 
 |   } | 
 |  | 
 |   Annotation *annotation_; | 
 | }; | 
 |  | 
 | ElementValue* ElementValue::Read(const u1 *&p) { | 
 |   const u1* start = p; | 
 |   ElementValue *result; | 
 |   u1 tag = get_u1(p); | 
 |   if (tag != 0 && strchr("BCDFIJSZs", (char) tag) != NULL) { | 
 |     result = BaseTypeElementValue::Read(p); | 
 |   } else if ((char) tag == 'e') { | 
 |     result = EnumTypeElementValue::Read(p); | 
 |   } else if ((char) tag == 'c') { | 
 |     result = ClassTypeElementValue::Read(p); | 
 |   } else if ((char) tag == '[') { | 
 |     result = ArrayTypeElementValue::Read(p); | 
 |   } else if ((char) tag == '@') { | 
 |     result = AnnotationTypeElementValue::Read(p); | 
 |   } else { | 
 |     fprintf(stderr, "Illegal element_value::tag: %d\n", tag); | 
 |     abort(); | 
 |   } | 
 |   result->tag_ = tag; | 
 |   result->length_ = p - start; | 
 |   return result; | 
 | } | 
 |  | 
 | // See sec.4.7.20 of JVM spec. | 
 | // We preserve AnnotationDefault attributes because they are required | 
 | // in order to make use of an annotation in new code. | 
 | struct AnnotationDefaultAttribute : Attribute { | 
 |   virtual ~AnnotationDefaultAttribute() { | 
 |     delete default_value_; | 
 |   } | 
 |  | 
 |   static AnnotationDefaultAttribute* Read(const u1 *&p, | 
 |                                           Constant *attribute_name) { | 
 |     AnnotationDefaultAttribute *attr = new AnnotationDefaultAttribute; | 
 |     attr->attribute_name_ = attribute_name; | 
 |     attr->default_value_ = ElementValue::Read(p); | 
 |     return attr; | 
 |   } | 
 |  | 
 |   void Write(u1 *&p) { | 
 |     WriteProlog(p, default_value_->length_); | 
 |     default_value_->Write(p); | 
 |   } | 
 |  | 
 |   virtual void ExtractClassNames() { | 
 |     default_value_->ExtractClassNames(); | 
 |   } | 
 |  | 
 |   ElementValue *default_value_; | 
 | }; | 
 |  | 
 | // See sec.4.7.2 of JVM spec. | 
 | // We preserve ConstantValue attributes because they are required for | 
 | // compile-time constant propagation. | 
 | struct ConstantValueAttribute : Attribute { | 
 |  | 
 |   static ConstantValueAttribute* Read(const u1 *&p, Constant *attribute_name) { | 
 |     ConstantValueAttribute *attr = new ConstantValueAttribute; | 
 |     attr->attribute_name_ = attribute_name; | 
 |     attr->constantvalue_ = constant(get_u2be(p)); | 
 |     return attr; | 
 |   } | 
 |  | 
 |   void Write(u1 *&p) { | 
 |     WriteProlog(p, 2); | 
 |     put_u2be(p, constantvalue_->slot()); | 
 |   } | 
 |  | 
 |   Constant *constantvalue_; | 
 | }; | 
 |  | 
 | // See sec.4.7.9 of JVM spec. | 
 | // We preserve Signature attributes because they are required by the | 
 | // compiler for type-checking of generics. | 
 | struct SignatureAttribute : Attribute { | 
 |  | 
 |   static SignatureAttribute* Read(const u1 *&p, Constant *attribute_name) { | 
 |     SignatureAttribute *attr = new SignatureAttribute; | 
 |     attr->attribute_name_ = attribute_name; | 
 |     attr->signature_  = constant(get_u2be(p)); | 
 |     return attr; | 
 |   } | 
 |  | 
 |   void Write(u1 *&p) { | 
 |     WriteProlog(p, 2); | 
 |     put_u2be(p, signature_->slot()); | 
 |   } | 
 |  | 
 |   virtual void ExtractClassNames() { | 
 |     size_t signature_idx = 0; | 
 |     devtools_ijar::ExtractClassNames(signature_->Display(), &signature_idx); | 
 |   } | 
 |  | 
 |   Constant *signature_; | 
 | }; | 
 |  | 
 | // See sec.4.7.15 of JVM spec. | 
 | // We preserve Deprecated attributes because they are required by the | 
 | // compiler to generate warning messages. | 
 | struct DeprecatedAttribute : Attribute { | 
 |  | 
 |   static DeprecatedAttribute* Read(const u1 *&p, Constant *attribute_name) { | 
 |     DeprecatedAttribute *attr = new DeprecatedAttribute; | 
 |     attr->attribute_name_ = attribute_name; | 
 |     return attr; | 
 |   } | 
 |  | 
 |   void Write(u1 *&p) { | 
 |     WriteProlog(p, 0); | 
 |   } | 
 | }; | 
 |  | 
 | // See sec.4.7.16-17 of JVM spec v3.  Includes RuntimeVisible and | 
 | // RuntimeInvisible. | 
 | // | 
 | // We preserve all annotations. | 
 | struct AnnotationsAttribute : Attribute { | 
 |   virtual ~AnnotationsAttribute() { | 
 |     for (size_t i = 0; i < annotations_.size(); i++) { | 
 |       delete annotations_[i]; | 
 |     } | 
 |   } | 
 |  | 
 |   static AnnotationsAttribute* Read(const u1 *&p, Constant *attribute_name) { | 
 |     AnnotationsAttribute *attr = new AnnotationsAttribute; | 
 |     attr->attribute_name_ = attribute_name; | 
 |     u2 num_annotations = get_u2be(p); | 
 |     for (int ii = 0; ii < num_annotations; ++ii) { | 
 |       Annotation *annotation = Annotation::Read(p); | 
 |       attr->annotations_.push_back(annotation); | 
 |     } | 
 |     return attr; | 
 |   } | 
 |  | 
 |   virtual void ExtractClassNames() { | 
 |     for (auto *annotation : annotations_) { | 
 |       annotation->ExtractClassNames(); | 
 |     } | 
 |   } | 
 |  | 
 |   void Write(u1 *&p) { | 
 |     WriteProlog(p, -1); | 
 |     u1 *payload_start = p - 4; | 
 |     put_u2be(p, annotations_.size()); | 
 |     for (auto *annotation : annotations_) { | 
 |       annotation->Write(p); | 
 |     } | 
 |     put_u4be(payload_start, p - 4 - payload_start);  // backpatch length | 
 |   } | 
 |  | 
 |   std::vector<Annotation*> annotations_; | 
 | }; | 
 |  | 
 | // See sec.4.7.18-19 of JVM spec.  Includes RuntimeVisible and | 
 | // RuntimeInvisible. | 
 | // | 
 | // We preserve all annotations. | 
 | struct ParameterAnnotationsAttribute : Attribute { | 
 |  | 
 |   static ParameterAnnotationsAttribute* Read(const u1 *&p, | 
 |                                              Constant *attribute_name) { | 
 |     ParameterAnnotationsAttribute *attr = new ParameterAnnotationsAttribute; | 
 |     attr->attribute_name_ = attribute_name; | 
 |     u1 num_parameters = get_u1(p); | 
 |     for (int ii = 0; ii < num_parameters; ++ii) { | 
 |       std::vector<Annotation*> annotations; | 
 |       u2 num_annotations = get_u2be(p); | 
 |       for (int ii = 0; ii < num_annotations; ++ii) { | 
 |         Annotation *annotation = Annotation::Read(p); | 
 |         annotations.push_back(annotation); | 
 |       } | 
 |       attr->parameter_annotations_.push_back(annotations); | 
 |     } | 
 |     return attr; | 
 |   } | 
 |  | 
 |   virtual void ExtractClassNames() { | 
 |     for (size_t i = 0; i < parameter_annotations_.size(); i++) { | 
 |       const std::vector<Annotation*>& annotations = parameter_annotations_[i]; | 
 |       for (size_t j = 0; j < annotations.size(); j++) { | 
 |         annotations[j]->ExtractClassNames(); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   void Write(u1 *&p) { | 
 |     WriteProlog(p, -1); | 
 |     u1 *payload_start = p - 4; | 
 |     put_u1(p, parameter_annotations_.size()); | 
 |     for (size_t ii = 0; ii < parameter_annotations_.size(); ++ii) { | 
 |       std::vector<Annotation *> &annotations = parameter_annotations_[ii]; | 
 |       put_u2be(p, annotations.size()); | 
 |       for (size_t jj = 0; jj < annotations.size(); ++jj) { | 
 |         annotations[jj]->Write(p); | 
 |       } | 
 |     } | 
 |     put_u4be(payload_start, p - 4 - payload_start);  // backpatch length | 
 |   } | 
 |  | 
 |   std::vector<std::vector<Annotation*> > parameter_annotations_; | 
 | }; | 
 |  | 
 | // See sec.4.7.20 of Java 8 JVM spec. Includes RuntimeVisibleTypeAnnotations | 
 | // and RuntimeInvisibleTypeAnnotations. | 
 | struct TypeAnnotationsAttribute : Attribute { | 
 |   static TypeAnnotationsAttribute* Read(const u1 *&p, Constant *attribute_name, | 
 |                                         u4 attribute_length) { | 
 |     auto attr = new TypeAnnotationsAttribute; | 
 |     attr->attribute_name_ = attribute_name; | 
 |     u2 num_annotations = get_u2be(p); | 
 |     for (int ii = 0; ii < num_annotations; ++ii) { | 
 |       TypeAnnotation *annotation = TypeAnnotation::Read(p); | 
 |       attr->type_annotations_.push_back(annotation); | 
 |     } | 
 |     return attr; | 
 |   } | 
 |  | 
 |   virtual void ExtractClassNames() { | 
 |     for (auto *type_annotation : type_annotations_) { | 
 |       type_annotation->ExtractClassNames(); | 
 |     } | 
 |   } | 
 |  | 
 |   void Write(u1 *&p) { | 
 |     WriteProlog(p, -1); | 
 |     u1 *payload_start = p - 4; | 
 |     put_u2be(p, type_annotations_.size()); | 
 |     for (TypeAnnotation *annotation : type_annotations_) { | 
 |       annotation->Write(p); | 
 |     } | 
 |     put_u4be(payload_start, p - 4 - payload_start);  // backpatch length | 
 |   } | 
 |  | 
 |   std::vector<TypeAnnotation*> type_annotations_; | 
 | }; | 
 |  | 
 | // See JVMS §4.7.24 | 
 | struct MethodParametersAttribute : Attribute { | 
 |   static MethodParametersAttribute *Read(const u1 *&p, Constant *attribute_name, | 
 |                                          u4 attribute_length) { | 
 |     auto attr = new MethodParametersAttribute; | 
 |     attr->attribute_name_ = attribute_name; | 
 |     u1 parameters_count = get_u1(p); | 
 |     for (int ii = 0; ii < parameters_count; ++ii) { | 
 |       MethodParameter* parameter = new MethodParameter; | 
 |       parameter->name_ = constant(get_u2be(p)); | 
 |       parameter->access_flags_ = get_u2be(p); | 
 |       attr->parameters_.push_back(parameter); | 
 |     } | 
 |     return attr; | 
 |   } | 
 |  | 
 |   void Write(u1 *&p) { | 
 |     WriteProlog(p, -1); | 
 |     u1 *payload_start = p - 4; | 
 |     put_u1(p, parameters_.size()); | 
 |     for (MethodParameter* parameter : parameters_) { | 
 |       put_u2be(p, parameter->name_->slot()); | 
 |       put_u2be(p, parameter->access_flags_); | 
 |     } | 
 |     put_u4be(payload_start, p - 4 - payload_start);  // backpatch length | 
 |   } | 
 |  | 
 |   struct MethodParameter { | 
 |     Constant *name_; | 
 |     u2 access_flags_; | 
 |   }; | 
 |  | 
 |   std::vector<MethodParameter*> parameters_; | 
 | }; | 
 |  | 
 | struct GeneralAttribute : Attribute { | 
 |   static GeneralAttribute* Read(const u1 *&p, Constant *attribute_name, | 
 |                                 u4 attribute_length) { | 
 |     auto attr = new GeneralAttribute; | 
 |     attr->attribute_name_ = attribute_name; | 
 |     attr->attribute_length_ = attribute_length; | 
 |     attr->attribute_content_ = p; | 
 |     p += attribute_length; | 
 |     return attr; | 
 |   } | 
 |  | 
 |   void Write(u1 *&p) { | 
 |     WriteProlog(p, attribute_length_); | 
 |     put_n(p, attribute_content_, attribute_length_); | 
 |   } | 
 |  | 
 |   u4 attribute_length_; | 
 |   const u1 *attribute_content_; | 
 | }; | 
 |  | 
 | /********************************************************************** | 
 |  *                                                                    * | 
 |  *                             ClassFile                              * | 
 |  *                                                                    * | 
 |  **********************************************************************/ | 
 |  | 
 | struct HasAttrs { | 
 |   std::vector<Attribute*> attributes; | 
 |  | 
 |   void WriteAttrs(u1 *&p); | 
 |   void ReadAttrs(const u1 *&p); | 
 |  | 
 |   virtual ~HasAttrs() { | 
 |     for (const auto *attribute : attributes) { | 
 |       delete attribute; | 
 |     } | 
 |   } | 
 |  | 
 |   void ExtractClassNames() { | 
 |     for (auto *attribute : attributes) { | 
 |       attribute->ExtractClassNames(); | 
 |     } | 
 |   } | 
 | }; | 
 |  | 
 | // A field or method. | 
 | // See sec.4.5 and 4.6 of JVM spec. | 
 | struct Member : HasAttrs { | 
 |   u2 access_flags; | 
 |   Constant *name; | 
 |   Constant *descriptor; | 
 |  | 
 |   static Member* Read(const u1 *&p) { | 
 |     Member *m = new Member; | 
 |     m->access_flags = get_u2be(p); | 
 |     m->name = constant(get_u2be(p)); | 
 |     m->descriptor = constant(get_u2be(p)); | 
 |     m->ReadAttrs(p); | 
 |     return m; | 
 |   } | 
 |  | 
 |   void Write(u1 *&p) { | 
 |     put_u2be(p, access_flags); | 
 |     put_u2be(p, name->slot()); | 
 |     put_u2be(p, descriptor->slot()); | 
 |     WriteAttrs(p); | 
 |   } | 
 | }; | 
 |  | 
 | // See sec.4.1 of JVM spec. | 
 | struct ClassFile : HasAttrs { | 
 |  | 
 |   size_t length; | 
 |  | 
 |   // Header: | 
 |   u4 magic; | 
 |   u2 major; | 
 |   u2 minor; | 
 |  | 
 |   // Body: | 
 |   u2 access_flags; | 
 |   Constant *this_class; | 
 |   Constant *super_class; | 
 |   std::vector<Constant*> interfaces; | 
 |   std::vector<Member*> fields; | 
 |   std::vector<Member*> methods; | 
 |  | 
 |   virtual ~ClassFile() { | 
 |     for (size_t i = 0; i < fields.size(); i++) { | 
 |       delete fields[i]; | 
 |     } | 
 |  | 
 |     for (size_t i = 0; i < methods.size(); i++) { | 
 |       delete methods[i]; | 
 |     } | 
 |  | 
 |     // Constants do not need to be deleted; they are owned by the constant pool. | 
 |   } | 
 |  | 
 |   void WriteClass(u1 *&p); | 
 |  | 
 |   bool ReadConstantPool(const u1 *&p); | 
 |  | 
 |   bool IsLocalOrAnonymous(); | 
 |  | 
 |   void WriteHeader(u1 *&p) { | 
 |     put_u4be(p, magic); | 
 |     put_u2be(p, major); | 
 |     put_u2be(p, minor); | 
 |  | 
 |     put_u2be(p, const_pool_out.size()); | 
 |     for (u2 ii = 1; ii < const_pool_out.size(); ++ii) { | 
 |       if (const_pool_out[ii] != NULL) { // NB: NULLs appear after long/double. | 
 |         const_pool_out[ii]->Write(p); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   void WriteBody(u1 *&p) { | 
 |     put_u2be(p, access_flags); | 
 |     put_u2be(p, this_class->slot()); | 
 |     put_u2be(p, super_class == NULL ? 0 : super_class->slot()); | 
 |     put_u2be(p, interfaces.size()); | 
 |     for (size_t ii = 0; ii < interfaces.size(); ++ii) { | 
 |       put_u2be(p, interfaces[ii]->slot()); | 
 |     } | 
 |     put_u2be(p, fields.size()); | 
 |     for (size_t ii = 0; ii < fields.size(); ++ii) { | 
 |       fields[ii]->Write(p); | 
 |     } | 
 |     put_u2be(p, methods.size()); | 
 |     for (size_t ii = 0; ii < methods.size(); ++ii) { | 
 |       methods[ii]->Write(p); | 
 |     } | 
 |  | 
 |     Attribute* inner_classes = NULL; | 
 |  | 
 |     // Make the inner classes attribute the last, so that it can know which | 
 |     // constants were needed | 
 |     for (size_t ii = 0; ii < attributes.size(); ii++) { | 
 |       if (attributes[ii]->attribute_name_->Display() == "InnerClasses") { | 
 |         inner_classes = attributes[ii]; | 
 |         attributes.erase(attributes.begin() + ii); | 
 |         break; | 
 |       } | 
 |     } | 
 |  | 
 |     if (inner_classes != NULL) { | 
 |       attributes.push_back(inner_classes); | 
 |     } | 
 |  | 
 |     WriteAttrs(p); | 
 |   } | 
 |  | 
 | }; | 
 |  | 
 | void HasAttrs::ReadAttrs(const u1 *&p) { | 
 |   u2 attributes_count = get_u2be(p); | 
 |   for (int ii = 0; ii < attributes_count; ii++) { | 
 |     Constant *attribute_name = constant(get_u2be(p)); | 
 |     u4 attribute_length = get_u4be(p); | 
 |  | 
 |     std::string attr_name = attribute_name->Display(); | 
 |     if (attr_name == "SourceFile" || | 
 |         attr_name == "StackMapTable" || | 
 |         attr_name == "LineNumberTable" || | 
 |         attr_name == "LocalVariableTable" || | 
 |         attr_name == "LocalVariableTypeTable" || | 
 |         attr_name == "Code" || | 
 |         attr_name == "Synthetic" || | 
 |         attr_name == "BootstrapMethods" || | 
 |         attr_name == "SourceDebugExtension") { | 
 |       p += attribute_length; // drop these attributes | 
 |     } else if (attr_name == "Exceptions") { | 
 |       attributes.push_back(ExceptionsAttribute::Read(p, attribute_name)); | 
 |     } else if (attr_name == "Signature") { | 
 |       attributes.push_back(SignatureAttribute::Read(p, attribute_name)); | 
 |     } else if (attr_name == "Deprecated") { | 
 |       attributes.push_back(DeprecatedAttribute::Read(p, attribute_name)); | 
 |     } else if (attr_name == "EnclosingMethod") { | 
 |       attributes.push_back(EnclosingMethodAttribute::Read(p, attribute_name)); | 
 |     } else if (attr_name == "InnerClasses") { | 
 |       // TODO(bazel-team): omit private inner classes | 
 |       attributes.push_back(InnerClassesAttribute::Read(p, attribute_name)); | 
 |     } else if (attr_name == "AnnotationDefault") { | 
 |       attributes.push_back(AnnotationDefaultAttribute::Read(p, attribute_name)); | 
 |     } else if (attr_name == "ConstantValue") { | 
 |       attributes.push_back(ConstantValueAttribute::Read(p, attribute_name)); | 
 |     } else if (attr_name == "RuntimeVisibleAnnotations" || | 
 |                attr_name == "RuntimeInvisibleAnnotations") { | 
 |       attributes.push_back(AnnotationsAttribute::Read(p, attribute_name)); | 
 |     } else if (attr_name == "RuntimeVisibleParameterAnnotations" || | 
 |                attr_name == "RuntimeInvisibleParameterAnnotations") { | 
 |       attributes.push_back( | 
 |           ParameterAnnotationsAttribute::Read(p, attribute_name)); | 
 |     } else if (attr_name == "Scala" || | 
 |                attr_name == "ScalaSig" || | 
 |                attr_name == "ScalaInlineInfo") { | 
 |       // These are opaque blobs, so can be handled with a general | 
 |       // attribute handler | 
 |       attributes.push_back(GeneralAttribute::Read(p, attribute_name, | 
 |                                                   attribute_length)); | 
 |     } else if (attr_name == "RuntimeVisibleTypeAnnotations" || | 
 |                attr_name == "RuntimeInvisibleTypeAnnotations") { | 
 |       attributes.push_back(TypeAnnotationsAttribute::Read(p, attribute_name, | 
 |                                                           attribute_length)); | 
 |     } else if (attr_name == "MethodParameters") { | 
 |       attributes.push_back( | 
 |           MethodParametersAttribute::Read(p, attribute_name, attribute_length)); | 
 |     } else { | 
 |       // Skip over unknown attributes with a warning.  The JVM spec | 
 |       // says this is ok, so long as we handle the mandatory attributes. | 
 |       fprintf(stderr, "ijar: skipping unknown attribute: \"%s\".\n", | 
 |               attr_name.c_str()); | 
 |       p += attribute_length; | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | void HasAttrs::WriteAttrs(u1 *&p) { | 
 |   u1* p_size = p; | 
 |  | 
 |   put_u2be(p, 0); | 
 |   int n_written_attrs = 0; | 
 |   for (size_t ii = 0; ii < attributes.size(); ii++) { | 
 |     u1* before = p; | 
 |     attributes[ii]->Write(p); | 
 |     if (p != before) { | 
 |       n_written_attrs++; | 
 |     } | 
 |   } | 
 |  | 
 |   put_u2be(p_size, n_written_attrs); | 
 | } | 
 |  | 
 | // See sec.4.4 of JVM spec. | 
 | bool ClassFile::ReadConstantPool(const u1 *&p) { | 
 |  | 
 |   const_pool_in.clear(); | 
 |   const_pool_in.push_back(NULL); // dummy first item | 
 |  | 
 |   u2 cp_count = get_u2be(p); | 
 |   for (int ii = 1; ii < cp_count; ++ii) { | 
 |     u1 tag = get_u1(p); | 
 |  | 
 |     if (devtools_ijar::verbose) { | 
 |       fprintf(stderr, "cp[%d/%d] = tag %d\n", ii, cp_count, tag); | 
 |     } | 
 |  | 
 |     switch(tag) { | 
 |       case CONSTANT_Class: { | 
 |         u2 name_index = get_u2be(p); | 
 |         const_pool_in.push_back(new Constant_Class(name_index)); | 
 |         break; | 
 |       } | 
 |       case CONSTANT_FieldRef: | 
 |       case CONSTANT_Methodref: | 
 |       case CONSTANT_Interfacemethodref: { | 
 |         u2 class_index = get_u2be(p); | 
 |         u2 nti = get_u2be(p); | 
 |         const_pool_in.push_back(new Constant_FMIref(tag, class_index, nti)); | 
 |         break; | 
 |       } | 
 |       case CONSTANT_String: { | 
 |         u2 string_index = get_u2be(p); | 
 |         const_pool_in.push_back(new Constant_String(string_index)); | 
 |         break; | 
 |       } | 
 |       case CONSTANT_NameAndType: { | 
 |         u2 name_index = get_u2be(p); | 
 |         u2 descriptor_index = get_u2be(p); | 
 |         const_pool_in.push_back( | 
 |             new Constant_NameAndType(name_index, descriptor_index)); | 
 |         break; | 
 |       } | 
 |       case CONSTANT_Utf8: { | 
 |         u2 length = get_u2be(p); | 
 |         if (devtools_ijar::verbose) { | 
 |           fprintf(stderr, "Utf8: \"%s\" (%d)\n", | 
 |                   std::string((const char*) p, length).c_str(), length); | 
 |         } | 
 |  | 
 |         const_pool_in.push_back(new Constant_Utf8(length, p)); | 
 |         p += length; | 
 |         break; | 
 |       } | 
 |       case CONSTANT_Integer: | 
 |       case CONSTANT_Float: { | 
 |         u4 bytes = get_u4be(p); | 
 |         const_pool_in.push_back(new Constant_IntegerOrFloat(tag, bytes)); | 
 |         break; | 
 |       } | 
 |       case CONSTANT_Long: | 
 |       case CONSTANT_Double: { | 
 |         u4 high_bytes = get_u4be(p); | 
 |         u4 low_bytes = get_u4be(p); | 
 |         const_pool_in.push_back( | 
 |             new Constant_LongOrDouble(tag, high_bytes, low_bytes)); | 
 |         // Longs and doubles occupy two constant pool slots. | 
 |         // ("In retrospect, making 8-byte constants take two "constant | 
 |         // pool entries was a poor choice." --JVM Spec.) | 
 |         const_pool_in.push_back(NULL); | 
 |         ii++; | 
 |         break; | 
 |       } | 
 |       case CONSTANT_MethodHandle: { | 
 |         u1 reference_kind = get_u1(p); | 
 |         u2 reference_index = get_u2be(p); | 
 |         const_pool_in.push_back( | 
 |             new Constant_MethodHandle(reference_kind, reference_index)); | 
 |         break; | 
 |       } | 
 |       case CONSTANT_MethodType: { | 
 |         u2 descriptor_index = get_u2be(p); | 
 |         const_pool_in.push_back(new Constant_MethodType(descriptor_index)); | 
 |         break; | 
 |       } | 
 |       case CONSTANT_InvokeDynamic: { | 
 |         u2 bootstrap_method_attr = get_u2be(p); | 
 |         u2 name_name_type_index = get_u2be(p); | 
 |         const_pool_in.push_back(new Constant_InvokeDynamic( | 
 |             bootstrap_method_attr, name_name_type_index)); | 
 |         break; | 
 |       } | 
 |       default: { | 
 |         fprintf(stderr, "Unknown constant: %02x. Passing class through.\n", | 
 |                 tag); | 
 |         return false; | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | bool ClassFile::IsLocalOrAnonymous() { | 
 |   for (const Attribute *attribute : attributes) { | 
 |     if (attribute->attribute_name_->Display() == "EnclosingMethod") { | 
 |       // JVMS 4.7.6: a class must has EnclosingMethod attribute iff it | 
 |       // represents a local class or an anonymous class | 
 |       return true; | 
 |     } | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | static ClassFile *ReadClass(const void *classdata, size_t length) { | 
 |   const u1 *p = (u1*) classdata; | 
 |  | 
 |   ClassFile *clazz = new ClassFile; | 
 |  | 
 |   clazz->length = length; | 
 |  | 
 |   clazz->magic = get_u4be(p); | 
 |   if (clazz->magic != 0xCAFEBABE) { | 
 |     fprintf(stderr, "Bad magic %" PRIx32 "\n", clazz->magic); | 
 |     abort(); | 
 |   } | 
 |   clazz->major = get_u2be(p); | 
 |   clazz->minor = get_u2be(p); | 
 |  | 
 |   if (!clazz->ReadConstantPool(p)) { | 
 |     delete clazz; | 
 |     return NULL; | 
 |   } | 
 |  | 
 |   clazz->access_flags = get_u2be(p); | 
 |   clazz->this_class = constant(get_u2be(p)); | 
 |   class_name = clazz->this_class; | 
 |  | 
 |   u2 super_class_id = get_u2be(p); | 
 |   clazz->super_class = super_class_id == 0 ? NULL : constant(super_class_id); | 
 |  | 
 |   u2 interfaces_count = get_u2be(p); | 
 |   for (int ii = 0; ii < interfaces_count; ++ii) { | 
 |     clazz->interfaces.push_back(constant(get_u2be(p))); | 
 |   } | 
 |  | 
 |   u2 fields_count = get_u2be(p); | 
 |   for (int ii = 0; ii < fields_count; ++ii) { | 
 |     Member *field = Member::Read(p); | 
 |  | 
 |     if ((field->access_flags & ACC_PRIVATE) == ACC_PRIVATE) { | 
 |       // drop private fields | 
 |       continue; | 
 |     } | 
 |     clazz->fields.push_back(field); | 
 |   } | 
 |  | 
 |   u2 methods_count = get_u2be(p); | 
 |   for (int ii = 0; ii < methods_count; ++ii) { | 
 |     Member *method = Member::Read(p); | 
 |  | 
 |     // drop class initializers | 
 |     if (method->name->Display() == "<clinit>") continue; | 
 |  | 
 |     if ((method->access_flags & ACC_PRIVATE) == ACC_PRIVATE) { | 
 |       // drop private methods | 
 |       continue; | 
 |     } | 
 |     if ((method->access_flags & (ACC_SYNTHETIC | ACC_BRIDGE)) == | 
 |         ACC_SYNTHETIC) { | 
 |       // drop non-bridge synthetic methods, e.g. package-private synthetic | 
 |       // constructors used to instantiate private nested classes within their | 
 |       // declaring compilation unit | 
 |       continue; | 
 |     } | 
 |     clazz->methods.push_back(method); | 
 |   } | 
 |  | 
 |   clazz->ReadAttrs(p); | 
 |  | 
 |   return clazz; | 
 | } | 
 |  | 
 | // In theory, '/' is also reserved, but it's okay if we just parse package | 
 | // identifiers as part of the class name. Note that signatures are UTF-8, but | 
 | // this works just as well as in plain ASCII. | 
 | static const char *SIGNATURE_NON_IDENTIFIER_CHARS = ".;[<>:"; | 
 |  | 
 | void Expect(const std::string& desc, size_t* p, char expected) { | 
 |   if (desc[*p] != expected) { | 
 |     fprintf(stderr, "Expected '%c' in '%s' at %zd in signature\n", | 
 |             expected, desc.substr(*p).c_str(), *p); | 
 |     exit(1); | 
 |   } | 
 |  | 
 |   *p += 1; | 
 | } | 
 |  | 
 | // These functions form a crude recursive descent parser for descriptors and | 
 | // signatures in class files (see JVM spec 4.3). | 
 | // | 
 | // This parser is a bit more liberal than the spec, but this should be fine, | 
 | // because it accepts all valid class files and croaks only on invalid ones. | 
 | void ParseFromClassTypeSignature(const std::string& desc, size_t* p); | 
 | void ParseSimpleClassTypeSignature(const std::string& desc, size_t* p); | 
 | void ParseClassTypeSignatureSuffix(const std::string& desc, size_t* p); | 
 | void ParseIdentifier(const std::string& desc, size_t* p); | 
 | void ParseTypeArgumentsOpt(const std::string& desc, size_t* p); | 
 | void ParseMethodDescriptor(const std::string& desc, size_t* p); | 
 |  | 
 | void ParseClassTypeSignature(const std::string& desc, size_t* p) { | 
 |   Expect(desc, p, 'L'); | 
 |   ParseSimpleClassTypeSignature(desc, p); | 
 |   ParseClassTypeSignatureSuffix(desc, p); | 
 |   Expect(desc, p, ';'); | 
 | } | 
 |  | 
 | void ParseSimpleClassTypeSignature(const std::string& desc, size_t* p) { | 
 |   ParseIdentifier(desc, p); | 
 |   ParseTypeArgumentsOpt(desc, p); | 
 | } | 
 |  | 
 | void ParseClassTypeSignatureSuffix(const std::string& desc, size_t* p) { | 
 |   while (desc[*p] == '.') { | 
 |     *p += 1; | 
 |     ParseSimpleClassTypeSignature(desc, p); | 
 |   } | 
 | } | 
 |  | 
 | void ParseIdentifier(const std::string& desc, size_t* p) { | 
 |   size_t next = desc.find_first_of(SIGNATURE_NON_IDENTIFIER_CHARS, *p); | 
 |   std::string id = desc.substr(*p, next - *p); | 
 |   used_class_names.insert(id); | 
 |   *p = next; | 
 | } | 
 |  | 
 | void ParseTypeArgumentsOpt(const std::string& desc, size_t* p) { | 
 |   if (desc[*p] != '<') { | 
 |     return; | 
 |   } | 
 |  | 
 |   *p += 1; | 
 |   while (desc[*p] != '>') { | 
 |     switch (desc[*p]) { | 
 |       case '*': | 
 |         *p += 1; | 
 |         break; | 
 |  | 
 |       case '+': | 
 |       case '-': | 
 |         *p += 1; | 
 |         ExtractClassNames(desc, p); | 
 |         break; | 
 |  | 
 |       default: | 
 |         ExtractClassNames(desc, p); | 
 |         break; | 
 |     } | 
 |   } | 
 |  | 
 |   *p += 1; | 
 | } | 
 |  | 
 | void ParseMethodDescriptor(const std::string& desc, size_t* p) { | 
 |   Expect(desc, p, '('); | 
 |   while (desc[*p] != ')') { | 
 |     ExtractClassNames(desc, p); | 
 |   } | 
 |  | 
 |   Expect(desc, p, ')'); | 
 |   ExtractClassNames(desc, p); | 
 | } | 
 |  | 
 | void ParseFormalTypeParameters(const std::string& desc, size_t* p) { | 
 |   Expect(desc, p, '<'); | 
 |   while (desc[*p] != '>') { | 
 |     ParseIdentifier(desc, p); | 
 |     Expect(desc, p, ':'); | 
 |     if (desc[*p] != ':' && desc[*p] != '>') { | 
 |       ExtractClassNames(desc, p); | 
 |     } | 
 |  | 
 |     while (desc[*p] == ':') { | 
 |       Expect(desc, p, ':'); | 
 |       ExtractClassNames(desc, p); | 
 |     } | 
 |   } | 
 |  | 
 |   Expect(desc, p, '>'); | 
 | } | 
 |  | 
 | void ExtractClassNames(const std::string& desc, size_t* p) { | 
 |   switch (desc[*p]) { | 
 |     case '<': | 
 |       ParseFormalTypeParameters(desc, p); | 
 |       ExtractClassNames(desc, p); | 
 |       break; | 
 |  | 
 |     case 'L': | 
 |       ParseClassTypeSignature(desc, p); | 
 |       break; | 
 |  | 
 |     case '[': | 
 |       *p += 1; | 
 |       ExtractClassNames(desc, p); | 
 |       break; | 
 |  | 
 |     case 'T': | 
 |       *p += 1; | 
 |       ParseIdentifier(desc, p); | 
 |       Expect(desc, p, ';'); | 
 |       break; | 
 |  | 
 |     case '(': | 
 |       ParseMethodDescriptor(desc, p); | 
 |       break; | 
 |  | 
 |     case 'B': | 
 |     case 'C': | 
 |     case 'D': | 
 |     case 'F': | 
 |     case 'I': | 
 |     case 'J': | 
 |     case 'S': | 
 |     case 'Z': | 
 |     case 'V': | 
 |       *p += 1; | 
 |       break; | 
 |  | 
 |     default: | 
 |       fprintf(stderr, "Invalid signature %s\n", desc.substr(*p).c_str()); | 
 |   } | 
 | } | 
 |  | 
 | void ClassFile::WriteClass(u1 *&p) { | 
 |   used_class_names.clear(); | 
 |   std::vector<Member *> members; | 
 |   members.insert(members.end(), fields.begin(), fields.end()); | 
 |   members.insert(members.end(), methods.begin(), methods.end()); | 
 |   ExtractClassNames(); | 
 |   for (auto *member : members) { | 
 |     size_t idx = 0; | 
 |     devtools_ijar::ExtractClassNames(member->descriptor->Display(), &idx); | 
 |     member->ExtractClassNames(); | 
 |   } | 
 |  | 
 |   // We have to write the body out before the header in order to reference | 
 |   // the essential constants and populate the output constant pool: | 
 |   u1 *body = new u1[length]; | 
 |   u1 *q = body; | 
 |   WriteBody(q); // advances q | 
 |   u4 body_length = q - body; | 
 |  | 
 |   WriteHeader(p); // advances p | 
 |   put_n(p, body, body_length); | 
 |   delete[] body; | 
 | } | 
 |  | 
 | bool StripClass(u1 *&classdata_out, const u1 *classdata_in, size_t in_length) { | 
 |   ClassFile *clazz = ReadClass(classdata_in, in_length); | 
 |   bool keep = true; | 
 |   if (clazz == NULL) { | 
 |     // Class is invalid. Simply copy it to the output and call it a day. | 
 |     put_n(classdata_out, classdata_in, in_length); | 
 |   } else if (clazz->IsLocalOrAnonymous()) { | 
 |     keep = false; | 
 |   } else { | 
 |  | 
 |     // Constant pool item zero is a dummy entry.  Setting it marks the | 
 |     // beginning of the output phase; calls to Constant::slot() will | 
 |     // fail if called prior to this. | 
 |     const_pool_out.push_back(NULL); | 
 |     clazz->WriteClass(classdata_out); | 
 |  | 
 |     delete clazz; | 
 |   } | 
 |  | 
 |   // Now clean up all the mess we left behind. | 
 |  | 
 |   for (size_t i = 0; i < const_pool_in.size(); i++) { | 
 |     delete const_pool_in[i]; | 
 |   } | 
 |  | 
 |   const_pool_in.clear(); | 
 |   const_pool_out.clear(); | 
 |   return keep; | 
 | } | 
 |  | 
 | }  // namespace devtools_ijar |