diff options
author | Han-Wen Nienhuys <hanwen@google.com> | 2015-02-25 16:45:20 +0100 |
---|---|---|
committer | Han-Wen Nienhuys <hanwen@google.com> | 2015-02-25 16:45:20 +0100 |
commit | d08b27fa9701fecfdb69e1b0d1ac2459efc2129b (patch) | |
tree | 5d50963026239ca5aebfb47ea5b8db7e814e57c8 /third_party/ijar/classfile.cc |
Update from Google.
--
MOE_MIGRATED_REVID=85702957
Diffstat (limited to 'third_party/ijar/classfile.cc')
-rw-r--r-- | third_party/ijar/classfile.cc | 1486 |
1 files changed, 1486 insertions, 0 deletions
diff --git a/third_party/ijar/classfile.cc b/third_party/ijar/classfile.cc new file mode 100644 index 0000000000..90749ff78b --- /dev/null +++ b/third_party/ijar/classfile.cc @@ -0,0 +1,1486 @@ +// 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_LIMIT_MACROS 1 +#include <inttypes.h> // for PRIx32 +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#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 + +// 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 || 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() {} + + // 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_; +}; + +// 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; + + 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 (int 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 (int 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) { + WriteProlog(p, 2 + entries_.size() * 8); + put_u2be(p, entries_.size()); + for (int ii = 0; ii < entries_.size(); ++ii) { + Entry *entry = entries_[ii]; + 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; + 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()); + } + 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 (int i = 0; i < values_.size(); i++) { + delete values_[i]; + } + } + + void Write(u1 *&p) { + put_u1(p, tag_); + put_u2be(p, values_.size()); + for (int 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 (int i = 0; i < element_value_pairs_.size(); i++) { + delete element_value_pairs_[i]->element_value_; + delete element_value_pairs_[i]; + } + } + + void Write(u1 *&p) { + put_u2be(p, type_->slot()); + put_u2be(p, element_value_pairs_.size()); + for (int 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 Write(u1 *&p) { + put_u1(p, target_type_); + target_info_->Write(p); + type_path_->Write(p); + annotation_->Write(p); + } + + static TypeAnnotation *Read(const u1 *&p) { + TypeAnnotation *value = new TypeAnnotation; + value->target_type_ = get_u1(p); + value->target_info_ = ReadTargetInfo(p, value->target_type_); + value->type_path_ = TypePath::Read(p); + value->annotation_ = Annotation::Read(p); + return value; + } + + struct TargetInfo { + virtual ~TargetInfo() {} + virtual void Write(u1 *&p) = 0; + }; + + struct TypeParameterTargetInfo : TargetInfo { + void Write(u1 *&p) { + put_u1(p, type_parameter_index_); + } + static TypeParameterTargetInfo *Read(const u1 *&p) { + TypeParameterTargetInfo *value = new TypeParameterTargetInfo; + value->type_parameter_index_ = get_u1(p); + return value; + } + u1 type_parameter_index_; + }; + + struct ClassExtendsInfo : TargetInfo { + void Write(u1 *&p) { + put_u2be(p, supertype_index_); + } + static ClassExtendsInfo *Read(const u1 *&p) { + ClassExtendsInfo *value = new ClassExtendsInfo; + value->supertype_index_ = get_u2be(p); + return value; + } + u2 supertype_index_; + }; + + struct TypeParameterBoundInfo : TargetInfo { + void Write(u1 *&p) { + put_u1(p, type_parameter_index_); + put_u1(p, bound_index_); + } + static TypeParameterBoundInfo *Read(const u1 *&p) { + TypeParameterBoundInfo *value = new TypeParameterBoundInfo; + value->type_parameter_index_ = get_u1(p); + value->bound_index_ = get_u1(p); + return value; + } + u1 type_parameter_index_; + u1 bound_index_; + }; + + struct EmptyInfo : TargetInfo { + void Write(u1 *&p) {} + static EmptyInfo *Read(const u1 *&p) { + return new EmptyInfo; + } + }; + + struct MethodFormalParameterInfo : TargetInfo { + void Write(u1 *&p) { + put_u1(p, method_formal_parameter_index_); + } + static MethodFormalParameterInfo *Read(const u1 *&p) { + MethodFormalParameterInfo *value = new MethodFormalParameterInfo; + value->method_formal_parameter_index_ = get_u1(p); + return value; + } + u1 method_formal_parameter_index_; + }; + + struct ThrowsTypeInfo : TargetInfo { + void Write(u1 *&p) { + put_u2be(p, throws_type_index_); + } + static ThrowsTypeInfo *Read(const u1 *&p) { + ThrowsTypeInfo *value = new ThrowsTypeInfo; + value->throws_type_index_ = get_u2be(p); + return value; + } + u2 throws_type_index_; + }; + + static TargetInfo *ReadTargetInfo(const u1 *&p, u1 target_type) { + switch (target_type) { + case CLASS_TYPE_PARAMETER: + case METHOD_TYPE_PARAMETER: + return TypeParameterTargetInfo::Read(p); + case CLASS_EXTENDS: + return ClassExtendsInfo::Read(p); + case CLASS_TYPE_PARAMETER_BOUND: + case METHOD_TYPE_PARAMETER_BOUND: + return TypeParameterBoundInfo::Read(p); + case FIELD: + case METHOD_RETURN: + case METHOD_RECEIVER: + return new EmptyInfo; + case METHOD_FORMAL_PARAMETER: + return MethodFormalParameterInfo::Read(p); + case THROWS: + return ThrowsTypeInfo::Read(p); + default: + fprintf(stderr, "Illegal type annotation target type: %d\n", + target_type); + abort(); + } + } + + struct TypePath { + void Write(u1 *&p) { + put_u1(p, path_.size()); + for (TypePathEntry entry : path_) { + put_u1(p, entry.type_path_kind_); + put_u1(p, entry.type_argument_index_); + } + } + static TypePath *Read(const u1 *&p) { + TypePath *value = new TypePath; + u1 path_length = get_u1(p); + for (int ii = 0; ii < path_length; ++ii) { + TypePathEntry entry; + entry.type_path_kind_ = get_u1(p); + entry.type_argument_index_ = get_u1(p); + value->path_.push_back(entry); + } + return value; + } + + struct TypePathEntry { + u1 type_path_kind_; + u1 type_argument_index_; + }; + std::vector<TypePathEntry> path_; + }; + + u1 target_type_; + TargetInfo *target_info_; + TypePath *type_path_; + Annotation *annotation_; +}; + +struct AnnotationTypeElementValue : ElementValue { + virtual ~AnnotationTypeElementValue() { + delete annotation_; + } + + void Write(u1 *&p) { + put_u1(p, tag_); + annotation_->Write(p); + } + static AnnotationTypeElementValue *Read(const u1 *&p) { + AnnotationTypeElementValue *value = new AnnotationTypeElementValue; + value->annotation_ = Annotation::Read(p); + return value; + } + + Annotation *annotation_; +}; + +ElementValue* ElementValue::Read(const u1 *&p) { + const u1* start = p; + ElementValue *result; + u1 tag = get_u1(p); + if (tag != 0 && strchr("BCDFIJSZs", (char) tag) != NULL) { + result = BaseTypeElementValue::Read(p); + } else if ((char) tag == 'e') { + result = EnumTypeElementValue::Read(p); + } else if ((char) tag == 'c') { + result = ClassTypeElementValue::Read(p); + } else if ((char) tag == '[') { + result = ArrayTypeElementValue::Read(p); + } else if ((char) tag == '@') { + result = AnnotationTypeElementValue::Read(p); + } else { + fprintf(stderr, "Illegal element_value::tag: %d\n", tag); + abort(); + } + result->tag_ = tag; + result->length_ = p - start; + return result; +} + +// See sec.4.7.20 of JVM spec. +// We preserve AnnotationDefault attributes because they are required +// in order to make use of an annotation in new code. +struct AnnotationDefaultAttribute : Attribute { + virtual ~AnnotationDefaultAttribute() { + delete default_value_; + } + + static AnnotationDefaultAttribute* Read(const u1 *&p, + Constant *attribute_name) { + AnnotationDefaultAttribute *attr = new AnnotationDefaultAttribute; + attr->attribute_name_ = attribute_name; + attr->default_value_ = ElementValue::Read(p); + return attr; + } + + void Write(u1 *&p) { + WriteProlog(p, default_value_->length_); + default_value_->Write(p); + } + + 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()); + } + + Constant *signature_; +}; + +// See sec.4.7.15 of JVM spec. +// We preserve Deprecated attributes because they are required by the +// compiler to generate warning messages. +struct DeprecatedAttribute : Attribute { + + static DeprecatedAttribute* Read(const u1 *&p, Constant *attribute_name) { + DeprecatedAttribute *attr = new DeprecatedAttribute; + attr->attribute_name_ = attribute_name; + return attr; + } + + void Write(u1 *&p) { + WriteProlog(p, 0); + } +}; + +// See sec.4.7.16-17 of JVM spec v3. Includes RuntimeVisible and +// RuntimeInvisible. +// +// We preserve all annotations. +struct AnnotationsAttribute : Attribute { + virtual ~AnnotationsAttribute() { + for (int 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; + } + + void Write(u1 *&p) { + WriteProlog(p, -1); + u1 *payload_start = p - 4; + put_u2be(p, annotations_.size()); + for (int 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; + } + + void Write(u1 *&p) { + WriteProlog(p, -1); + u1 *payload_start = p - 4; + put_u1(p, parameter_annotations_.size()); + for (int ii = 0; ii < parameter_annotations_.size(); ++ii) { + std::vector<Annotation*> &annotations = parameter_annotations_[ii]; + put_u2be(p, annotations.size()); + for (int jj = 0; jj < annotations.size(); ++jj) { + annotations[jj]->Write(p); + } + } + put_u4be(payload_start, p - 4 - payload_start); // backpatch length + } + + std::vector<std::vector<Annotation*> > parameter_annotations_; +}; + +// See sec.4.7.20 of Java 8 JVM spec. Includes RuntimeVisibleTypeAnnotations +// and RuntimeInvisibleTypeAnnotations. +struct TypeAnnotationsAttribute : Attribute { + static TypeAnnotationsAttribute* Read(const u1 *&p, Constant *attribute_name, + u4 attribute_length) { + auto attr = new TypeAnnotationsAttribute; + attr->attribute_name_ = attribute_name; + u2 num_annotations = get_u2be(p); + for (int ii = 0; ii < num_annotations; ++ii) { + TypeAnnotation *annotation = TypeAnnotation::Read(p); + attr->type_annotations_.push_back(annotation); + } + return attr; + } + + 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 (int i = 0; i < attributes.size(); i++) { + delete attributes[i]; + } + } +}; + +// 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 (int i = 0; i < fields.size(); i++) { + delete fields[i]; + } + + for (int 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 (int ii = 0; ii < interfaces.size(); ++ii) { + put_u2be(p, interfaces[ii]->slot()); + } + put_u2be(p, fields.size()); + for (int ii = 0; ii < fields.size(); ++ii) { + fields[ii]->Write(p); + } + put_u2be(p, methods.size()); + for (int ii = 0; ii < methods.size(); ++ii) { + methods[ii]->Write(p); + } + 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") { + // 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) { + put_u2be(p, attributes.size()); + for (int ii = 0; ii < attributes.size(); ii++) { + attributes[ii]->Write(p); + } +} + +// 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 (int 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 (int ii = 0; ii < fields.size(); ++ii) { + delete fields[ii]; + } + fields.clear(); + + // Clear away all methods (implementation details). + for (int 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 (int ii = 0; ii < attributes.size(); ++ii) { + if (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)); + 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; +} + +void ClassFile::WriteClass(u1 *&p) { + // 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); + + // TODO(bazel-team): We should only keep classes in the InnerClass attributes + // if they're used in the output. The entries can then be cleaned out of the + // constant pool in the normal way. + + clazz->WriteClass(classdata_out); + + delete clazz; + } + + // Now clean up all the mess we left behind. + + for (int i = 0; i < const_pool_in.size(); i++) { + delete const_pool_in[i]; + } + + const_pool_in.clear(); + const_pool_out.clear(); +} + +} // namespace devtools_ijar |