You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1788 lines
50 KiB
1788 lines
50 KiB
// Copyright 2001,2007 Alan Donovan. All rights reserved. |
|
// |
|
// Author: Alan Donovan <adonovan@google.com> |
|
// |
|
// 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 <string> |
|
#include <vector> |
|
|
|
#include "common.h" |
|
|
|
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_VOLATILE = 0x0040, |
|
ACC_TRANSIENT = 0x0080, |
|
ACC_INTERFACE = 0x0200, |
|
ACC_ABSTRACT = 0x0400 |
|
}; |
|
|
|
// 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 { |
|
|
|
explicit 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 |
|
{ |
|
explicit 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 |
|
{ |
|
explicit 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::" + std::to_string(reference_kind_) + "::" |
|
+ constant(reference_index_)->Display(); |
|
} |
|
|
|
u1 reference_kind_; |
|
u2 reference_index_; |
|
}; |
|
|
|
// See sec.4.4.9 of JVM spec. |
|
struct Constant_MethodType : Constant |
|
{ |
|
explicit 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::" |
|
+ std::to_string(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. |
|
size_t entry_count; |
|
int iteration = 0; |
|
|
|
do { |
|
entry_count = kept_entries.size(); |
|
for (size_t i_entry = 0; i_entry < 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 || |
|
entry->outer_class_info == NULL || |
|
entry->inner_name == NULL) { |
|
kept_entries.insert(i_entry); |
|
|
|
// These are zero for anonymous inner classes |
|
if (entry->outer_class_info != NULL) { |
|
entry->outer_class_info->slot(); |
|
} |
|
|
|
if (entry->inner_name != NULL) { |
|
entry->inner_name->slot(); |
|
} |
|
} |
|
} |
|
iteration += 1; |
|
} while (entry_count != 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 (size_t i = 0; i < values_.size(); i++) { |
|
delete values_[i]; |
|
} |
|
} |
|
|
|
virtual void ExtractClassNames() { |
|
for (size_t i = 0; i < values_.size(); i++) { |
|
values_[i]->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); |
|
} |
|
} |
|
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 *&) {} |
|
static EmptyInfo *Read(const u1 *&) { |
|
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 *&, 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 (size_t i = 0; i < annotations_.size(); i++) { |
|
annotations_[i]->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); |
|
} |
|
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) { |
|
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 (size_t i = 0; i < type_annotations_.size(); i++) { |
|
type_annotations_[i]->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_; |
|
}; |
|
|
|
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 (size_t i = 0; i < attributes.size(); i++) { |
|
delete attributes[i]; |
|
} |
|
} |
|
|
|
void ExtractClassNames() { |
|
for (size_t i = 0; i < attributes.size(); i++) { |
|
attributes[i]->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); |
|
|
|
void StripIfAnonymous(); |
|
|
|
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 == "LineNumberTable" || |
|
attr_name == "LocalVariableTable" || |
|
attr_name == "LocalVariableTypeTable" || |
|
attr_name == "Code" || |
|
attr_name == "Synthetic" || |
|
attr_name == "BootstrapMethods") { |
|
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") { |
|
// 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 { |
|
// 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; |
|
} |
|
|
|
// 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); |
|
} |
|
} |
|
} |
|
|
|
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)) { // drop private fields |
|
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)) { // drop private methods |
|
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 (size_t i = 0; i < members.size(); i++) { |
|
Member *member = members[i]; |
|
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; |
|
} |
|
|
|
|
|
void StripClass(u1 *&classdata_out, const u1 *classdata_in, size_t in_length) { |
|
ClassFile *clazz = ReadClass(classdata_in, in_length); |
|
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 { |
|
|
|
// 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(); |
|
} |
|
|
|
} // namespace devtools_ijar
|
|
|