Adding JNI code for dalvik.system.DexFile and java.lang.Class

Change-Id: I079c037db77aeaca0dec06660f7551f57adf2607
diff --git a/build/Android.common.mk b/build/Android.common.mk
index a77d5a8..2031f30 100644
--- a/build/Android.common.mk
+++ b/build/Android.common.mk
@@ -86,6 +86,7 @@
 	src/compiler/codegen/arm/Assemble.cc \
 	src/compiler/codegen/arm/LocalOptimizations.cc \
 	src/compiler/codegen/arm/armv7-a/Codegen.cc \
+	src/dalvik_system_DexFile.cc \
 	src/dalvik_system_VMDebug.cc \
 	src/dalvik_system_VMRuntime.cc \
 	src/dalvik_system_VMStack.cc \
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"