Class clean-up and compute name during image writing.
Name is computed lazily, if this occurs for an image class it will cause
a card mark and for that part of the image to be scanned every GC. By
precomputing the name we avoid the GC overhead, speed up some reflection
operations, save allocation heap footprint at a cost of ~300kb in the
image.
Remove and reorganize reflection native methods to agree with patch to
libcore.
Change-Id: I4b621be4a9d9bb381a647963066c3305ce40745f
diff --git a/src/class_linker_test.cc b/src/class_linker_test.cc
index 0fe52c8..096ff2d 100644
--- a/src/class_linker_test.cc
+++ b/src/class_linker_test.cc
@@ -467,31 +467,31 @@
struct ClassOffsets : public CheckOffsets<Class> {
ClassOffsets() : CheckOffsets<Class>(false, "Ljava/lang/Class;") {
// alphabetical references
+ offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Class, class_loader_), "classLoader"));
+ offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Class, component_type_), "componentType"));
+ offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Class, dex_cache_), "dexCache"));
+ offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Class, direct_methods_), "directMethods"));
+ offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Class, ifields_), "iFields"));
+ offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Class, iftable_), "ifTable"));
offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Class, name_), "name"));
- offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Class, class_loader_), "shadow$_class_loader_"));
- offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Class, component_type_), "shadow$_component_type_"));
- offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Class, dex_cache_), "shadow$_dex_cache_"));
- offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Class, direct_methods_), "shadow$_direct_methods_"));
- offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Class, ifields_), "shadow$_ifields_"));
- offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Class, iftable_), "shadow$_iftable_"));
- offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Class, sfields_), "shadow$_sfields_"));
- offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Class, super_class_), "shadow$_super_class_"));
- offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Class, verify_error_class_), "shadow$_verify_error_class_"));
- offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Class, virtual_methods_), "shadow$_virtual_methods_"));
- offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Class, vtable_), "shadow$_vtable_"));
+ offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Class, sfields_), "sFields"));
+ offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Class, super_class_), "superClass"));
+ offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Class, verify_error_class_), "verifyErrorClass"));
+ offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Class, virtual_methods_), "virtualMethods"));
+ offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Class, vtable_), "vtable"));
// alphabetical 32-bit
+ offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Class, access_flags_), "accessFlags"));
+ offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Class, class_size_), "classSize"));
+ offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Class, clinit_thread_id_), "clinitThreadId"));
offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Class, dex_type_idx_), "dexTypeIndex"));
- offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Class, access_flags_), "shadow$_access_flags_"));
- offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Class, class_size_), "shadow$_class_size_"));
- offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Class, clinit_thread_id_), "shadow$_clinit_thread_id_"));
- offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Class, num_reference_instance_fields_), "shadow$_num_reference_instance_fields_"));
- offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Class, num_reference_static_fields_), "shadow$_num_reference_static_fields_"));
- offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Class, object_size_), "shadow$_object_size_"));
- offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Class, primitive_type_), "shadow$_primitive_type_"));
- offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Class, reference_instance_offsets_), "shadow$_reference_instance_offsets_"));
- offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Class, reference_static_offsets_), "shadow$_reference_static_offsets_"));
- offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Class, status_), "shadow$_status_"));
+ offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Class, num_reference_instance_fields_), "numReferenceInstanceFields"));
+ offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Class, num_reference_static_fields_), "numReferenceStaticFields"));
+ offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Class, object_size_), "objectSize"));
+ offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Class, primitive_type_), "primitiveType"));
+ offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Class, reference_instance_offsets_), "referenceInstanceOffsets"));
+ offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Class, reference_static_offsets_), "referenceStaticOffsets"));
+ offsets.push_back(CheckOffset(OFFSETOF_MEMBER(Class, status_), "status"));
};
};
diff --git a/src/image_writer.cc b/src/image_writer.cc
index ca57f41..293bd96 100644
--- a/src/image_writer.cc
+++ b/src/image_writer.cc
@@ -59,6 +59,7 @@
return false;
}
PruneNonImageClasses();
+ ComputeLazyFieldsForImageClasses();
Heap::CollectGarbage(false);
#ifndef NDEBUG
CheckNonImageClassesRemoved();
@@ -92,6 +93,17 @@
return true;
}
+void ImageWriter::ComputeLazyFieldsForImageClasses() {
+ Runtime* runtime = Runtime::Current();
+ ClassLinker* class_linker = runtime->GetClassLinker();
+ class_linker->VisitClasses(ComputeLazyFieldsForClassesVisitor, NULL);
+}
+
+bool ImageWriter::ComputeLazyFieldsForClassesVisitor(Class* klass, void* arg) {
+ klass->ComputeName();
+ return true;
+}
+
bool ImageWriter::IsImageClass(const Class* klass) {
if (image_classes_ == NULL) {
return true;
diff --git a/src/image_writer.h b/src/image_writer.h
index d834361..1be07bd 100644
--- a/src/image_writer.h
+++ b/src/image_writer.h
@@ -99,6 +99,9 @@
bool IsImageClass(const Class* klass);
void DumpImageClasses();
+ void ComputeLazyFieldsForImageClasses();
+ static bool ComputeLazyFieldsForClassesVisitor(Class* klass, void* arg);
+
void PruneNonImageClasses();
static bool NonImageClassesVisitor(Class* c, void* arg);
diff --git a/src/java_lang_Class.cc b/src/java_lang_Class.cc
index ece98b3..826841b 100644
--- a/src/java_lang_Class.cc
+++ b/src/java_lang_Class.cc
@@ -89,7 +89,7 @@
return result;
}
-bool IsVisibleConstructor(Method* m, bool public_only) {
+static bool IsVisibleConstructor(Method* m, bool public_only) {
if (public_only && !m->IsPublic()) {
return false;
}
@@ -113,7 +113,7 @@
return ToArray(env, "java/lang/reflect/Constructor", constructors);
}
-bool IsVisibleField(Field* f, bool public_only) {
+static bool IsVisibleField(Field* f, bool public_only) {
if (public_only && !f->IsPublic()) {
return false;
}
@@ -146,7 +146,7 @@
return ToArray(env, "java/lang/reflect/Field", fields);
}
-bool IsVisibleMethod(Method* m, bool public_only) {
+static bool IsVisibleMethod(Method* m, bool public_only) {
if (public_only && !m->IsPublic()) {
return false;
}
@@ -181,10 +181,6 @@
return ToArray(env, "java/lang/reflect/Method", methods);
}
-jboolean Class_desiredAssertionStatus(JNIEnv* env, jobject javaThis) {
- return JNI_FALSE;
-}
-
jobject Class_getDex(JNIEnv* env, jobject javaClass) {
Class* c = Decode<Class*>(env, javaClass);
@@ -196,22 +192,7 @@
return Runtime::Current()->GetClassLinker()->FindDexFile(dex_cache).GetDexObject(env);
}
-jint Class_getNonInnerClassModifiers(JNIEnv* env, jclass javaClass) {
- Class* c = Decode<Class*>(env, javaClass);
- return c->GetAccessFlags() & kAccJavaFlagsMask;
-}
-
-jobject Class_getClassLoaderNative(JNIEnv* env, jclass javaClass) {
- Class* c = Decode<Class*>(env, javaClass);
- Object* result = c->GetClassLoader();
- return AddLocalReference<jobject>(env, result);
-}
-
-jclass Class_getComponentType(JNIEnv* env, jclass javaClass) {
- return AddLocalReference<jclass>(env, Decode<Class*>(env, javaClass)->GetComponentType());
-}
-
-bool MethodMatches(MethodHelper* mh, const std::string& name, ObjectArray<Class>* arg_array) {
+static bool MethodMatches(MethodHelper* mh, const std::string& name, ObjectArray<Class>* arg_array) {
if (name != mh->GetName()) {
return false;
}
@@ -231,7 +212,7 @@
return true;
}
-Method* FindConstructorOrMethodInArray(ObjectArray<Method>* methods, const std::string& name,
+static Method* FindConstructorOrMethodInArray(ObjectArray<Method>* methods, const std::string& name,
ObjectArray<Class>* arg_array) {
if (methods == NULL) {
return NULL;
@@ -300,46 +281,9 @@
return NULL;
}
-/*
- * private native String getNameNative()
- *
- * Return the class' name. The exact format is bizarre, but it's the specified
- * behavior: keywords for primitive types, regular "[I" form for primitive
- * arrays (so "int" but "[I"), and arrays of reference types written
- * between "L" and ";" but with dots rather than slashes (so "java.lang.String"
- * but "[Ljava.lang.String;"). Madness.
- */
jstring Class_getNameNative(JNIEnv* env, jobject javaThis) {
Class* c = Decode<Class*>(env, javaThis);
- std::string descriptor(ClassHelper(c).GetDescriptor());
- if ((descriptor[0] != 'L') && (descriptor[0] != '[')) {
- // The descriptor indicates that this is the class for
- // a primitive type; special-case the return value.
- const char* name = NULL;
- switch (descriptor[0]) {
- case 'Z': name = "boolean"; break;
- case 'B': name = "byte"; break;
- case 'C': name = "char"; break;
- case 'S': name = "short"; break;
- case 'I': name = "int"; break;
- case 'J': name = "long"; break;
- case 'F': name = "float"; break;
- case 'D': name = "double"; break;
- case 'V': name = "void"; break;
- default:
- LOG(FATAL) << "Unknown primitive type: " << PrintableChar(descriptor[0]);
- }
- return env->NewStringUTF(name);
- }
-
- // Convert the UTF-8 name to a java.lang.String. The
- // name must use '.' to separate package components.
- if (descriptor.size() > 2 && descriptor[0] == 'L' && descriptor[descriptor.size() - 1] == ';') {
- descriptor.erase(0, 1);
- descriptor.erase(descriptor.size() - 1);
- }
- std::replace(descriptor.begin(), descriptor.end(), '/', '.');
- return env->NewStringUTF(descriptor.c_str());
+ return AddLocalReference<jstring>(env, c->ComputeName());
}
jboolean Class_isAssignableFrom(JNIEnv* env, jobject javaLhs, jclass javaRhs) {
@@ -362,18 +306,8 @@
return o->InstanceOf(c) ? JNI_TRUE : JNI_FALSE;
}
-jboolean Class_isInterface(JNIEnv* env, jobject javaThis) {
- Class* c = Decode<Class*>(env, javaThis);
- return c->IsInterface();
-}
-
-jboolean Class_isPrimitive(JNIEnv* env, jobject javaThis) {
- Class* c = Decode<Class*>(env, javaThis);
- return c->IsPrimitive();
-}
-
// Validate method/field access.
-bool CheckMemberAccess(const Class* access_from, Class* access_to, uint32_t member_flags) {
+static bool CheckMemberAccess(const Class* access_from, Class* access_to, uint32_t member_flags) {
// quick accept for public access */
if (member_flags & kAccPublic) {
return true;
@@ -466,22 +400,16 @@
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(Class, classForName, "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;"),
- NATIVE_METHOD(Class, desiredAssertionStatus, "()Z"),
NATIVE_METHOD(Class, getAnnotationDirectoryOffset, "()I"),
- NATIVE_METHOD(Class, getClassLoaderNative, "()Ljava/lang/ClassLoader;"),
- NATIVE_METHOD(Class, getComponentType, "()Ljava/lang/Class;"),
NATIVE_METHOD(Class, getDeclaredConstructorOrMethod, "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Member;"),
NATIVE_METHOD(Class, getDeclaredConstructors, "(Z)[Ljava/lang/reflect/Constructor;"),
NATIVE_METHOD(Class, getDeclaredFieldNative, "(Ljava/lang/String;)Ljava/lang/reflect/Field;"),
NATIVE_METHOD(Class, getDeclaredFields, "(Z)[Ljava/lang/reflect/Field;"),
NATIVE_METHOD(Class, getDeclaredMethods, "(Z)[Ljava/lang/reflect/Method;"),
NATIVE_METHOD(Class, getDex, "()Lcom/android/dex/Dex;"),
- NATIVE_METHOD(Class, getNonInnerClassModifiers, "()I"),
NATIVE_METHOD(Class, getNameNative, "()Ljava/lang/String;"),
NATIVE_METHOD(Class, isAssignableFrom, "(Ljava/lang/Class;)Z"),
NATIVE_METHOD(Class, isInstance, "(Ljava/lang/Object;)Z"),
- NATIVE_METHOD(Class, isInterface, "()Z"),
- NATIVE_METHOD(Class, isPrimitive, "()Z"),
NATIVE_METHOD(Class, newInstanceImpl, "()Ljava/lang/Object;"),
};
diff --git a/src/java_lang_reflect_Method.cc b/src/java_lang_reflect_Method.cc
index bfdcd39..10f5779 100644
--- a/src/java_lang_reflect_Method.cc
+++ b/src/java_lang_reflect_Method.cc
@@ -48,16 +48,9 @@
return AddLocalReference<jobject>(env, declared_exceptions->Clone());
}
-jobject Method_getReturnTypeNative(JNIEnv* env, jobject javaMethod) {
- Method* m = Decode<Object*>(env, javaMethod)->AsMethod();
- MethodHelper mh(m);
- return AddLocalReference<jobject>(env, mh.GetReturnType());
-}
-
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(Method, invoke, "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"),
NATIVE_METHOD(Method, getExceptionTypesNative, "()[Ljava/lang/Class;"),
- NATIVE_METHOD(Method, getReturnTypeNative, "()Ljava/lang/Class;")
};
} // namespace
diff --git a/src/object.cc b/src/object.cc
index 145f7ef..32521f8 100644
--- a/src/object.cc
+++ b/src/object.cc
@@ -608,6 +608,48 @@
SetField32(OFFSET_OF_OBJECT_MEMBER(Class, class_size_), new_class_size, false);
}
+// Return the class' name. The exact format is bizarre, but it's the specified behavior for
+// Class.getName: keywords for primitive types, regular "[I" form for primitive arrays (so "int"
+// but "[I"), and arrays of reference types written between "L" and ";" but with dots rather than
+// slashes (so "java.lang.String" but "[Ljava.lang.String;"). Madness.
+String* Class::ComputeName() {
+ String* name = GetName();
+ if (name != NULL) {
+ return name;
+ }
+ std::string descriptor(ClassHelper(this).GetDescriptor());
+ if ((descriptor[0] != 'L') && (descriptor[0] != '[')) {
+ // The descriptor indicates that this is the class for
+ // a primitive type; special-case the return value.
+ const char* c_name = NULL;
+ switch (descriptor[0]) {
+ case 'Z': c_name = "boolean"; break;
+ case 'B': c_name = "byte"; break;
+ case 'C': c_name = "char"; break;
+ case 'S': c_name = "short"; break;
+ case 'I': c_name = "int"; break;
+ case 'J': c_name = "long"; break;
+ case 'F': c_name = "float"; break;
+ case 'D': c_name = "double"; break;
+ case 'V': c_name = "void"; break;
+ default:
+ LOG(FATAL) << "Unknown primitive type: " << PrintableChar(descriptor[0]);
+ }
+ name = String::AllocFromModifiedUtf8(c_name);
+ } else {
+ // Convert the UTF-8 name to a java.lang.String. The name must use '.' to separate package
+ // components.
+ if (descriptor.size() > 2 && descriptor[0] == 'L' && descriptor[descriptor.size() - 1] == ';') {
+ descriptor.erase(0, 1);
+ descriptor.erase(descriptor.size() - 1);
+ }
+ std::replace(descriptor.begin(), descriptor.end(), '/', '.');
+ name = String::AllocFromModifiedUtf8(descriptor.c_str());
+ }
+ SetName(name);
+ return name;
+}
+
void Class::DumpClass(std::ostream& os, int flags) const {
if ((flags & kDumpClassFullDetail) == 0) {
os << PrettyClass(this);
diff --git a/src/object.h b/src/object.h
index 58c36be..3cbe9d6 100644
--- a/src/object.h
+++ b/src/object.h
@@ -1059,14 +1059,13 @@
//
// kStatusIdx: LoadClass populates with Class with information from
// the DexFile, moving the status to kStatusIdx, indicating that the
- // Class values in super_class_ and interfaces_ have not been
- // populated based on super_class_type_idx_ and
- // interfaces_type_idx_. The new Class can then be inserted into the
- // classes table.
+ // Class value in super_class_ has not been populated. The new Class
+ // can then be inserted into the classes table.
//
// kStatusLoaded: After taking a lock on Class, the ClassLinker will
// attempt to move a kStatusIdx class forward to kStatusLoaded by
- // using ResolveClass to initialize the super_class_ and interfaces_.
+ // using ResolveClass to initialize the super_class_ and ensuring the
+ // interfaces are resolved.
//
// kStatusResolved: Still holding the lock on Class, the ClassLinker
// shows linking is complete and fields of the Class populated by making
@@ -1193,8 +1192,10 @@
return (GetAccessFlags() & kAccClassIsPhantomReference) != 0;
}
- String* GetName() const;
- void SetName(String* name);
+
+ String* GetName() const ; // Returns the cached name
+ void SetName(String* name); // Sets the cached name
+ String* ComputeName(); // Computes the name, then sets the cached value
bool IsProxyClass() const {
// Read access flags without using getter as whether something is a proxy can be check in
@@ -1724,9 +1725,6 @@
bool IsArrayAssignableFromArray(const Class* klass) const;
bool IsAssignableFromArray(const Class* klass) const;
- // descriptor for the class such as "java.lang.Class" or "[C"
- String* name_; // TODO initialize
-
// defining class loader, or NULL for the "bootstrap" system loader
ClassLoader* class_loader_;
@@ -1768,12 +1766,13 @@
// of the concrete vtable_ methods for the methods in the interface.
ObjectArray<InterfaceEntry>* iftable_;
+ // descriptor for the class such as "java.lang.Class" or "[C". Lazily initialized by ComputeName
+ String* name_;
+
// Static fields
ObjectArray<Field>* sfields_;
- // The superclass, or NULL if this is java.lang.Object or a
- // primitive type.
- // see also super_class_type_idx_;
+ // The superclass, or NULL if this is java.lang.Object, an interface or primitive type.
Class* super_class_;
// If class verify fails, we must return same error on subsequent tries.
@@ -1788,10 +1787,6 @@
// virtual_ methods_ for miranda methods.
ObjectArray<Method>* vtable_;
- // type index from dex file
- // TODO: really 16bits
- uint32_t dex_type_idx_;
-
// access flags; low 16 bits are defined by VM spec
uint32_t access_flags_;
@@ -1802,6 +1797,10 @@
// tid used to check for recursive <clinit> invocation
pid_t clinit_thread_id_;
+ // type index from dex file
+ // TODO: really 16bits
+ uint32_t dex_type_idx_;
+
// number of instance fields that are object refs
size_t num_reference_instance_fields_;
@@ -1813,7 +1812,7 @@
// See also class_size_.
size_t object_size_;
- // primitive type index, or Primitive::kPrimNot (0); set for generated prim classes
+ // primitive type value, or Primitive::kPrimNot (0); set for generated prim classes
Primitive::Type primitive_type_;
// Bitmap of offsets of ifields.