Adding JNI code for dalvik.system.DexFile and java.lang.Class
Change-Id: I079c037db77aeaca0dec06660f7551f57adf2607
diff --git a/src/common_test.h b/src/common_test.h
index 844fa00..b56090b 100644
--- a/src/common_test.h
+++ b/src/common_test.h
@@ -241,15 +241,6 @@
return class_loader;
}
- std::string ConvertClassNameToClassDescriptor(const char* class_name) {
- std::string desc;
- desc += "L";
- desc += class_name;
- desc += ";";
- std::replace(desc.begin(), desc.end(), '.', '/');
- return desc;
- }
-
void CompileMethod(Method* method) {
CHECK(method != NULL);
compiler_->CompileOne(method);
@@ -262,7 +253,7 @@
const char* class_name,
const char* method_name,
const char* signature) {
- std::string class_descriptor = ConvertClassNameToClassDescriptor(class_name);
+ std::string class_descriptor = DotToDescriptor(class_name);
Class* klass = class_linker_->FindClass(class_descriptor, class_loader);
CHECK(klass != NULL) << "Class not found " << class_name;
Method* method = klass->FindDirectMethod(method_name, signature);
@@ -275,7 +266,7 @@
const char* class_name,
const char* method_name,
const char* signature) {
- std::string class_descriptor = ConvertClassNameToClassDescriptor(class_name);
+ std::string class_descriptor = DotToDescriptor(class_name);
Class* klass = class_linker_->FindClass(class_descriptor, class_loader);
CHECK(klass != NULL) << "Class not found " << class_name;
Method* method = klass->FindVirtualMethod(method_name, signature);
diff --git a/src/dalvik_system_DexFile.cc b/src/dalvik_system_DexFile.cc
new file mode 100644
index 0000000..3c373d5
--- /dev/null
+++ b/src/dalvik_system_DexFile.cc
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#include "logging.h"
+#include "ScopedUtfChars.h"
+
+#include "JniConstants.h" // Last to avoid problems with LOG redefinition.
+
+namespace art {
+
+namespace {
+
+// A smart pointer that provides read-only access to a Java string's UTF chars.
+// Unlike libcore's NullableScopedUtfChars, this will *not* throw NullPointerException if
+// passed a null jstring. The correct idiom is:
+//
+// NullableScopedUtfChars name(env, javaName);
+// if (env->ExceptionOccurred()) {
+// return NULL;
+// }
+// // ... use name.c_str()
+//
+// TODO: rewrite to get rid of this, or change ScopedUtfChars to offer this option.
+class NullableScopedUtfChars {
+public:
+ NullableScopedUtfChars(JNIEnv* env, jstring s)
+ : mEnv(env), mString(s)
+ {
+ mUtfChars = (s != NULL) ? env->GetStringUTFChars(s, NULL) : NULL;
+ }
+
+ ~NullableScopedUtfChars() {
+ if (mUtfChars) {
+ mEnv->ReleaseStringUTFChars(mString, mUtfChars);
+ }
+ }
+
+ const char* c_str() const {
+ return mUtfChars;
+ }
+
+ size_t size() const {
+ return strlen(mUtfChars);
+ }
+
+ // Element access.
+ const char& operator[](size_t n) const {
+ return mUtfChars[n];
+ }
+
+private:
+ JNIEnv* mEnv;
+ jstring mString;
+ const char* mUtfChars;
+
+ // Disallow copy and assignment.
+ NullableScopedUtfChars(const NullableScopedUtfChars&);
+ void operator=(const NullableScopedUtfChars&);
+};
+
+static jint DexFile_openDexFile(JNIEnv* env, jclass, jstring javaSourceName, jstring javaOutputName) {
+ ScopedUtfChars sourceName(env, javaSourceName);
+ if (sourceName.c_str() == NULL) {
+ return 0;
+ }
+ NullableScopedUtfChars outputName(env, javaOutputName);
+ if (env->ExceptionOccurred()) {
+ return 0;
+ }
+ UNIMPLEMENTED(WARNING) << sourceName.c_str();
+ return 0;
+}
+
+void DexFile_closeDexFile(JNIEnv* env, jclass, jint cookie) {
+ UNIMPLEMENTED(WARNING);
+}
+
+jclass DexFile_defineClass(JNIEnv* env, jclass, jstring javaName, jobject javaLoader, jint cookie) {
+ UNIMPLEMENTED(ERROR);
+ return NULL;
+}
+
+jobjectArray DexFile_getClassNameList(JNIEnv* env, jclass, jint cookie) {
+ UNIMPLEMENTED(ERROR);
+ return NULL;
+}
+
+jboolean DexFile_isDexOptNeeded(JNIEnv* env, jclass, jstring javaFilename) {
+ // TODO: run dex2oat?
+ UNIMPLEMENTED(WARNING);
+ return JNI_FALSE;
+}
+
+static JNINativeMethod gMethods[] = {
+ NATIVE_METHOD(DexFile, closeDexFile, "(I)V"),
+ NATIVE_METHOD(DexFile, defineClass, "(Ljava/lang/String;Ljava/lang/ClassLoader;I)Ljava/lang/Class;"),
+ NATIVE_METHOD(DexFile, getClassNameList, "(I)[Ljava/lang/String;"),
+ NATIVE_METHOD(DexFile, isDexOptNeeded, "(Ljava/lang/String;)Z"),
+ NATIVE_METHOD(DexFile, openDexFile, "(Ljava/lang/String;Ljava/lang/String;I)I"),
+};
+
+} // namespace
+
+void register_dalvik_system_DexFile(JNIEnv* env) {
+ jniRegisterNativeMethods(env, "dalvik/system/DexFile", gMethods, NELEM(gMethods));
+}
+
+} // namespace art
diff --git a/src/java_lang_Class.cc b/src/java_lang_Class.cc
index 8ad59b4..fe5da98 100644
--- a/src/java_lang_Class.cc
+++ b/src/java_lang_Class.cc
@@ -16,7 +16,9 @@
#include "jni_internal.h"
#include "class_linker.h"
+#include "class_loader.h"
#include "object.h"
+#include "ScopedUtfChars.h"
#include "JniConstants.h" // Last to avoid problems with LOG redefinition.
@@ -24,6 +26,33 @@
namespace {
+// "name" is in "binary name" format, e.g. "dalvik.system.Debug$1".
+jclass Class_classForName(JNIEnv* env, jclass, jstring javaName, jboolean initialize, jobject javaLoader) {
+ ScopedUtfChars name(env, javaName);
+ if (name.c_str() == NULL) {
+ return NULL;
+ }
+
+ // We need to validate and convert the name (from x.y.z to x/y/z). This
+ // is especially handy for array types, since we want to avoid
+ // auto-generating bogus array classes.
+ if (!IsValidClassName(name.c_str(), true, true)) {
+ Thread::Current()->ThrowNewException("Ljava/lang/ClassNotFoundException;",
+ "Invalid name: %s", name.c_str());
+ return NULL;
+ }
+
+ std::string descriptor(DotToDescriptor(name.c_str()));
+ Object* loader = Decode<Object*>(env, javaLoader);
+ ClassLoader* class_loader = down_cast<ClassLoader*>(loader);
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ Class* c = class_linker->FindClass(descriptor.c_str(), class_loader);
+ if (initialize) {
+ class_linker->EnsureInitialized(c, true);
+ }
+ return AddLocalReference<jclass>(env, c);
+}
+
jboolean Class_desiredAssertionStatus(JNIEnv* env, jobject javaThis) {
return JNI_FALSE;
}
@@ -194,8 +223,101 @@
return c->IsPrimitive();
}
+bool CheckClassAccess(const Class* access_from, const Class* klass) {
+ if (klass->IsPublic()) {
+ return true;
+ }
+ return access_from->IsInSamePackage(klass);
+}
+
+// Validate method/field access.
+bool CheckMemberAccess(const Class* access_from, const Class* access_to, uint32_t member_flags) {
+ // quick accept for public access */
+ if (member_flags & kAccPublic) {
+ return true;
+ }
+
+ // quick accept for access from same class
+ if (access_from == access_to) {
+ return true;
+ }
+
+ // quick reject for private access from another class
+ if (member_flags & kAccPrivate) {
+ return false;
+ }
+
+ // Semi-quick test for protected access from a sub-class, which may or
+ // may not be in the same package.
+ if (member_flags & kAccProtected) {
+ if (access_from->IsSubClass(access_to)) {
+ return true;
+ }
+ }
+
+ // Allow protected and private access from other classes in the same
+ return access_from->IsInSamePackage(access_to);
+}
+
+jobject Class_newInstanceImpl(JNIEnv* env, jobject javaThis) {
+ Class* c = Decode<Class*>(env, javaThis);
+ if (c->IsPrimitive() || c->IsInterface() || c->IsArrayClass() || c->IsAbstract()) {
+ Thread::Current()->ThrowNewException("Ljava/lang/InstantiationException;",
+ "Class %s can not be instantiated", PrettyDescriptor(c->GetDescriptor()).c_str());
+ return NULL;
+ }
+
+ Method* init = c->FindDirectMethod("<init>", "()V");
+ if (init == NULL) {
+ Thread::Current()->ThrowNewException("Ljava/lang/InstantiationException;",
+ "Class %s has no default <init>()V constructor", PrettyDescriptor(c->GetDescriptor()).c_str());
+ return NULL;
+ }
+
+ // Verify access from the call site.
+ //
+ // First, make sure the method invoking Class.newInstance() has permission
+ // to access the class.
+ //
+ // Second, make sure it has permission to invoke the constructor. The
+ // constructor must be public or, if the caller is in the same package,
+ // have package scope.
+ // TODO: need SmartFrame (Thread::WalkStack-like iterator).
+ Frame frame = Thread::Current()->GetTopOfStack();
+ frame.Next();
+ frame.Next();
+ Method* caller_caller = frame.GetMethod();
+ Class* caller_class = caller_caller->GetDeclaringClass();
+
+ if (!CheckClassAccess(c, caller_class)) {
+ Thread::Current()->ThrowNewException("Ljava/lang/IllegalAccessException;",
+ "Class %s is not accessible from class %s",
+ PrettyDescriptor(c->GetDescriptor()).c_str(),
+ PrettyDescriptor(caller_class->GetDescriptor()).c_str());
+ return NULL;
+ }
+ if (!CheckMemberAccess(caller_class, init->GetDeclaringClass(), init->GetAccessFlags())) {
+ Thread::Current()->ThrowNewException("Ljava/lang/IllegalAccessException;",
+ "%s is not accessible from class %s",
+ PrettyMethod(init).c_str(),
+ PrettyDescriptor(caller_class->GetDescriptor()).c_str());
+ return NULL;
+ }
+
+ Object* new_obj = c->AllocObject();
+ if (new_obj == NULL) {
+ DCHECK(Thread::Current()->IsExceptionPending());
+ return NULL;
+ }
+
+ // invoke constructor; unlike reflection calls, we don't wrap exceptions
+ jclass jklass = AddLocalReference<jclass>(env, c);
+ jmethodID mid = EncodeMethod(init);
+ return env->NewObject(jklass, mid);
+}
+
static JNINativeMethod gMethods[] = {
- //NATIVE_METHOD(Class, classForName, "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;"),
+ NATIVE_METHOD(Class, classForName, "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;"),
NATIVE_METHOD(Class, desiredAssertionStatus, "()Z"),
NATIVE_METHOD(Class, getClassLoader, "(Ljava/lang/Class;)Ljava/lang/ClassLoader;"),
NATIVE_METHOD(Class, getComponentType, "()Ljava/lang/Class;"),
@@ -223,7 +345,7 @@
//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;"),
+ NATIVE_METHOD(Class, newInstanceImpl, "()Ljava/lang/Object;"),
};
} // namespace
diff --git a/src/java_lang_VMClassLoader.cc b/src/java_lang_VMClassLoader.cc
index bed89d6..28421b4 100644
--- a/src/java_lang_VMClassLoader.cc
+++ b/src/java_lang_VMClassLoader.cc
@@ -25,16 +25,6 @@
namespace {
-// Turn "java.lang.String" into "Ljava/lang/String;".
-std::string ToDescriptor(const char* class_name) {
- std::string descriptor(class_name);
- std::replace(descriptor.begin(), descriptor.end(), '.', '/');
- if (descriptor.length() > 0 && descriptor[0] != '[') {
- descriptor = "L" + descriptor + ";";
- }
- return descriptor;
-}
-
jclass VMClassLoader_findLoadedClass(JNIEnv* env, jclass, jobject javaLoader, jstring javaName) {
ClassLoader* loader = Decode<ClassLoader*>(env, javaLoader);
ScopedUtfChars name(env, javaName);
@@ -42,7 +32,7 @@
return NULL;
}
- std::string descriptor(ToDescriptor(name.c_str()));
+ std::string descriptor(DotToDescriptor(name.c_str()));
Class* c = Runtime::Current()->GetClassLinker()->LookupClass(descriptor.c_str(), loader);
return AddLocalReference<jclass>(env, c);
}
@@ -90,48 +80,10 @@
return env->NewStringUTF(url.c_str());
}
-/*
- * static Class loadClass(String name, boolean resolve)
- * throws ClassNotFoundException
- *
- * Load class using bootstrap class loader.
- *
- * Return the Class object associated with the class or interface with
- * the specified name.
- *
- * "name" is in "binary name" format, e.g. "dalvik.system.Debug$1".
- */
-jclass VMClassLoader_loadClass(JNIEnv* env, jclass, jstring javaName, jboolean resolve) {
- ScopedUtfChars name(env, javaName);
- if (name.c_str() == NULL) {
- return NULL;
- }
-
- /*
- * We need to validate and convert the name (from x.y.z to x/y/z). This
- * is especially handy for array types, since we want to avoid
- * auto-generating bogus array classes.
- */
- if (!IsValidClassName(name.c_str(), true, true)) {
- Thread::Current()->ThrowNewException("Ljava/lang/ClassNotFoundException;",
- "Invalid name: %s", name.c_str());
- return NULL;
- }
-
- std::string descriptor(ToDescriptor(name.c_str()));
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- Class* c = class_linker->FindClass(descriptor.c_str(), NULL);
- if (resolve) {
- class_linker->EnsureInitialized(c, true);
- }
- return AddLocalReference<jclass>(env, c);
-}
-
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(VMClassLoader, findLoadedClass, "(Ljava/lang/ClassLoader;Ljava/lang/String;)Ljava/lang/Class;"),
NATIVE_METHOD(VMClassLoader, getBootClassPathResource, "(Ljava/lang/String;I)Ljava/lang/String;"),
NATIVE_METHOD(VMClassLoader, getBootClassPathSize, "()I"),
- NATIVE_METHOD(VMClassLoader, loadClass, "(Ljava/lang/String;Z)Ljava/lang/Class;"),
};
} // namespace
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index f9fcb78..e196c0c 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -298,7 +298,7 @@
return NULL;
}
- return reinterpret_cast<jmethodID>(method);
+ return EncodeMethod(method);
}
jfieldID FindFieldID(ScopedJniThreadState& ts, jclass jni_class, const char* name, const char* sig, bool is_static) {
@@ -342,7 +342,7 @@
// Check invariant that all jfieldIDs have resolved types (how else would
// the type equality in Find...Field hold?)
DCHECK(field->GetType() != NULL);
- return reinterpret_cast<jfieldID>(field);
+ return EncodeField(field);
}
void PinPrimitiveArray(ScopedJniThreadState& ts, const Array* array) {
@@ -639,13 +639,13 @@
static jmethodID FromReflectedMethod(JNIEnv* env, jobject java_method) {
ScopedJniThreadState ts(env);
Method* method = Decode<Method*>(ts, java_method);
- return reinterpret_cast<jmethodID>(method);
+ return EncodeMethod(method);
}
static jfieldID FromReflectedField(JNIEnv* env, jobject java_field) {
ScopedJniThreadState ts(env);
Field* field = Decode<Field*>(ts, java_field);
- return reinterpret_cast<jfieldID>(field);
+ return EncodeField(field);
}
static jobject ToReflectedMethod(JNIEnv* env, jclass, jmethodID mid, jboolean) {
diff --git a/src/jni_internal.h b/src/jni_internal.h
index 736e5db..1d7fd17 100644
--- a/src/jni_internal.h
+++ b/src/jni_internal.h
@@ -36,6 +36,13 @@
return reinterpret_cast<Field*>(fid);
}
+inline jfieldID EncodeField(Field* field) {
+#ifdef MOVING_GARBAGE_COLLECTOR
+ UNIMPLEMENTED(WARNING);
+#endif
+ return reinterpret_cast<jfieldID>(field);
+}
+
inline Method* DecodeMethod(jmethodID mid) {
#ifdef MOVING_GARBAGE_COLLECTOR
// TODO: we should make these unique weak globals if Method instances can ever move.
@@ -44,6 +51,13 @@
return reinterpret_cast<Method*>(mid);
}
+inline jmethodID EncodeMethod(Method* method) {
+#ifdef MOVING_GARBAGE_COLLECTOR
+ UNIMPLEMENTED(WARNING);
+#endif
+ return reinterpret_cast<jmethodID>(method);
+}
+
struct JavaVMExt : public JavaVM {
JavaVMExt(Runtime* runtime, Runtime::ParsedOptions* options);
~JavaVMExt();
diff --git a/src/object.h b/src/object.h
index a562999..7da6a5d 100644
--- a/src/object.h
+++ b/src/object.h
@@ -1492,6 +1492,8 @@
return that->IsPublic() || this->IsInSamePackage(that);
}
+ bool IsSubClass(const Class* klass) const;
+
bool IsAssignableFrom(const Class* src) const {
DCHECK(src != NULL);
if (this == src) {
@@ -1905,7 +1907,6 @@
bool Implements(const Class* klass) const;
bool IsArrayAssignableFromArray(const Class* klass) const;
bool IsAssignableFromArray(const Class* klass) const;
- bool IsSubClass(const Class* klass) const;
// descriptor for the class such as "java.lang.Class" or "[C"
String* name_; // TODO initialize
diff --git a/src/runtime.cc b/src/runtime.cc
index e9b7fdc..751e8da 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -456,7 +456,7 @@
void Runtime::RegisterRuntimeNativeMethods(JNIEnv* env) {
#define REGISTER(FN) extern void FN(JNIEnv*); FN(env)
- //REGISTER(register_dalvik_system_DexFile);
+ REGISTER(register_dalvik_system_DexFile);
REGISTER(register_dalvik_system_VMDebug);
REGISTER(register_dalvik_system_VMRuntime);
REGISTER(register_dalvik_system_VMStack);
diff --git a/src/utils.cc b/src/utils.cc
index 33a07ad..b47ee2c 100644
--- a/src/utils.cc
+++ b/src/utils.cc
@@ -184,6 +184,15 @@
return result;
}
+std::string DotToDescriptor(const char* class_name) {
+ std::string descriptor(class_name);
+ std::replace(descriptor.begin(), descriptor.end(), '.', '/');
+ if (descriptor.length() > 0 && descriptor[0] != '[') {
+ descriptor = "L" + descriptor + ";";
+ }
+ return descriptor;
+}
+
std::string JniShortName(const Method* m) {
Class* declaring_class = m->GetDeclaringClass();
diff --git a/src/utils.h b/src/utils.h
index e582c7d..97bf2c8 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -172,6 +172,9 @@
// of the JNI spec.
std::string MangleForJni(const std::string& s);
+// Turn "java.lang.String" into "Ljava/lang/String;".
+std::string DotToDescriptor(const char* class_name);
+
// Tests whether 's' is a valid class name.
// name_or_descriptor
// true => "java/lang/String"