Add oat file generation and tests to build
- Currently builds boot.oat for host and target
and target oat files for art tests.
- Added cross compilation support via --strip-prefix option to dex2oat
- Reduced output to prevent build log spam (Compiler::verbose_)
- Added image roots for recovering important pointers on image load
- Redid JNI stub creation and made the stub array an image root
- Fixed JNI stub test by making JNI stub array executable
- Fixed JNI UnregisterNative to having it reinstall the JNI stub
- Fixed ARM JNI stub to generate PIC code (with irogers)
- Fixed JniCompiler to generate PIC code (with irogers)
- Fixed FindNativeMethod to handle recursive calls
- Finished checkFieldType to use Object::InstanceOf
- Fixed thread unsafe access to ClassLinker::{dex_files_,dex_caches_}
- Added ResolvedMethod variant for use with Method* for context
- Fixed ImageWriter to call FixupMethod
- Fixed ImageWriter to rewrite JNI stub references
- Improved error reporting on lack of ANDROID_DATA dir or art-cache dir
- Fixed Runtime::Start to InitLibraries before creating thread peer
- Implemented Space::IsCondemned to skip spaces loaded from images
- Implemented artFindInterfaceMethodInCache,
allowing interface invocation from managed code
Change-Id: I603e97fa0ac44508ae05a2e47c1cdb4481678d7b
diff --git a/Android.mk b/Android.mk
index 4543042..ae17953 100644
--- a/Android.mk
+++ b/Android.mk
@@ -35,6 +35,8 @@
include $(build_path)/Android.libart.mk
include $(build_path)/Android.executable.mk
+include $(build_path)/Android.oat.mk
+
include $(build_path)/Android.libarttest.mk
include $(build_path)/Android.test.mk
@@ -54,8 +56,8 @@
$(foreach file,$(sort $(ART_HOST_TEST_EXECUTABLES)),$(1) $(file) &&) true
endef
-ART_HOST_TEST_DEPENDENCIES := $(ART_HOST_TEST_EXECUTABLES) $(ANDROID_HOST_OUT)/framework/core-hostdex.jar $(ART_TEST_DEX_FILES)
-ART_TARGET_TEST_DEPENDENCIES := $(ART_TARGET_TEST_EXECUTABLES) $(ANDROID_PRODUCT_OUT)/system/framework/core.jar $(ART_TEST_DEX_FILES)
+ART_HOST_TEST_DEPENDENCIES := $(ART_HOST_TEST_EXECUTABLES) $(ANDROID_HOST_OUT)/framework/core-hostdex.jar $(ART_TEST_OAT_FILES)
+ART_TARGET_TEST_DEPENDENCIES := $(ART_TARGET_TEST_EXECUTABLES) $(ANDROID_PRODUCT_OUT)/system/framework/core.jar $(ART_TEST_OAT_FILES)
# "mm test-art-host" to build and run all host tests
.PHONY: test-art-host
@@ -79,9 +81,14 @@
adb sync
adb shell touch /sdcard/test-art-target
adb shell rm /sdcard/test-art-target
+ # gtest unit tests
adb shell sh -c "$(foreach file,$(sort $(ART_TARGET_TEST_EXECUTABLES)), /system/bin/$(notdir $(file)) &&) touch /sdcard/test-art-target"
adb pull /sdcard/test-art-target /tmp/
- rm /tmp/test-art-target
+ rm /tmp/test-art-target # this will cause the make on test failure (since the file will not exist)
+ # oatexec test
+ adb shell sh -c "oatexecd -Xbootclasspath:/system/framework/core.jar -Xbootimage:/system/framework/boot.oat -classpath /system/framework/art-test-dex-HelloWorld.jar -Ximage:/system/framework/art-test-dex-HelloWorld.oat HelloWorld && touch /sdcard/test-art-target"
+ adb pull /sdcard/test-art-target /tmp/
+ rm /tmp/test-art-target # this will cause the make on test failure (since the file will not exist)
# "mm cpplint-art" to style check art source files
.PHONY: cpplint-art
diff --git a/build/Android.oat.mk b/build/Android.oat.mk
new file mode 100644
index 0000000..86be8b6
--- /dev/null
+++ b/build/Android.oat.mk
@@ -0,0 +1,45 @@
+#
+# Copyright (C) 2011 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.
+#
+
+DEX2OAT := $(HOST_OUT_EXECUTABLES)/dex2oat$(HOST_EXECUTABLE_SUFFIX)
+DEX2OATD := $(HOST_OUT_EXECUTABLES)/dex2oatd$(HOST_EXECUTABLE_SUFFIX)
+# TODO: for now, override with debug version for better error reporting
+DEX2OAT := $(DEX2OATD)
+
+# start of oat reserved address space
+OAT_HOST_BASE_ADDRESS := 0x50000000
+OAT_TARGET_BASE_ADDRESS := 0x50000000
+
+HOST_BOOT_OAT := $(HOST_OUT_JAVA_LIBRARIES)/boot.oat
+TARGET_BOOT_OAT := $(TARGET_OUT_JAVA_LIBRARIES)/boot.oat
+
+# TODO: just use libcore for now, not full bootclasspath.
+# eventually need to replace with full list based on DEXPREOPT_BOOT_JARS.
+HOST_BOOT_JARS := core-hostdex
+TARGET_BOOT_JARS := core
+
+HOST_BOOT_DEX := $(foreach jar,$(HOST_BOOT_JARS), $(HOST_OUT_JAVA_LIBRARIES)/$(jar).jar)
+TARGET_BOOT_DEX := $(foreach jar,$(TARGET_BOOT_JARS),$(TARGET_OUT_JAVA_LIBRARIES)/$(jar).jar)
+
+# TODO: change DEX2OATD to order-only prerequisite when output is stable
+$(HOST_BOOT_OAT): $(HOST_BOOT_DEX) $(DEX2OAT)
+ @echo "host dex2oat: $@ ($<)"
+ $(hide) $(DEX2OAT) $(addprefix --dex-file=,$(filter-out $(DEX2OAT),$^)) --image=$@ --base=$(OAT_HOST_BASE_ADDRESS)
+
+# TODO: change DEX2OATD to order-only prerequisite when output is stable
+$(TARGET_BOOT_OAT): $(TARGET_BOOT_DEX) $(DEX2OAT)
+ @echo "target dex2oat: $@ ($<)"
+ $(hide) $(DEX2OAT) $(addprefix --dex-file=,$(filter-out $(DEX2OAT),$^)) --image=$@ --base=$(OAT_TARGET_BASE_ADDRESS) --strip-prefix=$(PRODUCT_OUT)
diff --git a/build/Android.test.mk b/build/Android.test.mk
index 6c4c858..798a69f 100644
--- a/build/Android.test.mk
+++ b/build/Android.test.mk
@@ -72,3 +72,16 @@
ART_TEST_DEX_FILES += $(TARGET_OUT_JAVA_LIBRARIES)/$$(LOCAL_MODULE).jar
endef
$(foreach dir,$(TEST_DEX_DIRECTORIES), $(eval $(call build-art-test-dex,$(dir))))
+
+ART_TEST_OAT_FILES :=
+
+# $(1): directory
+define build-art-test-oat
+# TODO: change DEX2OATD to order-only prerequisite when output is stable
+$(TARGET_OUT_JAVA_LIBRARIES)/art-test-dex-$(1).oat: $(TARGET_OUT_JAVA_LIBRARIES)/art-test-dex-$(1).jar $(TARGET_BOOT_OAT) $(DEX2OAT)
+ @echo "target dex2oat: $$@ ($$<)"
+ $(hide) $(DEX2OAT) $(addprefix --boot-dex-file=,$(TARGET_BOOT_DEX)) --boot=$(TARGET_BOOT_OAT) $(addprefix --dex-file=,$$<) --image=$$@ --strip-prefix=$(PRODUCT_OUT)
+
+ART_TEST_OAT_FILES += $(TARGET_OUT_JAVA_LIBRARIES)/art-test-dex-$(1).oat
+endef
+$(foreach dir,$(TEST_DEX_DIRECTORIES), $(eval $(call build-art-test-oat,$(dir))))
diff --git a/src/assembler.h b/src/assembler.h
index 7b5e77a..173609e 100644
--- a/src/assembler.h
+++ b/src/assembler.h
@@ -395,7 +395,6 @@
ManagedRegister scratch) = 0;
virtual void Call(FrameOffset base, Offset offset,
ManagedRegister scratch) = 0;
- virtual void Call(uintptr_t addr, ManagedRegister scratch) = 0;
// Generate code to check if Thread::Current()->suspend_count_ is non-zero
// and branch to a SuspendSlowPath if it is. The SuspendSlowPath will continue
diff --git a/src/assembler_arm.cc b/src/assembler_arm.cc
index 591012f..d0fae17 100644
--- a/src/assembler_arm.cc
+++ b/src/assembler_arm.cc
@@ -1752,15 +1752,6 @@
// TODO: place reference map on call
}
-void ArmAssembler::Call(uintptr_t addr, ManagedRegister mscratch) {
- ArmManagedRegister scratch = mscratch.AsArm();
- CHECK(scratch.IsCoreRegister());
- CHECK(sizeof(uintptr_t) == sizeof(int32_t));
- LoadImmediate(scratch.AsCoreRegister(), static_cast<int32_t>(addr));
- blx(scratch.AsCoreRegister());
- // TODO: place reference map on call
-}
-
void ArmAssembler::GetCurrentThread(ManagedRegister tr) {
mov(tr.AsArm().AsCoreRegister(), ShifterOperand(TR));
}
diff --git a/src/assembler_arm.h b/src/assembler_arm.h
index d4bf93d..cf031c7 100644
--- a/src/assembler_arm.h
+++ b/src/assembler_arm.h
@@ -516,7 +516,6 @@
ManagedRegister scratch);
virtual void Call(FrameOffset base, Offset offset,
ManagedRegister scratch);
- virtual void Call(uintptr_t addr, ManagedRegister scratch);
// Generate code to check if Thread::Current()->suspend_count_ is non-zero
// and branch to a SuspendSlowPath if it is. The SuspendSlowPath will continue
diff --git a/src/assembler_x86.cc b/src/assembler_x86.cc
index c303fb1..45c0086 100644
--- a/src/assembler_x86.cc
+++ b/src/assembler_x86.cc
@@ -1654,9 +1654,15 @@
}
void X86Assembler::Call(FrameOffset base, Offset offset, ManagedRegister) {
+ // TODO: Needed for:
+ // JniCompilerTest.CompileAndRunIntObjectObjectMethod
+ // JniCompilerTest.CompileAndRunStaticIntObjectObjectMethod
+ // JniCompilerTest.CompileAndRunStaticSynchronizedIntObjectObjectMethod
+ // JniCompilerTest.ReturnGlobalRef
UNIMPLEMENTED(FATAL);
}
+// TODO: remove this generator of non-PIC code
void X86Assembler::Call(uintptr_t addr, ManagedRegister mscratch) {
Register scratch = mscratch.AsX86().AsCpuRegister();
movl(scratch, Immediate(addr));
diff --git a/src/check_jni.cc b/src/check_jni.cc
index ce4c2d0..61be30e 100644
--- a/src/check_jni.cc
+++ b/src/check_jni.cc
@@ -374,16 +374,11 @@
JniAbort();
return;
} else {
-#if 0
- Class* field_class = dvmFindLoadedClass(f->signature);
- if (!obj->GetClass()->InstanceOf(field_class)) {
- LOG(ERROR) << "JNI ERROR: attempt to set field " << PrettyField(f) << " with value of wrong type: " << PrettyType(java_object);
+ if (!obj->InstanceOf(field_type)) {
+ LOG(ERROR) << "JNI ERROR: attempt to set field " << PrettyField(f) << " with value of wrong type: " << PrettyType(obj);
JniAbort();
return;
}
-#else
- UNIMPLEMENTED(WARNING) << "need way to get Class* for a given Field*'s type";
-#endif
}
}
} else if (field_type != Runtime::Current()->GetClassLinker()->FindPrimitiveClass(prim)) {
diff --git a/src/class_linker.cc b/src/class_linker.cc
index 5b4c08c..f25bac0 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -71,7 +71,7 @@
}
ClassLinker::ClassLinker(InternTable* intern_table)
- : classes_lock_("ClassLinker lock"),
+ : lock_("ClassLinker lock"),
class_roots_(NULL),
array_interfaces_(NULL),
array_iftable_(NULL),
@@ -251,7 +251,6 @@
// this initializes their dex_cache_ fields and register them in classes_.
Class* Class_class = FindSystemClass("Ljava/lang/Class;");
CHECK_EQ(java_lang_Class, Class_class);
- // No sanity check on size as Class is variably sized
java_lang_reflect_Field->SetStatus(Class::kStatusNotReady);
Class* Field_class = FindSystemClass("Ljava/lang/reflect/Field;");
@@ -278,43 +277,6 @@
java_lang_ref_WeakReference->GetAccessFlags() |
kAccClassIsReference | kAccClassIsWeakReference);
- // Let the heap know some key offsets into java.lang.ref instances
- // NB we hard code the field indexes here rather than using FindInstanceField
- // as the types of the field can't be resolved prior to the runtime being
- // fully initialized
- Class* java_lang_ref_Reference = FindSystemClass("Ljava/lang/ref/Reference;");
-
- Field* pendingNext = java_lang_ref_Reference->GetInstanceField(0);
- CHECK(pendingNext->GetName()->Equals("pendingNext"));
- CHECK(ResolveType(pendingNext->GetTypeIdx(), pendingNext) ==
- java_lang_ref_Reference);
-
- Field* queue = java_lang_ref_Reference->GetInstanceField(1);
- CHECK(queue->GetName()->Equals("queue"));
- CHECK(ResolveType(queue->GetTypeIdx(), queue) ==
- FindSystemClass("Ljava/lang/ref/ReferenceQueue;"));
-
- Field* queueNext = java_lang_ref_Reference->GetInstanceField(2);
- CHECK(queueNext->GetName()->Equals("queueNext"));
- CHECK(ResolveType(queueNext->GetTypeIdx(), queueNext) ==
- java_lang_ref_Reference);
-
- Field* referent = java_lang_ref_Reference->GetInstanceField(3);
- CHECK(referent->GetName()->Equals("referent"));
- CHECK(ResolveType(referent->GetTypeIdx(), referent) ==
- java_lang_Object);
-
- Field* zombie = java_lang_ref_FinalizerReference->GetInstanceField(2);
- CHECK(zombie->GetName()->Equals("zombie"));
- CHECK(ResolveType(zombie->GetTypeIdx(), zombie) ==
- java_lang_Object);
-
- Heap::SetReferenceOffsets(referent->GetOffset(),
- queue->GetOffset(),
- queueNext->GetOffset(),
- pendingNext->GetOffset(),
- zombie->GetOffset());
-
// Setup the ClassLoaders, adjusting the object_size_ as necessary
Class* java_lang_ClassLoader = FindSystemClass("Ljava/lang/ClassLoader;");
CHECK_LT(java_lang_ClassLoader->GetObjectSize(), sizeof(ClassLoader));
@@ -339,6 +301,41 @@
}
void ClassLinker::FinishInit() {
+
+ // Let the heap know some key offsets into java.lang.ref instances
+ // NB we hard code the field indexes here rather than using FindInstanceField
+ // as the types of the field can't be resolved prior to the runtime being
+ // fully initialized
+ Class* java_lang_ref_Reference = FindSystemClass("Ljava/lang/ref/Reference;");
+ Class* java_lang_ref_FinalizerReference = FindSystemClass("Ljava/lang/ref/FinalizerReference;");
+
+ Field* pendingNext = java_lang_ref_Reference->GetInstanceField(0);
+ CHECK(pendingNext->GetName()->Equals("pendingNext"));
+ CHECK_EQ(ResolveType(pendingNext->GetTypeIdx(), pendingNext), java_lang_ref_Reference);
+
+ Field* queue = java_lang_ref_Reference->GetInstanceField(1);
+ CHECK(queue->GetName()->Equals("queue"));
+ CHECK_EQ(ResolveType(queue->GetTypeIdx(), queue),
+ FindSystemClass("Ljava/lang/ref/ReferenceQueue;"));
+
+ Field* queueNext = java_lang_ref_Reference->GetInstanceField(2);
+ CHECK(queueNext->GetName()->Equals("queueNext"));
+ CHECK_EQ(ResolveType(queueNext->GetTypeIdx(), queueNext), java_lang_ref_Reference);
+
+ Field* referent = java_lang_ref_Reference->GetInstanceField(3);
+ CHECK(referent->GetName()->Equals("referent"));
+ CHECK_EQ(ResolveType(referent->GetTypeIdx(), referent), GetClassRoot(kJavaLangObject));
+
+ Field* zombie = java_lang_ref_FinalizerReference->GetInstanceField(2);
+ CHECK(zombie->GetName()->Equals("zombie"));
+ CHECK_EQ(ResolveType(zombie->GetTypeIdx(), zombie), GetClassRoot(kJavaLangObject));
+
+ Heap::SetReferenceOffsets(referent->GetOffset(),
+ queue->GetOffset(),
+ queueNext->GetOffset(),
+ pendingNext->GetOffset(),
+ zombie->GetOffset());
+
// ensure all class_roots_ are initialized
for (size_t i = 0; i < kClassRootsMax; i++) {
ClassRoot class_root = static_cast<ClassRoot>(i);
@@ -494,7 +491,7 @@
}
{
- MutexLock mu(classes_lock_);
+ MutexLock mu(lock_);
typedef Table::const_iterator It; // TODO: C++0x auto
for (It it = classes_.begin(), end = classes_.end(); it != end; ++it) {
visitor(it->second, arg);
@@ -924,6 +921,7 @@
}
void ClassLinker::RegisterDexFile(const DexFile& dex_file, DexCache* dex_cache) {
+ MutexLock mu(lock_);
CHECK(dex_cache != NULL) << dex_file.GetLocation();
CHECK(dex_cache->GetLocation()->Equals(dex_file.GetLocation()));
dex_files_.push_back(&dex_file);
@@ -931,6 +929,7 @@
}
const DexFile& ClassLinker::FindDexFile(const DexCache* dex_cache) const {
+ MutexLock mu(lock_);
for (size_t i = 0; i != dex_caches_.size(); ++i) {
if (dex_caches_[i] == dex_cache) {
return *dex_files_[i];
@@ -941,6 +940,7 @@
}
DexCache* ClassLinker::FindDexCache(const DexFile& dex_file) const {
+ MutexLock mu(lock_);
for (size_t i = 0; i != dex_files_.size(); ++i) {
if (dex_files_[i] == &dex_file) {
return dex_caches_[i];
@@ -1150,14 +1150,14 @@
bool ClassLinker::InsertClass(const StringPiece& descriptor, Class* klass) {
size_t hash = StringPieceHash()(descriptor);
- MutexLock mu(classes_lock_);
+ MutexLock mu(lock_);
Table::iterator it = classes_.insert(std::make_pair(hash, klass));
return ((*it).second == klass);
}
Class* ClassLinker::LookupClass(const StringPiece& descriptor, const ClassLoader* class_loader) {
size_t hash = StringPieceHash()(descriptor);
- MutexLock mu(classes_lock_);
+ MutexLock mu(lock_);
typedef Table::const_iterator It; // TODO: C++0x auto
for (It it = classes_.find(hash), end = classes_.end(); it != end; ++it) {
Class* klass = it->second;
@@ -2125,7 +2125,7 @@
}
size_t ClassLinker::NumLoadedClasses() const {
- MutexLock mu(classes_lock_);
+ MutexLock mu(lock_);
return classes_.size();
}
diff --git a/src/class_linker.h b/src/class_linker.h
index 88871f4..e143fd8 100644
--- a/src/class_linker.h
+++ b/src/class_linker.h
@@ -104,6 +104,15 @@
const ClassLoader* class_loader,
bool is_direct);
+ Method* ResolveMethod(uint32_t method_idx, const Method* referrer, bool is_direct) {
+ Class* declaring_class = referrer->GetDeclaringClass();
+ DexCache* dex_cache = declaring_class->GetDexCache();
+ // TODO: we could check for a dex cache hit here
+ const ClassLoader* class_loader = declaring_class->GetClassLoader();
+ const DexFile& dex_file = FindDexFile(dex_cache);
+ return ResolveMethod(dex_file, method_idx, dex_cache, class_loader, is_direct);
+ }
+
Field* ResolveField(uint32_t field_idx, const Method* referrer) {
Class* declaring_class = referrer->GetDeclaringClass();
DexCache* dex_cache = declaring_class->GetDexCache();
@@ -113,7 +122,7 @@
return ResolveField(dex_file, field_idx, dex_cache, class_loader, true);
}
- // Resolve a method with a given ID from the DexFile, storing the
+ // Resolve a field with a given ID from the DexFile, storing the
// result in DexCache. The ClassLinker and ClassLoader are used as
// in ResolveType. What is unique is the is_static argument which is
// used to determine if we are resolving a static or non-static
@@ -252,16 +261,17 @@
void CreateReferenceOffsets(Class *klass, bool instance,
uint32_t reference_offsets);
+ // lock to protect ClassLinker state
+ mutable Mutex lock_;
+
std::vector<const DexFile*> boot_class_path_;
std::vector<const DexFile*> dex_files_;
-
std::vector<DexCache*> dex_caches_;
// multimap from a StringPiece hash code of a class descriptor to
// Class* instances. Results should be compared for a matching
// Class::descriptor_ and Class::class_loader_.
- mutable Mutex classes_lock_;
typedef std::tr1::unordered_multimap<size_t, Class*> Table;
Table classes_;
diff --git a/src/common_test.h b/src/common_test.h
index fdea986..fefbbda 100644
--- a/src/common_test.h
+++ b/src/common_test.h
@@ -110,8 +110,10 @@
class_linker_ = runtime_->GetClassLinker();
#if defined(__i386__)
+ runtime_->SetJniStubArray(JniCompiler::CreateJniStub(kX86));
compiler_.reset(new Compiler(kX86));
#elif defined(__arm__)
+ runtime_->SetJniStubArray(JniCompiler::CreateJniStub(kThumb2));
compiler_.reset(new Compiler(kThumb2));
#endif
@@ -171,7 +173,7 @@
const DexFile* GetLibCoreDex() {
std::string libcore_dex_file_name = GetLibCoreDexFileName();
- return DexFile::OpenZip(libcore_dex_file_name);
+ return DexFile::OpenZip(libcore_dex_file_name, "");
}
uint32_t FindTypeIdxByDescriptor(const DexFile& dex_file, const StringPiece& descriptor) {
@@ -217,7 +219,7 @@
filename += "/system/framework/art-test-dex-";
filename += name;
filename += ".jar";
- const DexFile* dex_file = DexFile::OpenZip(filename);
+ const DexFile* dex_file = DexFile::OpenZip(filename, "");
CHECK(dex_file != NULL) << "Failed to open " << filename;
return dex_file;
}
@@ -247,6 +249,7 @@
void CompileMethod(Method* method) {
CHECK(method != NULL);
compiler_->CompileOne(method);
+ MakeExecutable(runtime_->GetJniStubArray());
MakeExecutable(method->GetCodeArray());
MakeExecutable(method->GetInvokeStubArray());
}
diff --git a/src/compiler.cc b/src/compiler.cc
index fa9867d..8a382f5 100644
--- a/src/compiler.cc
+++ b/src/compiler.cc
@@ -10,7 +10,7 @@
#include "jni_internal.h"
#include "runtime.h"
-extern bool oatCompileMethod(art::Method*, art::InstructionSet);
+extern bool oatCompileMethod(const art::Compiler& compiler, art::Method*, art::InstructionSet);
namespace art {
@@ -31,7 +31,8 @@
ByteArray* CreateAbstractMethodErrorStub(ThrowAme);
}
-Compiler::Compiler(InstructionSet insns) : instruction_set_(insns), jni_compiler_(insns) {
+Compiler::Compiler(InstructionSet insns) : instruction_set_(insns), jni_compiler_(insns),
+ verbose_(false) {
if (insns == kArm || insns == kThumb2) {
abstract_method_error_stub_ = arm::CreateAbstractMethodErrorStub(&ThrowAbstractMethodError);
} else if (insns == kX86) {
@@ -41,7 +42,15 @@
void Compiler::CompileAll(const ClassLoader* class_loader) {
Resolve(class_loader);
- // TODO add verification step
+ // TODO: add verification step
+
+ // TODO: mark all verified classes initialized if they have no <clinit>
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ Class* Class_class = class_linker->FindSystemClass("Ljava/lang/Class;");
+ Method* Class_clinit = Class_class->FindDirectMethod("<clinit>", "()V");
+ CHECK(Class_clinit == NULL);
+ Class_class->SetStatus(Class::kStatusInitialized);
+
Compile(class_loader);
SetCodeAndDirectMethods(class_loader);
}
@@ -49,7 +58,7 @@
void Compiler::CompileOne(Method* method) {
const ClassLoader* class_loader = method->GetDeclaringClass()->GetClassLoader();
Resolve(class_loader);
- // TODO add verification step
+ // TODO: add verification step
CompileMethod(method);
SetCodeAndDirectMethods(class_loader);
}
@@ -131,6 +140,8 @@
void Compiler::CompileMethod(Method* method) {
if (method->IsNative()) {
jni_compiler_.Compile(method);
+ // unregister will install the stub to lookup via dlsym
+ method->UnregisterNative();
} else if (method->IsAbstract()) {
DCHECK(abstract_method_error_stub_ != NULL);
if (instruction_set_ == kX86) {
@@ -140,7 +151,7 @@
method->SetCode(abstract_method_error_stub_, kArm);
}
} else {
- oatCompileMethod(method, kThumb2);
+ oatCompileMethod(*this, method, kThumb2);
}
CHECK(method->GetCode() != NULL);
diff --git a/src/compiler.h b/src/compiler.h
index 24889e8..958d9ab 100644
--- a/src/compiler.h
+++ b/src/compiler.h
@@ -20,6 +20,14 @@
// Compile a single Method
void CompileOne(Method* method);
+ void SetVerbose(bool verbose) {
+ verbose_ = verbose;
+ }
+
+ bool IsVerbose() const {
+ return verbose_;
+ }
+
private:
// Attempt to resolve all type, methods, fields, and strings
// referenced from code in the dex file following PathClassLoader
@@ -41,6 +49,8 @@
JniCompiler jni_compiler_;
ByteArray* abstract_method_error_stub_;
+ bool verbose_;
+
DISALLOW_COPY_AND_ASSIGN(Compiler);
};
diff --git a/src/compiler/Compiler.h b/src/compiler/Compiler.h
index c1eadcc..ba97aac 100644
--- a/src/compiler/Compiler.h
+++ b/src/compiler/Compiler.h
@@ -61,12 +61,12 @@
struct GrowableList;
struct MIR;
-void oatInit(void);
+void oatInit(const Compiler& compiler);
bool oatArchInit(void);
void oatArchDump(void);
bool oatStartup(void);
void oatShutdown(void);
-bool oatCompileMethod(Method* method, OatInstructionSetType);
+bool oatCompileMethod(const Compiler& compiler, Method* method, OatInstructionSetType);
void oatDumpStats(void);
void oatScanAllClassPointers(void (*callback)(void* ptr));
void oatInitializeSSAConversion(struct CompilationUnit* cUnit);
diff --git a/src/compiler/Dalvik.h b/src/compiler/Dalvik.h
index a9ddcd8..f4f94e2 100644
--- a/src/compiler/Dalvik.h
+++ b/src/compiler/Dalvik.h
@@ -28,6 +28,7 @@
#include "object.h"
#include "thread.h"
#include "class_linker.h"
+#include "compiler.h"
#include "dex_cache.h"
#include "utils.h"
@@ -51,14 +52,15 @@
#include "DexOpcodes.h"
#include "InstrUtils.h"
-typedef art::JValue JValue;
-typedef art::Thread Thread;
-typedef art::Class Class;
typedef art::Array Array;
+typedef art::Class Class;
+typedef art::Compiler Compiler;
+typedef art::Field Field;
+typedef art::JValue JValue;
typedef art::Method Method;
typedef art::Object Object;
-typedef art::Field Field;
typedef art::String String;
+typedef art::Thread Thread;
// From alloc/CardTable.h
#define GC_CARD_SHIFT 7
diff --git a/src/compiler/Frontend.cc b/src/compiler/Frontend.cc
index 086c0bb..9e6617e 100644
--- a/src/compiler/Frontend.cc
+++ b/src/compiler/Frontend.cc
@@ -669,9 +669,11 @@
/*
* Compile a method.
*/
-bool oatCompileMethod(Method* method, art::InstructionSet insnSet)
+bool oatCompileMethod(const Compiler& compiler, Method* method, art::InstructionSet insnSet)
{
- LOG(INFO) << "Compiling " << PrettyMethod(method) << "...";
+ if (compiler.IsVerbose()) {
+ LOG(INFO) << "Compiling " << PrettyMethod(method) << "...";
+ }
oatArenaReset();
CompilationUnit cUnit;
@@ -687,7 +689,7 @@
#if 1
// FIXME - temp 'till properly integrated
- oatInit();
+ oatInit(compiler);
#endif
memset(&cUnit, 0, sizeof(cUnit));
@@ -697,8 +699,8 @@
cUnit.insnsSize = code_item->insns_size_;
#if 1
// TODO: Use command-line argument passing mechanism
- cUnit.printMe = false;
- cUnit.printMeVerbose = false;
+ cUnit.printMe = compiler.IsVerbose();
+ cUnit.printMeVerbose = compiler.IsVerbose();
cUnit.disableOpt = 0 |
(1 << kLoadStoreElimination) |
(1 << kLoadHoisting) |
@@ -889,9 +891,11 @@
method->SetFrameSizeInBytes(cUnit.frameSize);
method->SetCoreSpillMask(cUnit.coreSpillMask);
method->SetFpSpillMask(cUnit.fpSpillMask);
- LOG(INFO) << "Compiled " << PrettyMethod(method)
- << " code at " << reinterpret_cast<void*>(managed_code->GetData())
- << " (" << managed_code->GetLength() << " bytes)";
+ if (compiler.IsVerbose()) {
+ LOG(INFO) << "Compiled " << PrettyMethod(method)
+ << " code at " << reinterpret_cast<void*>(managed_code->GetData())
+ << " (" << managed_code->GetLength() << " bytes)";
+ }
#if 0
oatDumpCFG(&cUnit, "/sdcard/cfg/");
#endif
@@ -899,7 +903,7 @@
return true;
}
-void oatInit(void)
+void oatInit(const Compiler& compiler)
{
#if 1
// FIXME - temp hack 'till properly integrated
@@ -907,7 +911,9 @@
if (initialized)
return;
initialized = true;
- LOG(INFO) << "Initializing compiler";
+ if (compiler.IsVerbose()) {
+ LOG(INFO) << "Initializing compiler";
+ }
#endif
if (!oatArchInit()) {
LOG(FATAL) << "Failed to initialize oat";
diff --git a/src/compiler/Utility.cc b/src/compiler/Utility.cc
index 6254b3d..fbdf62c 100644
--- a/src/compiler/Utility.cc
+++ b/src/compiler/Utility.cc
@@ -78,7 +78,7 @@
currentArena->next = newArena;
currentArena = newArena;
numArenaBlocks++;
- if (numArenaBlocks > 10) {
+ if (numArenaBlocks > 1000) {
LOG(INFO) << "Total arena pages: " << numArenaBlocks;
}
goto retry;
diff --git a/src/dex2oat.cc b/src/dex2oat.cc
index 64e0aaa..304c7d2 100644
--- a/src/dex2oat.cc
+++ b/src/dex2oat.cc
@@ -46,14 +46,20 @@
" --method may be used to limit compilation to a subset of methods.\n"
" Example: --method=Ljava/lang/Object;<init>()V\n"
"\n");
+ fprintf(stderr,
+ " --strip-prefix may be used to strip a path prefix from dex file names in the\n"
+ " the generated image to match the target file system layout.\n"
+ " Example: --strip-prefix=out/target/product/crespo\n"
+ "\n");
exit(EXIT_FAILURE);
}
static void OpenDexFiles(std::vector<const char*>& dex_filenames,
- std::vector<const DexFile*>& dex_files) {
+ std::vector<const DexFile*>& dex_files,
+ const std::string& strip_location_prefix) {
for (size_t i = 0; i < dex_filenames.size(); i++) {
const char* dex_filename = dex_filenames[i];
- const DexFile* dex_file = DexFile::Open(dex_filename);
+ const DexFile* dex_file = DexFile::Open(dex_filename, strip_location_prefix);
if (dex_file == NULL) {
fprintf(stderr, "could not open .dex from file %s\n", dex_filename);
exit(EXIT_FAILURE);
@@ -78,6 +84,8 @@
std::string boot_image_option;
std::vector<const char*> boot_dex_filenames;
uintptr_t image_base = 0;
+ std::string strip_location_prefix;
+
for (int i = 0; i < argc; i++) {
const StringPiece option(argv[i]);
if (option.starts_with("--dex-file=")) {
@@ -101,6 +109,8 @@
boot_image_option += boot_image_filename;
} else if (option.starts_with("--boot-dex-file=")) {
boot_dex_filenames.push_back(option.substr(strlen("--boot-dex-file=")).data());
+ } else if (option.starts_with("--strip-prefix=")) {
+ strip_location_prefix = option.substr(strlen("--strip-prefix=")).data();
} else {
fprintf(stderr, "unknown argument %s\n", option.data());
usage();
@@ -125,10 +135,10 @@
}
std::vector<const DexFile*> dex_files;
- OpenDexFiles(dex_filenames, dex_files);
+ OpenDexFiles(dex_filenames, dex_files, strip_location_prefix);
std::vector<const DexFile*> boot_dex_files;
- OpenDexFiles(boot_dex_filenames, boot_dex_files);
+ OpenDexFiles(boot_dex_filenames, boot_dex_files, strip_location_prefix);
Runtime::Options options;
if (boot_image_option.empty()) {
@@ -162,6 +172,11 @@
class_loader = PathClassLoader::Alloc(dex_files);
}
+ // if we loaded an existing image, we will reuse its stub array.
+ if (!runtime->HasJniStubArray()) {
+ runtime->SetJniStubArray(JniCompiler::CreateJniStub(kThumb2));
+ }
+
Compiler compiler(kThumb2);
if (method_names.empty()) {
compiler.CompileAll(class_loader);
diff --git a/src/dex_file.cc b/src/dex_file.cc
index b982291..71fd5a7 100644
--- a/src/dex_file.cc
+++ b/src/dex_file.cc
@@ -42,16 +42,17 @@
reinterpret_cast<const DexFile::ClassDef*>(NULL));
}
-const DexFile* DexFile::Open(const std::string& filename) {
+const DexFile* DexFile::Open(const std::string& filename,
+ const std::string& strip_location_prefix) {
if (filename.size() < 4) {
LOG(WARNING) << "Ignoring short classpath entry '" << filename << "'";
return NULL;
}
std::string suffix(filename.substr(filename.size() - 4));
if (suffix == ".zip" || suffix == ".jar" || suffix == ".apk") {
- return DexFile::OpenZip(filename);
+ return DexFile::OpenZip(filename, strip_location_prefix);
} else {
- return DexFile::OpenFile(filename);
+ return DexFile::OpenFile(filename, filename, strip_location_prefix);
}
}
@@ -69,7 +70,15 @@
DexFile::PtrCloser::PtrCloser(byte* addr) : addr_(addr) {}
DexFile::PtrCloser::~PtrCloser() { delete[] addr_; }
-const DexFile* DexFile::OpenFile(const std::string& filename) {
+const DexFile* DexFile::OpenFile(const std::string& filename,
+ const std::string& original_location,
+ const std::string& strip_location_prefix) {
+ StringPiece location = original_location;
+ if (!location.starts_with(strip_location_prefix)) {
+ LOG(ERROR) << filename << " does not start with " << strip_location_prefix;
+ return NULL;
+ }
+ location.remove_prefix(strip_location_prefix.size());
int fd = open(filename.c_str(), O_RDONLY); // TODO: scoped_fd
if (fd == -1) {
PLOG(ERROR) << "open(\"" << filename << "\", O_RDONLY) failed";
@@ -92,7 +101,7 @@
close(fd);
byte* dex_file = reinterpret_cast<byte*>(addr);
Closer* closer = new MmapCloser(addr, length);
- return Open(dex_file, length, filename, closer);
+ return Open(dex_file, length, location.ToString(), closer);
}
static const char* kClassesDex = "classes.dex";
@@ -152,7 +161,8 @@
};
// Open classes.dex from within a .zip, .jar, .apk, ...
-const DexFile* DexFile::OpenZip(const std::string& filename) {
+const DexFile* DexFile::OpenZip(const std::string& filename,
+ const std::string& strip_location_prefix) {
// First, look for a ".dex" alongside the jar file. It will have
// the same name/path except for the extension.
@@ -161,7 +171,7 @@
std::string adjacent_dex_filename(filename);
size_t found = adjacent_dex_filename.find_last_of(".");
if (found == std::string::npos) {
- LOG(WARNING) << "No . in filename" << filename;
+ LOG(ERROR) << "No . in filename" << filename;
return NULL;
}
adjacent_dex_filename.replace(adjacent_dex_filename.begin() + found,
@@ -169,7 +179,9 @@
".dex");
// Example adjacent_dex_filename = dir/foo.dex
if (OS::FileExists(adjacent_dex_filename.c_str())) {
- const DexFile* adjacent_dex_file = DexFile::OpenFile(adjacent_dex_filename);
+ const DexFile* adjacent_dex_file = DexFile::OpenFile(adjacent_dex_filename,
+ filename,
+ strip_location_prefix);
if (adjacent_dex_file != NULL) {
// We don't verify anything in this case, because we aren't in
// the cache and typically the file is in the readonly /system
@@ -182,8 +194,8 @@
char resolved[PATH_MAX];
char* absolute_path = realpath(filename.c_str(), resolved);
if (absolute_path == NULL) {
- LOG(WARNING) << "Failed to create absolute path for " << filename
- << " when looking for classes.dex";
+ LOG(ERROR) << "Failed to create absolute path for " << filename
+ << " when looking for classes.dex";
return NULL;
}
std::string cache_file(absolute_path+1); // skip leading slash
@@ -194,20 +206,43 @@
const char* data_root = getenv("ANDROID_DATA");
if (data_root == NULL) {
- data_root = "/data";
+ if (OS::DirectoryExists("/data")) {
+ data_root = "/data";
+ } else {
+ data_root = "/tmp";
+ }
+ }
+ if (!OS::DirectoryExists(data_root)) {
+ LOG(ERROR) << "Failed to find ANDROID_DATA directory " << data_root;
+ return NULL;
}
- std::string cache_path_tmp = StringPrintf("%s/art-cache/%s", data_root, cache_file.c_str());
+ std::string art_cache = StringPrintf("%s/art-cache", data_root);
+
+ if (!OS::DirectoryExists(art_cache.c_str())) {
+ if (StringPiece(art_cache).starts_with("/tmp/")) {
+ int result = mkdir(art_cache.c_str(), 0700);
+ if (result != 0) {
+ LOG(ERROR) << "Failed to create art-cache directory " << art_cache;
+ return NULL;
+ }
+ } else {
+ LOG(ERROR) << "Failed to find art-cache directory " << art_cache;
+ return NULL;
+ }
+ }
+
+ std::string cache_path_tmp = StringPrintf("%s/%s", art_cache.c_str(), cache_file.c_str());
// Example cache_path_tmp = /data/art-cache/parent@dir@foo.jar@classes.dex
UniquePtr<ZipArchive> zip_archive(ZipArchive::Open(filename));
if (zip_archive.get() == NULL) {
- LOG(WARNING) << "Failed to open " << filename << " when looking for classes.dex";
+ LOG(ERROR) << "Failed to open " << filename << " when looking for classes.dex";
return NULL;
}
UniquePtr<ZipEntry> zip_entry(zip_archive->Find(kClassesDex));
if (zip_entry.get() == NULL) {
- LOG(WARNING) << "Failed to find classes.dex within " << filename;
+ LOG(ERROR) << "Failed to find classes.dex within " << filename;
return NULL;
}
@@ -216,7 +251,9 @@
while (true) {
if (OS::FileExists(cache_path.c_str())) {
- const DexFile* cached_dex_file = DexFile::OpenFile(cache_path);
+ const DexFile* cached_dex_file = DexFile::OpenFile(cache_path,
+ filename,
+ strip_location_prefix);
if (cached_dex_file != NULL) {
return cached_dex_file;
}
@@ -355,7 +392,7 @@
bool DexFile::CheckMagic(const byte* magic) {
CHECK(magic != NULL);
if (memcmp(magic, kDexMagic, sizeof(kDexMagic)) != 0) {
- LOG(WARNING) << "Unrecognized magic number:"
+ LOG(ERROR) << "Unrecognized magic number:"
<< " " << magic[0]
<< " " << magic[1]
<< " " << magic[2]
@@ -364,7 +401,7 @@
}
const byte* version = &magic[sizeof(kDexMagic)];
if (memcmp(version, kDexMagicVersion, sizeof(kDexMagicVersion)) != 0) {
- LOG(WARNING) << "Unrecognized version number:"
+ LOG(ERROR) << "Unrecognized version number:"
<< " " << version[0]
<< " " << version[1]
<< " " << version[2]
diff --git a/src/dex_file.h b/src/dex_file.h
index cf0bf20..4448981 100644
--- a/src/dex_file.h
+++ b/src/dex_file.h
@@ -317,13 +317,17 @@
const ClassPath& class_path);
// Opens .dex file, guessing the format based on file extension
- static const DexFile* Open(const std::string& filename);
+ static const DexFile* Open(const std::string& filename,
+ const std::string& strip_location_prefix);
// Opens a .dex file from the file system.
- static const DexFile* OpenFile(const std::string& filename);
+ static const DexFile* OpenFile(const std::string& filename,
+ const std::string& original_location,
+ const std::string& strip_location_prefix);
// Opens a .jar, .zip, or .apk file from the file system.
- static const DexFile* OpenZip(const std::string& filename);
+ static const DexFile* OpenZip(const std::string& filename,
+ const std::string& strip_location_prefix);
// Opens a .dex file from a new allocated pointer. location is used
// to identify the source, for example "/system/framework/core.jar"
diff --git a/src/image.h b/src/image.h
index 02d15de..0694630 100644
--- a/src/image.h
+++ b/src/image.h
@@ -15,7 +15,8 @@
public:
ImageHeader() {}
- ImageHeader(uint32_t base_addr) : base_addr_(base_addr) {
+ ImageHeader(uint32_t base_addr, uint32_t image_roots)
+ : base_addr_(base_addr), image_roots_(image_roots) {
memcpy(magic_, kImageMagic, sizeof(kImageMagic));
memcpy(version_, kImageVersion, sizeof(kImageVersion));
}
@@ -34,6 +35,15 @@
return reinterpret_cast<byte*>(base_addr_);
}
+ enum ImageRoot {
+ kJniStubArray,
+ kImageRootsMax,
+ };
+
+ Object* GetImageRoot(ImageRoot image_root) const {
+ return reinterpret_cast<ObjectArray<Object>*>(image_roots_)->Get(image_root);
+ }
+
private:
static const byte kImageMagic[4];
static const byte kImageVersion[4];
@@ -43,6 +53,11 @@
// required base address for mapping the image.
uint32_t base_addr_;
+
+ // absolute address of an Object[] of objects needed to reinitialize from an image
+ uint32_t image_roots_;
+
+ friend class ImageWriter;
};
} // namespace art
diff --git a/src/image_test.cc b/src/image_test.cc
index 02a7d69..8f3f0b7 100644
--- a/src/image_test.cc
+++ b/src/image_test.cc
@@ -66,6 +66,8 @@
ASSERT_TRUE(runtime_.get() != NULL);
class_linker_ = runtime_->GetClassLinker();
+ ASSERT_TRUE(runtime_->GetJniStubArray() != NULL);
+
ASSERT_EQ(2U, Heap::GetSpaces().size());
Space* boot_space = Heap::GetBootSpace();
ASSERT_TRUE(boot_space != NULL);
diff --git a/src/image_writer.cc b/src/image_writer.cc
index c265838..17171bb 100644
--- a/src/image_writer.cc
+++ b/src/image_writer.cc
@@ -99,19 +99,34 @@
}
}
+ObjectArray<Object>* CreateImageRoots() {
+ // build a Object[] of the roots needed to restore the runtime
+ Runtime* runtime = Runtime::Current();
+ ClassLinker* class_linker = runtime->GetClassLinker();
+ Class* object_array_class = class_linker->FindSystemClass("[Ljava/lang/Object;");
+ ObjectArray<Object>* image_roots = ObjectArray<Object>::Alloc(object_array_class,
+ ImageHeader::kImageRootsMax);
+ image_roots->Set(ImageHeader::kJniStubArray, runtime->GetJniStubArray());
+ return image_roots;
+}
+
void ImageWriter::CalculateNewObjectOffsets() {
+ ObjectArray<Object>* image_roots = CreateImageRoots();
+
HeapBitmap* heap_bitmap = Heap::GetLiveBits();
DCHECK(heap_bitmap != NULL);
DCHECK_EQ(0U, image_top_);
- // leave space for the header, but do not write it yet
+ // leave space for the header, but do not write it yet, we need to
+ // know where image_roots is going to end up
image_top_ += RoundUp(sizeof(ImageHeader), 8); // 64-bit-alignment
heap_bitmap->Walk(CalculateNewObjectOffsetsCallback, this); // TODO: add Space-limited Walk
DCHECK_LT(image_top_, image_->GetLength());
- // return to write header at start of image
- ImageHeader image_header(reinterpret_cast<uint32_t>(image_base_));
+ // return to write header at start of image with future location of image_roots
+ ImageHeader image_header(reinterpret_cast<uint32_t>(image_base_),
+ reinterpret_cast<uint32_t>(GetImageAddress(image_roots)));
memcpy(image_->GetAddress(), &image_header, sizeof(image_header));
// Note that top_ is left at end of used space
@@ -156,6 +171,8 @@
FixupClass(orig->AsClass(), down_cast<Class*>(copy));
} else if (orig->IsObjectArray()) {
FixupObjectArray(orig->AsObjectArray<Object>(), down_cast<ObjectArray<Object>*>(copy));
+ } else if (orig->IsMethod()) {
+ FixupMethod(orig->AsMethod(), down_cast<Method*>(copy));
} else {
FixupInstanceFields(orig, copy);
}
@@ -174,16 +191,22 @@
const void* copy_code = copy_code_array->GetData();
// TODO: remember InstructionSet with each code array so we know if we need to do thumb fixup?
if ((reinterpret_cast<uintptr_t>(orig_code) % 2) == 1) {
- return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(copy_code) + 1);
+ return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(copy_code) + 1);
}
return copy_code;
}
void ImageWriter::FixupMethod(const Method* orig, Method* copy) {
FixupInstanceFields(orig, copy);
- // TODO: convert shorty_ to heap allocated storage
copy->code_ = FixupCode(copy->code_array_, orig->code_);
copy->invoke_stub_ = reinterpret_cast<Method::InvokeStub*>(FixupCode(copy->invoke_stub_array_, reinterpret_cast<void*>(orig->invoke_stub_)));
+ if (orig->IsNative()) {
+ ByteArray* orig_jni_stub_array_ = Runtime::Current()->GetJniStubArray();
+ ByteArray* copy_jni_stub_array_ = down_cast<ByteArray*>(GetImageAddress(orig_jni_stub_array_));
+ copy->native_method_ = copy_jni_stub_array_->GetData();
+ } else {
+ DCHECK(copy->native_method_ == NULL);
+ }
}
void ImageWriter::FixupObjectArray(const ObjectArray<Object>* orig, ObjectArray<Object>* copy) {
diff --git a/src/jni_compiler.cc b/src/jni_compiler.cc
index 11b99c8..eb84f14 100644
--- a/src/jni_compiler.cc
+++ b/src/jni_compiler.cc
@@ -25,7 +25,20 @@
ByteArray* CreateJniStub();
}
-JniCompiler::JniCompiler(InstructionSet insns) : jni_stub_(NULL) {
+ByteArray* JniCompiler::CreateJniStub(InstructionSet instruction_set) {
+ switch (instruction_set) {
+ case kArm:
+ case kThumb2:
+ return arm::CreateJniStub();
+ case kX86:
+ return x86::CreateJniStub();
+ default:
+ LOG(FATAL) << "Unknown InstructionSet " << (int) instruction_set;
+ return NULL;
+ }
+}
+
+JniCompiler::JniCompiler(InstructionSet insns) {
if (insns == kThumb2) {
// currently only ARM code generation is supported
instruction_set_ = kArm;
@@ -36,11 +49,6 @@
JniCompiler::~JniCompiler() {}
-// Return value helper for jobject return types
-static Object* DecodeJObjectInThread(Thread* thread, jobject obj) {
- return thread->DecodeJObject(obj);
-}
-
// Generate the JNI bridge for the given method, general contract:
// - Arguments are in the managed runtime format, either on stack or in
// registers, a reference to the method object is supplied as part of this
@@ -68,17 +76,6 @@
// Cache of IsStatic as we call it often enough
const bool is_static = native_method->IsStatic();
- // 0. native_method->RegisterNative(jni_stub_ stuff). Note that jni_stub_ will invoke dlsym.
- if (jni_stub_ == NULL) {
- if (instruction_set_ == kArm) {
- jni_stub_ = arm::CreateJniStub();
- } else {
- jni_stub_ = x86::CreateJniStub();
- }
- }
- if (!native_method->IsRegistered()) {
- native_method->RegisterNative(jni_stub_->GetData());
- }
// TODO: Need to make sure that the stub is copied into the image. I.e.,
// ByteArray* needs to be reachable either as a root or from the object graph.
@@ -347,14 +344,17 @@
jni_conv->ResetIterator(FrameOffset(out_arg_size));
if (jni_conv->IsCurrentParamInRegister()) {
__ GetCurrentThread(jni_conv->CurrentParamRegister());
+ __ Call(jni_conv->CurrentParamRegister(),
+ Offset(OFFSETOF_MEMBER(Thread, pDecodeJObjectInThread)),
+ jni_conv->InterproceduralScratchRegister());
} else {
__ GetCurrentThread(jni_conv->CurrentParamStackOffset(),
jni_conv->InterproceduralScratchRegister());
+ __ Call(jni_conv->CurrentParamStackOffset(),
+ Offset(OFFSETOF_MEMBER(Thread, pDecodeJObjectInThread)),
+ jni_conv->InterproceduralScratchRegister());
}
- __ Call(reinterpret_cast<uintptr_t>(DecodeJObjectInThread),
- jni_conv->InterproceduralScratchRegister());
-
__ DecreaseFrameSize(out_arg_size);
jni_conv->ResetIterator(FrameOffset(0));
}
diff --git a/src/jni_compiler.h b/src/jni_compiler.h
index 25514b9..8b5f31a 100644
--- a/src/jni_compiler.h
+++ b/src/jni_compiler.h
@@ -25,6 +25,10 @@
void Compile(Method* method);
+ // Stub to perform native method symbol lookup via dlsym
+ // TODO: remove from JniCompiler
+ static ByteArray* CreateJniStub(InstructionSet instruction_set);
+
private:
// Copy a single parameter from the managed to the JNI calling convention
void CopyParameter(Assembler* jni_asm,
@@ -38,8 +42,6 @@
InstructionSet instruction_set_;
- ByteArray* jni_stub_; // Stub to perform native method symbol lookup
-
DISALLOW_COPY_AND_ASSIGN(JniCompiler);
};
diff --git a/src/jni_compiler_test.cc b/src/jni_compiler_test.cc
index 7a5a487..20c403c 100644
--- a/src/jni_compiler_test.cc
+++ b/src/jni_compiler_test.cc
@@ -233,6 +233,10 @@
}
TEST_F(JniCompilerTest, CompileAndRunIntObjectObjectMethod) {
+#if !defined(__arm__)
+ UNIMPLEMENTED(WARNING) << "needs X86Assembler::Call(FrameOffset base, Offset offset, ManagedRegister)";
+ return;
+#endif
SetupForTest(false, "fooIOO",
"(ILjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
reinterpret_cast<void*>(&Java_MyClass_fooIOO));
@@ -332,6 +336,10 @@
TEST_F(JniCompilerTest, CompileAndRunStaticIntObjectObjectMethod) {
+#if !defined(__arm__)
+ UNIMPLEMENTED(WARNING) << "needs X86Assembler::Call(FrameOffset base, Offset offset, ManagedRegister)";
+ return;
+#endif
SetupForTest(true, "fooSIOO",
"(ILjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
reinterpret_cast<void*>(&Java_MyClass_fooSIOO));
@@ -382,6 +390,10 @@
}
TEST_F(JniCompilerTest, CompileAndRunStaticSynchronizedIntObjectObjectMethod) {
+#if !defined(__arm__)
+ UNIMPLEMENTED(WARNING) << "needs X86Assembler::Call(FrameOffset base, Offset offset, ManagedRegister)";
+ return;
+#endif
SetupForTest(true, "fooSSIOO",
"(ILjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
reinterpret_cast<void*>(&Java_MyClass_fooSSIOO));
@@ -534,6 +546,10 @@
}
TEST_F(JniCompilerTest, ReturnGlobalRef) {
+#if !defined(__arm__)
+ UNIMPLEMENTED(WARNING) << "needs X86Assembler::Call(FrameOffset base, Offset offset, ManagedRegister)";
+ return;
+#endif
SetupForTest(false, "fooO", "(Ljava/lang/Object;)Ljava/lang/Object;",
reinterpret_cast<void*>(&Java_MyClass_fooO));
jobject result = env_->CallNonvirtualObjectMethod(jobj_, jklass_, jmethod_, jobj_);
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index 56be2f4..b9b1ab0 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -572,7 +572,7 @@
}
// See section 11.3 "Linking Native Methods" of the JNI spec.
- void* FindNativeMethod(const Method* m) {
+ void* FindNativeMethod(const Method* m, std::string& detail) {
std::string jni_short_name(JniShortName(m));
std::string jni_long_name(JniLongName(m));
const ClassLoader* declaring_class_loader = m->GetDeclaringClass()->GetClassLoader();
@@ -595,12 +595,9 @@
return fn;
}
}
- std::string detail;
detail += "No implementation found for ";
detail += PrettyMethod(m);
LOG(ERROR) << detail;
- Thread::Current()->ThrowNewException("Ljava/lang/UnsatisfiedLinkError;",
- "%s", detail.c_str());
return NULL;
}
@@ -2840,8 +2837,18 @@
CHECK_GE(c->GetStatus(), Class::kStatusInitializing);
}
- MutexLock mu(libraries_lock);
- return libraries->FindNativeMethod(m);
+ std::string detail;
+ void* native_method;
+ {
+ MutexLock mu(libraries_lock);
+ native_method = libraries->FindNativeMethod(m, detail);
+ }
+ // throwing can cause libraries_lock to be reacquired
+ if (native_method == NULL) {
+ Thread::Current()->ThrowNewException("Ljava/lang/UnsatisfiedLinkError;",
+ "%s", detail.c_str());
+ }
+ return native_method;
}
void JavaVMExt::VisitRoots(Heap::RootVisitor* visitor, void* arg) {
diff --git a/src/object.cc b/src/object.cc
index 373c8c2..24fe628 100644
--- a/src/object.cc
+++ b/src/object.cc
@@ -513,6 +513,25 @@
self->PopNativeToManagedRecord(record);
}
+bool Method::IsRegistered() {
+ void* native_method = GetFieldPtr<void*>(OFFSET_OF_OBJECT_MEMBER(Method, native_method_), false);
+ void* jni_stub = Runtime::Current()->GetJniStubArray()->GetData();
+ return native_method != jni_stub;
+}
+
+void Method::RegisterNative(const void* native_method) {
+ CHECK(IsNative());
+ CHECK(native_method != NULL);
+ SetFieldPtr<const void*>(OFFSET_OF_OBJECT_MEMBER(Method, native_method_),
+ native_method, false);
+}
+
+void Method::UnregisterNative() {
+ CHECK(IsNative());
+ // restore stub to lookup native pointer via dlsym
+ RegisterNative(Runtime::Current()->GetJniStubArray()->GetData());
+}
+
void Class::SetStatus(Status new_status) {
CHECK(new_status > GetStatus() || new_status == kStatusError ||
!Runtime::Current()->IsStarted());
@@ -790,7 +809,7 @@
return interface_entry->GetMethodArray()->Get(method->GetMethodIndex());
}
}
- UNIMPLEMENTED(FATAL) << "Need to throw an error of some kind";
+ UNIMPLEMENTED(FATAL) << "Need to throw an error of some kind " << PrettyMethod(method);
return NULL;
}
diff --git a/src/object.h b/src/object.h
index 3d11009..2f9ea33 100644
--- a/src/object.h
+++ b/src/object.h
@@ -892,22 +892,11 @@
return_pc_offset_in_bytes, false);
}
- bool IsRegistered() {
- return GetFieldPtr<const void*>(OFFSET_OF_OBJECT_MEMBER(Method, native_method_), false) != NULL;
- }
+ bool IsRegistered();
- void RegisterNative(const void* native_method) {
- CHECK(IsNative());
- CHECK(native_method != NULL);
- SetFieldPtr<const void*>(OFFSET_OF_OBJECT_MEMBER(Method, native_method_),
- native_method, false);
- }
+ void RegisterNative(const void* native_method);
- void UnregisterNative() {
- CHECK(IsNative());
- SetFieldPtr<const void*>(OFFSET_OF_OBJECT_MEMBER(Method, native_method_),
- NULL, false);
- }
+ void UnregisterNative();
static MemberOffset NativeMethodOffset() {
return OFFSET_OF_OBJECT_MEMBER(Method, native_method_);
diff --git a/src/os.h b/src/os.h
index dda5a30..c36680d 100644
--- a/src/os.h
+++ b/src/os.h
@@ -20,6 +20,9 @@
// Check if a file exists.
static bool FileExists(const char* name);
+
+ // Check if a directory exists.
+ static bool DirectoryExists(const char* name);
};
} // namespace art
diff --git a/src/os_linux.cc b/src/os_linux.cc
index 6d84b51..3fae386 100644
--- a/src/os_linux.cc
+++ b/src/os_linux.cc
@@ -36,4 +36,13 @@
}
}
+bool OS::DirectoryExists(const char* name) {
+ struct stat st;
+ if (stat(name, &st) == 0) {
+ return S_ISDIR(st.st_mode); // TODO: Deal with symlinks?
+ } else {
+ return false;
+ }
+}
+
} // namespace art
diff --git a/src/runtime.cc b/src/runtime.cc
index 0d48aea..c21dcc5 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -30,6 +30,7 @@
class_linker_(NULL),
signal_catcher_(NULL),
java_vm_(NULL),
+ jni_stub_array_(NULL),
started_(false),
vfprintf_(NULL),
exit_(NULL),
@@ -171,7 +172,7 @@
std::vector<std::string> parsed;
Split(class_path, ':', parsed);
for (size_t i = 0; i < parsed.size(); ++i) {
- const DexFile* dex_file = DexFile::Open(parsed[i]);
+ const DexFile* dex_file = DexFile::Open(parsed[i], "");
if (dex_file != NULL) {
class_path_vector.push_back(dex_file);
}
@@ -339,9 +340,8 @@
// Finish attaching the main thread.
Thread* main_thread = Thread::Current();
- main_thread->CreatePeer("main", false);
-
instance_->InitLibraries();
+ main_thread->CreatePeer("main", false);
instance_->signal_catcher_ = new SignalCatcher;
}
@@ -484,6 +484,7 @@
intern_table_->VisitRoots(visitor, arg);
java_vm_->VisitRoots(visitor, arg);
thread_list_->VisitRoots(visitor, arg);
+ visitor(jni_stub_array_, arg);
//(*visitor)(&gDvm.outOfMemoryObj, 0, ROOT_VM_INTERNAL, arg);
//(*visitor)(&gDvm.internalErrorObj, 0, ROOT_VM_INTERNAL, arg);
diff --git a/src/runtime.h b/src/runtime.h
index 5e57018..084e49c 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -20,6 +20,8 @@
namespace art {
+template<class T> class PrimitiveArray;
+typedef PrimitiveArray<int8_t> ByteArray;
class ClassLinker;
class DexFile;
class Heap;
@@ -136,6 +138,21 @@
void VisitRoots(Heap::RootVisitor* visitor, void* arg) const;
+ bool HasJniStubArray() const {
+ return jni_stub_array_ != NULL;
+ }
+
+ ByteArray* GetJniStubArray() const {
+ CHECK(jni_stub_array_ != NULL);
+ return jni_stub_array_;
+ }
+
+ void SetJniStubArray(ByteArray* jni_stub_array) {
+ CHECK(jni_stub_array != NULL);
+ CHECK(jni_stub_array_ == NULL || jni_stub_array_ == jni_stub_array);
+ jni_stub_array_ = jni_stub_array;
+ }
+
private:
static void PlatformAbort(const char*, int);
@@ -164,6 +181,8 @@
JavaVMExt* java_vm_;
+ ByteArray* jni_stub_array_;
+
bool started_;
// Hooks supported by JNI_CreateJavaVM
diff --git a/src/space.cc b/src/space.cc
index 36e3cf2..38c5720 100644
--- a/src/space.cc
+++ b/src/space.cc
@@ -114,6 +114,9 @@
image_header_ = reinterpret_cast<ImageHeader*>(map->GetAddress());
DCHECK_EQ(0, memcmp(&image_header, image_header_, sizeof(ImageHeader)));
+ Object* jni_stub_array = image_header.GetImageRoot(ImageHeader::kJniStubArray);
+ Runtime::Current()->SetJniStubArray(down_cast<ByteArray*>(jni_stub_array));
+
Init(map.release());
return true;
}
diff --git a/src/space.h b/src/space.h
index 92b017a..e95d2b0 100644
--- a/src/space.h
+++ b/src/space.h
@@ -59,7 +59,7 @@
size_t AllocationSize(const Object* obj);
bool IsCondemned() const {
- return true; // TODO
+ return mspace_ != NULL;
}
private:
@@ -97,8 +97,6 @@
byte* limit_;
- // bool is_condemned_; // TODO: with IsCondemned
-
DISALLOW_COPY_AND_ASSIGN(Space);
};
diff --git a/src/stub_arm.cc b/src/stub_arm.cc
index 7939780..0711085 100644
--- a/src/stub_arm.cc
+++ b/src/stub_arm.cc
@@ -51,7 +51,7 @@
__ mov(R0, ShifterOperand(R9));
// Call FindNativeMethod
- __ LoadImmediate(R12, reinterpret_cast<int32_t>(&FindNativeMethod));
+ __ LoadFromOffset(kLoadWord, R12, TR, OFFSETOF_MEMBER(Thread, pFindNativeMethod));
__ blx(R12);
// Save result of FindNativeMethod in R12
diff --git a/src/stub_x86.cc b/src/stub_x86.cc
index ce9a875..fd8d0f2 100644
--- a/src/stub_x86.cc
+++ b/src/stub_x86.cc
@@ -21,6 +21,8 @@
__ pushl(EDI); // Method*
// Call throw_ame to throw AbstractMethodError
+ // TODO: make this PIC (throw_ame will not be in the same location after image load)
+ // TODO: remove X86Assembler::Call(uintptr_t addr, ManagedRegister scratch)
__ Call(reinterpret_cast<int32_t>(throw_ame), X86ManagedRegister::FromCpuRegister(ECX));
// Because the call above never returns, we do not need to do ESP+=16 here.
@@ -48,6 +50,8 @@
__ fs()->movl(ECX, Address::Absolute(Thread::SelfOffset()));
__ pushl(ECX); // Thread*
+ // TODO: make this PIC (FindNativeMethod will not be in the same location after image load)
+ // TODO: remove X86Assembler::Call(uintptr_t addr, ManagedRegister scratch)
__ Call(reinterpret_cast<int32_t>(&FindNativeMethod), X86ManagedRegister::FromCpuRegister(ECX));
__ addl(ESP, Immediate(16));
diff --git a/src/thread.cc b/src/thread.cc
index 426677e..d879cea 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -48,33 +48,6 @@
LOG(INFO) << "Info: " << info;
}
-/*
- * TODO: placeholder for a method that can be called by the
- * invoke-interface trampoline to unwind and handle exception. The
- * trampoline will arrange it so that the caller appears to be the
- * callsite of the failed invoke-interface. See comments in
- * compiler/runtime_support.S
- */
-extern "C" void artFailedInvokeInterface() {
- UNIMPLEMENTED(FATAL) << "Unimplemented exception throw";
-}
-
-// TODO: placeholder. See comments in compiler/runtime_support.S
-extern "C" uint64_t artFindInterfaceMethodInCache(uint32_t method_idx,
- Object* this_object , Method* caller_method)
-{
- /*
- * Note: this_object has not yet been null-checked. To match
- * the old-world state, nullcheck this_object and load
- * Class* this_class = this_object->GetClass().
- * See comments and possible thrown exceptions in old-world
- * Interp.cpp:dvmInterpFindInterfaceMethod, and complete with
- * new-world FindVirtualMethodForInterface.
- */
- UNIMPLEMENTED(FATAL) << "Unimplemented invoke interface";
- return 0LL;
-}
-
// TODO: placeholder. This is what generated code will call to throw
void ThrowException(Thread* thread, Throwable* exception) {
/*
@@ -146,6 +119,7 @@
// TODO: placeholder
void StackOverflowFromCode(Method* method) {
+ Thread::Current()->Dump(std::cerr);
//NOTE: to save code space, this handler needs to look up its own Thread*
UNIMPLEMENTED(FATAL) << "Stack overflow: " << PrettyMethod(method);
}
@@ -220,6 +194,38 @@
(char*)&table[4], size_in_bytes);
}
+/*
+ * TODO: placeholder for a method that can be called by the
+ * invoke-interface trampoline to unwind and handle exception. The
+ * trampoline will arrange it so that the caller appears to be the
+ * callsite of the failed invoke-interface. See comments in
+ * runtime_support.S
+ */
+extern "C" void artFailedInvokeInterface() {
+ UNIMPLEMENTED(FATAL) << "Unimplemented exception throw";
+}
+
+// See comments in runtime_support.S
+extern "C" uint64_t artFindInterfaceMethodInCache(uint32_t method_idx,
+ Object* this_object , Method* caller_method)
+{
+ if (this_object == NULL) {
+ ThrowNullPointerFromCode();
+ }
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ Method* interface_method = class_linker->ResolveMethod(method_idx, caller_method, false);
+ if (interface_method == NULL) {
+ UNIMPLEMENTED(FATAL) << "Could not resolve interface method. Throw error and unwind";
+ }
+ Method* method = this_object->GetClass()->FindVirtualMethodForInterface(interface_method);
+ const void* code = method->GetCode();
+
+ uint32_t method_uint = reinterpret_cast<uint32_t>(method);
+ uint64_t code_uint = reinterpret_cast<uint32_t>(code);
+ uint64_t result = ((code_uint << 32) | method_uint);
+ return result;
+}
+
// TODO: move to more appropriate location
/*
* Float/double conversion requires clamping to min and max of integer form. If
@@ -251,6 +257,11 @@
return (int64_t)f;
}
+// Return value helper for jobject return types
+static Object* DecodeJObjectInThread(Thread* thread, jobject obj) {
+ return thread->DecodeJObject(obj);
+}
+
void Thread::InitFunctionPointers() {
#if defined(__arm__)
pShlLong = art_shl_long;
@@ -312,6 +323,8 @@
pThrowRuntimeExceptionFromCode = ThrowRuntimeExceptionFromCode;
pThrowInternalErrorFromCode = ThrowInternalErrorFromCode;
pThrowNoSuchMethodFromCode = ThrowNoSuchMethodFromCode;
+ pFindNativeMethod = FindNativeMethod;
+ pDecodeJObjectInThread = DecodeJObjectInThread;
pDebugMe = DebugMe;
}
diff --git a/src/thread.h b/src/thread.h
index 8539962..66eba85 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -224,6 +224,8 @@
void (*pThrowRuntimeExceptionFromCode)(int32_t);
void (*pThrowInternalErrorFromCode)(int32_t);
void (*pThrowNoSuchMethodFromCode)(int32_t);
+ void* (*pFindNativeMethod)(Thread* thread);
+ Object* (*pDecodeJObjectInThread)(Thread* thread, jobject obj);
class StackVisitor {
public: