| 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
|
|
|