8f2968f095
Also add -Werror. Change-Id: I2f9fda288d31bd34735856ade33916c93c45922a
1788 lines
50 KiB
C++
1788 lines
50 KiB
C++
// 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 {
|
|
|
|
Constant(u1 tag) :
|
|
slot_(0),
|
|
tag_(tag) {}
|
|
|
|
virtual ~Constant() {}
|
|
|
|
// For UTF-8 string constants, returns the encoded string.
|
|
// Otherwise, returns an undefined string value suitable for debugging.
|
|
virtual std::string Display() = 0;
|
|
|
|
virtual void Write(u1 *&p) = 0;
|
|
|
|
// Called by slot() when a constant has been identified as required
|
|
// in the output classfile's constant pool. This is a hook allowing
|
|
// constants to register their dependency on other constants, by
|
|
// calling slot() on them in turn.
|
|
virtual void Keep() {}
|
|
|
|
bool Kept() {
|
|
return slot_ != 0;
|
|
}
|
|
|
|
// Returns the index of this constant in the output class's constant
|
|
// pool, assigning a slot if not already done.
|
|
u2 slot() {
|
|
if (slot_ == 0) {
|
|
Keep();
|
|
slot_ = const_pool_out.size(); // BugBot's "narrowing" warning
|
|
// is bogus. The number of
|
|
// output constants can't exceed
|
|
// the number of input constants.
|
|
if (slot_ == 0) {
|
|
fprintf(stderr, "Constant::slot() called before output phase.\n");
|
|
abort();
|
|
}
|
|
const_pool_out.push_back(this);
|
|
if (tag_ == CONSTANT_Long || tag_ == CONSTANT_Double) {
|
|
const_pool_out.push_back(NULL);
|
|
}
|
|
}
|
|
return slot_;
|
|
}
|
|
|
|
u2 slot_; // zero => "this constant is unreachable garbage"
|
|
u1 tag_;
|
|
};
|
|
|
|
// Extracts class names from a signature and puts them into the global
|
|
// variable used_class_names.
|
|
//
|
|
// desc: the descriptor class names should be extracted from.
|
|
// p: the position where the extraction should tart.
|
|
void ExtractClassNames(const std::string& desc, size_t* p);
|
|
|
|
// See sec.4.4.1 of JVM spec.
|
|
struct Constant_Class : Constant
|
|
{
|
|
Constant_Class(u2 name_index) :
|
|
Constant(CONSTANT_Class),
|
|
name_index_(name_index) {}
|
|
|
|
void Write(u1 *&p) {
|
|
put_u1(p, tag_);
|
|
put_u2be(p, constant(name_index_)->slot());
|
|
}
|
|
|
|
std::string Display() {
|
|
return constant(name_index_)->Display();
|
|
}
|
|
|
|
void Keep() { constant(name_index_)->slot(); }
|
|
|
|
u2 name_index_;
|
|
};
|
|
|
|
// See sec.4.4.2 of JVM spec.
|
|
struct Constant_FMIref : Constant
|
|
{
|
|
Constant_FMIref(u1 tag,
|
|
u2 class_index,
|
|
u2 name_type_index) :
|
|
Constant(tag),
|
|
class_index_(class_index),
|
|
name_type_index_(name_type_index) {}
|
|
|
|
void Write(u1 *&p) {
|
|
put_u1(p, tag_);
|
|
put_u2be(p, constant(class_index_)->slot());
|
|
put_u2be(p, constant(name_type_index_)->slot());
|
|
}
|
|
|
|
std::string Display() {
|
|
return constant(class_index_)->Display() + "::" +
|
|
constant(name_type_index_)->Display();
|
|
}
|
|
|
|
void Keep() {
|
|
constant(class_index_)->slot();
|
|
constant(name_type_index_)->slot();
|
|
}
|
|
|
|
u2 class_index_;
|
|
u2 name_type_index_;
|
|
};
|
|
|
|
// See sec.4.4.3 of JVM spec.
|
|
struct Constant_String : Constant
|
|
{
|
|
Constant_String(u2 string_index) :
|
|
Constant(CONSTANT_String),
|
|
string_index_(string_index) {}
|
|
|
|
void Write(u1 *&p) {
|
|
put_u1(p, tag_);
|
|
put_u2be(p, constant(string_index_)->slot());
|
|
}
|
|
|
|
std::string Display() {
|
|
return "\"" + constant(string_index_)->Display() + "\"";
|
|
}
|
|
|
|
void Keep() { constant(string_index_)->slot(); }
|
|
|
|
u2 string_index_;
|
|
};
|
|
|
|
// See sec.4.4.4 of JVM spec.
|
|
struct Constant_IntegerOrFloat : Constant
|
|
{
|
|
Constant_IntegerOrFloat(u1 tag, u4 bytes) :
|
|
Constant(tag),
|
|
bytes_(bytes) {}
|
|
|
|
void Write(u1 *&p) {
|
|
put_u1(p, tag_);
|
|
put_u4be(p, bytes_);
|
|
}
|
|
|
|
std::string Display() { return "int/float"; }
|
|
|
|
u4 bytes_;
|
|
};
|
|
|
|
// See sec.4.4.5 of JVM spec.
|
|
struct Constant_LongOrDouble : Constant_IntegerOrFloat
|
|
{
|
|
Constant_LongOrDouble(u1 tag, u4 high_bytes, u4 low_bytes) :
|
|
Constant_IntegerOrFloat(tag, high_bytes),
|
|
low_bytes_(low_bytes) {}
|
|
|
|
void Write(u1 *&p) {
|
|
put_u1(p, tag_);
|
|
put_u4be(p, bytes_);
|
|
put_u4be(p, low_bytes_);
|
|
}
|
|
|
|
std::string Display() { return "long/double"; }
|
|
|
|
u4 low_bytes_;
|
|
};
|
|
|
|
// See sec.4.4.6 of JVM spec.
|
|
struct Constant_NameAndType : Constant
|
|
{
|
|
Constant_NameAndType(u2 name_index, u2 descr_index) :
|
|
Constant(CONSTANT_NameAndType),
|
|
name_index_(name_index),
|
|
descr_index_(descr_index) {}
|
|
|
|
void Write(u1 *&p) {
|
|
put_u1(p, tag_);
|
|
put_u2be(p, constant(name_index_)->slot());
|
|
put_u2be(p, constant(descr_index_)->slot());
|
|
}
|
|
|
|
std::string Display() {
|
|
return constant(name_index_)->Display() + "::" +
|
|
constant(descr_index_)->Display();
|
|
}
|
|
|
|
void Keep() {
|
|
constant(name_index_)->slot();
|
|
constant(descr_index_)->slot();
|
|
}
|
|
|
|
u2 name_index_;
|
|
u2 descr_index_;
|
|
};
|
|
|
|
// See sec.4.4.7 of JVM spec.
|
|
struct Constant_Utf8 : Constant
|
|
{
|
|
Constant_Utf8(u4 length, const u1 *utf8) :
|
|
Constant(CONSTANT_Utf8),
|
|
length_(length),
|
|
utf8_(utf8) {}
|
|
|
|
void Write(u1 *&p) {
|
|
put_u1(p, tag_);
|
|
put_u2be(p, length_);
|
|
put_n(p, utf8_, length_);
|
|
}
|
|
|
|
std::string Display() {
|
|
return std::string((const char*) utf8_, length_);
|
|
}
|
|
|
|
u4 length_;
|
|
const u1 *utf8_;
|
|
};
|
|
|
|
// See sec.4.4.8 of JVM spec.
|
|
struct Constant_MethodHandle : Constant
|
|
{
|
|
Constant_MethodHandle(u1 reference_kind, u2 reference_index) :
|
|
Constant(CONSTANT_MethodHandle),
|
|
reference_kind_(reference_kind),
|
|
reference_index_(reference_index) {}
|
|
|
|
void Write(u1 *&p) {
|
|
put_u1(p, tag_);
|
|
put_u1(p, reference_kind_);
|
|
put_u2be(p, reference_index_);
|
|
}
|
|
|
|
std::string Display() {
|
|
return "Constant_MethodHandle::" + 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
|
|
{
|
|
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
|