Index: third_party/ijar/classfile.cc |
diff --git a/third_party/ijar/classfile.cc b/third_party/ijar/classfile.cc |
index 39d24f9dd49686e412ae384f61550b949be6b7b4..9d48429e0cd68aaf08cb25d2f926498bc234e094 100644 |
--- a/third_party/ijar/classfile.cc |
+++ b/third_party/ijar/classfile.cc |
@@ -1,6 +1,4 @@ |
-// Copyright 2001,2007 Alan Donovan. All rights reserved. |
-// |
-// Author: Alan Donovan <adonovan@google.com> |
+// 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. |
@@ -34,11 +32,25 @@ |
#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. |
@@ -60,17 +72,19 @@ enum CONSTANT { |
}; |
// 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_VOLATILE = 0x0040, |
- ACC_TRANSIENT = 0x0080, |
- ACC_INTERFACE = 0x0200, |
- ACC_ABSTRACT = 0x0400 |
+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. |
@@ -99,6 +113,8 @@ 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 |
@@ -138,6 +154,10 @@ struct Constant { |
// 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() { |
@@ -163,6 +183,13 @@ struct Constant { |
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 |
{ |
@@ -335,7 +362,7 @@ struct Constant_MethodHandle : Constant |
} |
std::string Display() { |
- return "Constant_MethodHandle::" + std::to_string(reference_kind_) + "::" |
+ return "Constant_MethodHandle::" + ToString(reference_kind_) + "::" |
+ constant(reference_index_)->Display(); |
} |
@@ -378,7 +405,7 @@ struct Constant_InvokeDynamic : Constant |
std::string Display() { |
return "Constant_InvokeDynamic::" |
- + std::to_string(bootstrap_method_attr_index_) + "::" |
+ + ToString(bootstrap_method_attr_index_) + "::" |
+ constant(name_and_type_index_)->Display(); |
} |
@@ -397,6 +424,7 @@ 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()); |
@@ -464,10 +492,51 @@ struct InnerClassesAttribute : Attribute { |
} |
void Write(u1 *&p) { |
- WriteProlog(p, 2 + entries_.size() * 8); |
- put_u2be(p, entries_.size()); |
- for (size_t ii = 0; ii < entries_.size(); ++ii) { |
- Entry *entry = entries_[ii]; |
+ 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()); |
@@ -516,6 +585,7 @@ struct EnclosingMethodAttribute : Attribute { |
struct ElementValue { |
virtual ~ElementValue() {} |
virtual void Write(u1 *&p) = 0; |
+ virtual void ExtractClassNames() {} |
static ElementValue* Read(const u1 *&p); |
u1 tag_; |
u4 length_; |
@@ -555,6 +625,12 @@ struct ClassTypeElementValue : ElementValue { |
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)); |
@@ -565,16 +641,22 @@ struct ClassTypeElementValue : ElementValue { |
struct ArrayTypeElementValue : ElementValue { |
virtual ~ArrayTypeElementValue() { |
- for (size_t i = 0; i < values_.size(); i++) { |
- delete values_[i]; |
+ 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 (size_t ii = 0; ii < values_.size(); ++ii) { |
- values_[ii]->Write(p); |
+ for (auto *value : values_) { |
+ value->Write(p); |
} |
} |
static ArrayTypeElementValue *Read(const u1 *&p) { |
@@ -597,6 +679,12 @@ struct Annotation { |
} |
} |
+ 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()); |
@@ -662,6 +750,10 @@ struct TypeAnnotation { |
delete annotation_; |
} |
+ void ExtractClassNames() { |
+ annotation_->ExtractClassNames(); |
+ } |
+ |
void Write(u1 *&p) { |
put_u1(p, target_type_); |
target_info_->Write(p); |
@@ -873,6 +965,10 @@ struct AnnotationDefaultAttribute : Attribute { |
default_value_->Write(p); |
} |
+ virtual void ExtractClassNames() { |
+ default_value_->ExtractClassNames(); |
+ } |
+ |
ElementValue *default_value_; |
}; |
@@ -913,6 +1009,11 @@ struct SignatureAttribute : Attribute { |
put_u2be(p, signature_->slot()); |
} |
+ virtual void ExtractClassNames() { |
+ size_t signature_idx = 0; |
+ devtools_ijar::ExtractClassNames(signature_->Display(), &signature_idx); |
+ } |
+ |
Constant *signature_; |
}; |
@@ -954,12 +1055,18 @@ struct AnnotationsAttribute : Attribute { |
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 (size_t ii = 0; ii < annotations_.size(); ++ii) { |
- annotations_[ii]->Write(p); |
+ for (auto *annotation : annotations_) { |
+ annotation->Write(p); |
} |
put_u4be(payload_start, p - 4 - payload_start); // backpatch length |
} |
@@ -990,6 +1097,15 @@ struct ParameterAnnotationsAttribute : Attribute { |
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; |
@@ -1022,6 +1138,12 @@ struct TypeAnnotationsAttribute : Attribute { |
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; |
@@ -1035,6 +1157,41 @@ struct TypeAnnotationsAttribute : Attribute { |
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) { |
@@ -1068,8 +1225,14 @@ struct HasAttrs { |
void ReadAttrs(const u1 *&p); |
virtual ~HasAttrs() { |
- for (size_t i = 0; i < attributes.size(); i++) { |
- delete attributes[i]; |
+ for (const auto *attribute : attributes) { |
+ delete attribute; |
+ } |
+ } |
+ |
+ void ExtractClassNames() { |
+ for (auto *attribute : attributes) { |
+ attribute->ExtractClassNames(); |
} |
} |
}; |
@@ -1132,7 +1295,7 @@ struct ClassFile : HasAttrs { |
bool ReadConstantPool(const u1 *&p); |
- void StripIfAnonymous(); |
+ bool IsLocalOrAnonymous(); |
void WriteHeader(u1 *&p) { |
put_u4be(p, magic); |
@@ -1163,6 +1326,23 @@ struct ClassFile : HasAttrs { |
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); |
} |
@@ -1176,12 +1356,14 @@ void HasAttrs::ReadAttrs(const u1 *&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 == "BootstrapMethods" || |
+ attr_name == "SourceDebugExtension") { |
p += attribute_length; // drop these attributes |
} else if (attr_name == "Exceptions") { |
attributes.push_back(ExceptionsAttribute::Read(p, attribute_name)); |
@@ -1214,10 +1396,11 @@ void HasAttrs::ReadAttrs(const u1 *&p) { |
attribute_length)); |
} else if (attr_name == "RuntimeVisibleTypeAnnotations" || |
attr_name == "RuntimeInvisibleTypeAnnotations") { |
- // JSR 308: annotations on types. JDK 7 has no use for these yet, but the |
- // Checkers Framework relies on them. |
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. |
@@ -1229,10 +1412,19 @@ void HasAttrs::ReadAttrs(const u1 *&p) { |
} |
void HasAttrs::WriteAttrs(u1 *&p) { |
- put_u2be(p, attributes.size()); |
+ 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. |
@@ -1335,53 +1527,15 @@ bool ClassFile::ReadConstantPool(const u1 *&p) { |
return true; |
} |
-// Anonymous inner classes are stripped to opaque classes that only extend |
-// Object. None of their methods or fields are accessible anyway. |
-void ClassFile::StripIfAnonymous() { |
- int enclosing_index = -1; |
- int inner_classes_index = -1; |
- |
- for (size_t ii = 0; ii < attributes.size(); ++ii) { |
- if (attributes[ii]->attribute_name_->Display() == "EnclosingMethod") { |
- enclosing_index = ii; |
- } else if (attributes[ii]->attribute_name_->Display() == "InnerClasses") { |
- inner_classes_index = ii; |
- } |
- } |
- |
- // Presence of an EnclosingMethod attribute indicates a local or anonymous |
- // class, which can be stripped. |
- if (enclosing_index > -1) { |
- // Clear the signature to only extend java.lang.Object. |
- super_class = NULL; |
- interfaces.clear(); |
- |
- // Clear away all fields (implementation details). |
- for (size_t ii = 0; ii < fields.size(); ++ii) { |
- delete fields[ii]; |
- } |
- fields.clear(); |
- |
- // Clear away all methods (implementation details). |
- for (size_t ii = 0; ii < methods.size(); ++ii) { |
- delete methods[ii]; |
- } |
- methods.clear(); |
- |
- // Only preserve the InnerClasses attribute to comply with the spec. |
- Attribute *attr = NULL; |
- for (size_t ii = 0; ii < attributes.size(); ++ii) { |
- if (static_cast<int>(ii) != inner_classes_index) { |
- delete attributes[ii]; |
- } else { |
- attr = attributes[ii]; |
- } |
- } |
- attributes.clear(); |
- if (attr != NULL) { |
- attributes.push_back(attr); |
+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) { |
@@ -1406,6 +1560,8 @@ static ClassFile *ReadClass(const void *classdata, size_t length) { |
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); |
@@ -1418,9 +1574,11 @@ static ClassFile *ReadClass(const void *classdata, size_t length) { |
for (int ii = 0; ii < fields_count; ++ii) { |
Member *field = Member::Read(p); |
- if (!(field->access_flags & ACC_PRIVATE)) { // drop private fields |
- clazz->fields.push_back(field); |
+ if ((field->access_flags & ACC_PRIVATE) == ACC_PRIVATE) { |
+ // drop private fields |
+ continue; |
} |
+ clazz->fields.push_back(field); |
} |
u2 methods_count = get_u2be(p); |
@@ -1430,18 +1588,188 @@ static ClassFile *ReadClass(const void *classdata, size_t length) { |
// drop class initializers |
if (method->name->Display() == "<clinit>") continue; |
- if (!(method->access_flags & ACC_PRIVATE)) { // drop private methods |
- clazz->methods.push_back(method); |
+ 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); |
- clazz->StripIfAnonymous(); |
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]; |
@@ -1454,23 +1782,20 @@ void ClassFile::WriteClass(u1 *&p) { |
delete[] body; |
} |
- |
-void StripClass(u1 *&classdata_out, const u1 *classdata_in, size_t in_length) { |
+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); |
- |
- // TODO(bazel-team): We should only keep classes in the InnerClass attributes |
- // if they're used in the output. The entries can then be cleaned out of the |
- // constant pool in the normal way. |
- |
clazz->WriteClass(classdata_out); |
delete clazz; |
@@ -1484,6 +1809,7 @@ void StripClass(u1 *&classdata_out, const u1 *classdata_in, size_t in_length) { |
const_pool_in.clear(); |
const_pool_out.clear(); |
+ return keep; |
} |
} // namespace devtools_ijar |