Merge "Optimizing: Improve const-string code generation."
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 504eaa8..fe2abe4 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -5284,6 +5284,12 @@
CpuRegister(ensure_scratch.GetRegister()));
}
+void ParallelMoveResolverX86_64::Exchange64(CpuRegister reg1, CpuRegister reg2) {
+ __ movq(CpuRegister(TMP), reg1);
+ __ movq(reg1, reg2);
+ __ movq(reg2, CpuRegister(TMP));
+}
+
void ParallelMoveResolverX86_64::Exchange64(CpuRegister reg, int mem) {
__ movq(CpuRegister(TMP), Address(CpuRegister(RSP), mem));
__ movq(Address(CpuRegister(RSP), mem), reg);
@@ -5321,7 +5327,7 @@
Location destination = move->GetDestination();
if (source.IsRegister() && destination.IsRegister()) {
- __ xchgq(destination.AsRegister<CpuRegister>(), source.AsRegister<CpuRegister>());
+ Exchange64(source.AsRegister<CpuRegister>(), destination.AsRegister<CpuRegister>());
} else if (source.IsRegister() && destination.IsStackSlot()) {
Exchange32(source.AsRegister<CpuRegister>(), destination.GetStackIndex());
} else if (source.IsStackSlot() && destination.IsRegister()) {
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index fdcf3c3..7ebce58 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -142,6 +142,7 @@
void Exchange32(CpuRegister reg, int mem);
void Exchange32(XmmRegister reg, int mem);
void Exchange32(int mem1, int mem2);
+ void Exchange64(CpuRegister reg1, CpuRegister reg2);
void Exchange64(CpuRegister reg, int mem);
void Exchange64(XmmRegister reg, int mem);
void Exchange64(int mem1, int mem2);
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index bcdd3e7..fe47f7d 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -178,41 +178,47 @@
: nullptr),
indent_(0) {}
+ void Flush() {
+ // We use "\n" instead of std::endl to avoid implicit flushing which
+ // generates too many syscalls during debug-GC tests (b/27826765).
+ output_ << std::flush;
+ }
+
void StartTag(const char* name) {
AddIndent();
- output_ << "begin_" << name << std::endl;
+ output_ << "begin_" << name << "\n";
indent_++;
}
void EndTag(const char* name) {
indent_--;
AddIndent();
- output_ << "end_" << name << std::endl;
+ output_ << "end_" << name << "\n";
}
void PrintProperty(const char* name, const char* property) {
AddIndent();
- output_ << name << " \"" << property << "\"" << std::endl;
+ output_ << name << " \"" << property << "\"\n";
}
void PrintProperty(const char* name, const char* property, int id) {
AddIndent();
- output_ << name << " \"" << property << id << "\"" << std::endl;
+ output_ << name << " \"" << property << id << "\"\n";
}
void PrintEmptyProperty(const char* name) {
AddIndent();
- output_ << name << std::endl;
+ output_ << name << "\n";
}
void PrintTime(const char* name) {
AddIndent();
- output_ << name << " " << time(nullptr) << std::endl;
+ output_ << name << " " << time(nullptr) << "\n";
}
void PrintInt(const char* name, int value) {
AddIndent();
- output_ << name << " " << value << std::endl;
+ output_ << name << " " << value << "\n";
}
void AddIndent() {
@@ -249,7 +255,7 @@
if (block->IsEntryBlock() && (disasm_info_ != nullptr)) {
output_ << " \"" << kDisassemblyBlockFrameEntry << "\" ";
}
- output_<< std::endl;
+ output_<< "\n";
}
void PrintSuccessors(HBasicBlock* block) {
@@ -258,7 +264,7 @@
for (HBasicBlock* successor : block->GetNormalSuccessors()) {
output_ << " \"B" << successor->GetBlockId() << "\" ";
}
- output_<< std::endl;
+ output_<< "\n";
}
void PrintExceptionHandlers(HBasicBlock* block) {
@@ -272,7 +278,7 @@
!disasm_info_->GetSlowPathIntervals().empty()) {
output_ << " \"" << kDisassemblyBlockSlowPaths << "\" ";
}
- output_<< std::endl;
+ output_<< "\n";
}
void DumpLocation(std::ostream& stream, const Location& location) {
@@ -592,7 +598,7 @@
auto it = disasm_info_->GetInstructionIntervals().find(instruction);
if (it != disasm_info_->GetInstructionIntervals().end()
&& it->second.start != it->second.end) {
- output_ << std::endl;
+ output_ << "\n";
disassembler_->Disassemble(output_, it->second.start, it->second.end);
}
}
@@ -612,7 +618,7 @@
output_ << bci << " " << num_uses << " "
<< GetTypeId(instruction->GetType()) << instruction->GetId() << " ";
PrintInstruction(instruction);
- output_ << " " << kEndInstructionMarker << std::endl;
+ output_ << " " << kEndInstructionMarker << "\n";
}
}
@@ -656,10 +662,10 @@
output_ << " 0 0 disasm " << kDisassemblyBlockFrameEntry << " ";
GeneratedCodeInterval frame_entry = disasm_info_->GetFrameEntryInterval();
if (frame_entry.start != frame_entry.end) {
- output_ << std::endl;
+ output_ << "\n";
disassembler_->Disassemble(output_, frame_entry.start, frame_entry.end);
}
- output_ << kEndInstructionMarker << std::endl;
+ output_ << kEndInstructionMarker << "\n";
DumpEndOfDisassemblyBlock();
}
@@ -675,9 +681,9 @@
GetGraph()->HasExitBlock() ? GetGraph()->GetExitBlock()->GetBlockId() : -1,
-1);
for (SlowPathCodeInfo info : disasm_info_->GetSlowPathIntervals()) {
- output_ << " 0 0 disasm " << info.slow_path->GetDescription() << std::endl;
+ output_ << " 0 0 disasm " << info.slow_path->GetDescription() << "\n";
disassembler_->Disassemble(output_, info.code_interval.start, info.code_interval.end);
- output_ << kEndInstructionMarker << std::endl;
+ output_ << kEndInstructionMarker << "\n";
}
DumpEndOfDisassemblyBlock();
}
@@ -698,6 +704,7 @@
DumpDisassemblyBlockForSlowPaths();
}
EndTag("cfg");
+ Flush();
}
void VisitBasicBlock(HBasicBlock* block) OVERRIDE {
@@ -737,7 +744,7 @@
for (HInputIterator inputs(instruction); !inputs.Done(); inputs.Advance()) {
output_ << inputs.Current()->GetId() << " ";
}
- output_ << "]" << std::endl;
+ output_ << "]\n";
}
EndTag("locals");
EndTag("states");
@@ -779,6 +786,7 @@
printer.PrintProperty("method", method_name);
printer.PrintTime("date");
printer.EndTag("compilation");
+ printer.Flush();
}
void HGraphVisualizer::DumpGraph(const char* pass_name,
diff --git a/oatdump/oatdump_test.cc b/oatdump/oatdump_test.cc
index 3e420ad..5407d3a 100644
--- a/oatdump/oatdump_test.cc
+++ b/oatdump/oatdump_test.cc
@@ -84,8 +84,8 @@
std::string core_oat_location_;
};
-// Disable tests on arm as they are taking too long to run for hammerhead. b/27824283.
-#ifndef __arm__
+// Disable tests on arm and mips as they are taking too long to run. b/27824283.
+#if !defined(__arm__) && !defined(__mips__)
TEST_F(OatDumpTest, TestImage) {
std::string error_msg;
ASSERT_TRUE(Exec(kModeArt, {}, &error_msg)) << error_msg;
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index 60caa73..e63eaa2 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -506,8 +506,8 @@
return false;
}
-uint32_t DexFile::GetVersion() const {
- const char* version = reinterpret_cast<const char*>(&GetHeader().magic_[sizeof(kDexMagic)]);
+uint32_t DexFile::Header::GetVersion() const {
+ const char* version = reinterpret_cast<const char*>(&magic_[sizeof(kDexMagic)]);
return atoi(version);
}
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index 6849984..1456636 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -107,6 +107,9 @@
uint32_t data_size_; // unused
uint32_t data_off_; // unused
+ // Decode the dex magic version
+ uint32_t GetVersion() const;
+
private:
DISALLOW_COPY_AND_ASSIGN(Header);
};
@@ -479,7 +482,9 @@
}
// Decode the dex magic version
- uint32_t GetVersion() const;
+ uint32_t GetVersion() const {
+ return GetHeader().GetVersion();
+ }
// Returns true if the byte string points to the magic value.
static bool IsMagicValid(const uint8_t* magic);
diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc
index 9c9b8c5..811e763 100644
--- a/runtime/dex_file_verifier.cc
+++ b/runtime/dex_file_verifier.cc
@@ -2465,7 +2465,13 @@
GetFieldDescriptionOrError(begin_, header_, idx).c_str(),
field_access_flags,
PrettyJavaAccessFlags(field_access_flags).c_str());
- return false;
+ if (header_->GetVersion() >= 37) {
+ return false;
+ } else {
+ // Allow in older versions, but warn.
+ LOG(WARNING) << "This dex file is invalid and will be rejected in the future. Error is: "
+ << *error_msg;
+ }
}
// Interface fields may be synthetic, but may not have other flags.
constexpr uint32_t kDisallowed = ~(kPublicFinalStatic | kAccSynthetic);
@@ -2474,7 +2480,13 @@
GetFieldDescriptionOrError(begin_, header_, idx).c_str(),
field_access_flags,
PrettyJavaAccessFlags(field_access_flags).c_str());
- return false;
+ if (header_->GetVersion() >= 37) {
+ return false;
+ } else {
+ // Allow in older versions, but warn.
+ LOG(WARNING) << "This dex file is invalid and will be rejected in the future. Error is: "
+ << *error_msg;
+ }
}
return true;
}
@@ -2657,7 +2669,13 @@
*error_msg = StringPrintf("Interface method %" PRIu32 "(%s) is not public and abstract",
method_index,
GetMethodDescriptionOrError(begin_, header_, method_index).c_str());
- return false;
+ if (header_->GetVersion() >= 37) {
+ return false;
+ } else {
+ // Allow in older versions, but warn.
+ LOG(WARNING) << "This dex file is invalid and will be rejected in the future. Error is: "
+ << *error_msg;
+ }
}
// At this point, we know the method is public and abstract. This means that all the checks
// for invalid combinations above applies. In addition, interface methods must not be
diff --git a/runtime/dex_file_verifier_test.cc b/runtime/dex_file_verifier_test.cc
index 44cf2ee..4a5ed5d 100644
--- a/runtime/dex_file_verifier_test.cc
+++ b/runtime/dex_file_verifier_test.cc
@@ -725,6 +725,13 @@
return result;
}
+// Make the Dex file version 37.
+static void MakeDexVersion37(DexFile* dex_file) {
+ size_t offset = OFFSETOF_MEMBER(DexFile::Header, magic_) + 6;
+ CHECK_EQ(*(dex_file->Begin() + offset), '5');
+ *(const_cast<uint8_t*>(dex_file->Begin()) + offset) = '7';
+}
+
TEST_F(DexFileVerifierTest, MethodAccessFlagsInterfaces) {
VerifyModification(
kMethodFlagsInterface,
@@ -733,6 +740,14 @@
ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
},
nullptr);
+ VerifyModification(
+ kMethodFlagsInterface,
+ "method_flags_interface_ok37",
+ [](DexFile* dex_file) {
+ MakeDexVersion37(dex_file);
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ },
+ nullptr);
VerifyModification(
kMethodFlagsInterface,
@@ -742,7 +757,18 @@
ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic);
},
+ nullptr); // Should be allowed in older dex versions for backwards compatibility.
+ VerifyModification(
+ kMethodFlagsInterface,
+ "method_flags_interface_non_public",
+ [](DexFile* dex_file) {
+ MakeDexVersion37(dex_file);
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic);
+ },
"Interface method 1(LInterfaceMethodFlags;.foo) is not public and abstract");
+
VerifyModification(
kMethodFlagsInterface,
"method_flags_interface_non_abstract",
@@ -781,7 +807,18 @@
ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic);
},
+ nullptr); // Should be allowed in older dex versions for backwards compatibility.
+ VerifyModification(
+ kMethodFlagsInterface,
+ "method_flags_interface_non_public",
+ [](DexFile* dex_file) {
+ MakeDexVersion37(dex_file);
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic);
+ },
"Interface method 1(LInterfaceMethodFlags;.foo) is not public and abstract");
+
VerifyModification(
kMethodFlagsInterface,
"method_flags_interface_protected",
@@ -791,6 +828,17 @@
ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic);
OrMaskToMethodFlags(dex_file, "foo", kAccProtected);
},
+ nullptr); // Should be allowed in older dex versions for backwards compatibility.
+ VerifyModification(
+ kMethodFlagsInterface,
+ "method_flags_interface_protected",
+ [](DexFile* dex_file) {
+ MakeDexVersion37(dex_file);
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic);
+ OrMaskToMethodFlags(dex_file, "foo", kAccProtected);
+ },
"Interface method 1(LInterfaceMethodFlags;.foo) is not public and abstract");
constexpr uint32_t kAllMethodFlags =
@@ -1070,6 +1118,14 @@
ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
},
nullptr);
+ VerifyModification(
+ kFieldFlagsInterfaceTestDex,
+ "field_flags_interface",
+ [](DexFile* dex_file) {
+ MakeDexVersion37(dex_file);
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ },
+ nullptr);
VerifyModification(
kFieldFlagsInterfaceTestDex,
@@ -1079,7 +1135,18 @@
ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic);
},
+ nullptr); // Should be allowed in older dex versions for backwards compatibility.
+ VerifyModification(
+ kFieldFlagsInterfaceTestDex,
+ "field_flags_interface_non_public",
+ [](DexFile* dex_file) {
+ MakeDexVersion37(dex_file);
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic);
+ },
"Interface field is not public final static");
+
VerifyModification(
kFieldFlagsInterfaceTestDex,
"field_flags_interface_non_final",
@@ -1088,7 +1155,18 @@
ApplyMaskToFieldFlags(dex_file, "foo", ~kAccFinal);
},
+ nullptr); // Should be allowed in older dex versions for backwards compatibility.
+ VerifyModification(
+ kFieldFlagsInterfaceTestDex,
+ "field_flags_interface_non_final",
+ [](DexFile* dex_file) {
+ MakeDexVersion37(dex_file);
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccFinal);
+ },
"Interface field is not public final static");
+
VerifyModification(
kFieldFlagsInterfaceTestDex,
"field_flags_interface_protected",
@@ -1098,7 +1176,19 @@
ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic);
OrMaskToFieldFlags(dex_file, "foo", kAccProtected);
},
+ nullptr); // Should be allowed in older dex versions for backwards compatibility.
+ VerifyModification(
+ kFieldFlagsInterfaceTestDex,
+ "field_flags_interface_protected",
+ [](DexFile* dex_file) {
+ MakeDexVersion37(dex_file);
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic);
+ OrMaskToFieldFlags(dex_file, "foo", kAccProtected);
+ },
"Interface field is not public final static");
+
VerifyModification(
kFieldFlagsInterfaceTestDex,
"field_flags_interface_private",
@@ -1108,6 +1198,17 @@
ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic);
OrMaskToFieldFlags(dex_file, "foo", kAccPrivate);
},
+ nullptr); // Should be allowed in older dex versions for backwards compatibility.
+ VerifyModification(
+ kFieldFlagsInterfaceTestDex,
+ "field_flags_interface_private",
+ [](DexFile* dex_file) {
+ MakeDexVersion37(dex_file);
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic);
+ OrMaskToFieldFlags(dex_file, "foo", kAccPrivate);
+ },
"Interface field is not public final static");
VerifyModification(
@@ -1152,6 +1253,21 @@
}
OrMaskToFieldFlags(dex_file, "foo", mask);
},
+ nullptr); // Should be allowed in older dex versions for backwards compatibility.
+ VerifyModification(
+ kFieldFlagsInterfaceTestDex,
+ "field_flags_interface_disallowed",
+ [&](DexFile* dex_file) {
+ MakeDexVersion37(dex_file);
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+
+ uint32_t mask = ApplyMaskShifted(kInterfaceDisallowed, i);
+ if ((mask & kAccProtected) != 0) {
+ mask &= ~kAccProtected;
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic);
+ }
+ OrMaskToFieldFlags(dex_file, "foo", mask);
+ },
"Interface field has disallowed flag");
}
}
@@ -1180,6 +1296,14 @@
[](DexFile* dex_file) {
ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
},
+ nullptr); // Should be allowed in older dex versions for backwards compatibility.
+ VerifyModification(
+ kFieldFlagsInterfaceBadTestDex,
+ "field_flags_interface_non_static",
+ [](DexFile* dex_file) {
+ MakeDexVersion37(dex_file);
+ ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
+ },
"Interface field is not public final static");
}
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 3397989..1e4b35f 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -21,6 +21,7 @@
#include "base/stringprintf.h"
#include "class_linker.h"
#include "common_throws.h"
+#include "compiler_filter.h"
#include "dex_file-inl.h"
#include "jni_internal.h"
#include "mirror/class_loader.h"
@@ -390,7 +391,8 @@
jclass,
jstring javaFilename,
jstring javaInstructionSet,
- jint javaTargetCompilationTypeMask) {
+ jstring javaTargetCompilerFilter,
+ jboolean newProfile) {
ScopedUtfChars filename(env, javaFilename);
if (env->ExceptionCheck()) {
return -1;
@@ -401,18 +403,16 @@
return -1;
}
- // TODO: Take profile changed and compiler filter as arguments.
- // For now, we use "speed" by default, unless EXTRACT_ONLY = 0x4 was
- // included in the mask.
- const char* compiler_filter = "speed";
- if (javaTargetCompilationTypeMask & 0x4) {
- compiler_filter = "verify-at-runtime";
+ ScopedUtfChars target_compiler_filter(env, javaTargetCompilerFilter);
+ if (env->ExceptionCheck()) {
+ return -1;
}
+
return GetDexOptNeeded(env,
filename.c_str(),
instruction_set.c_str(),
- compiler_filter,
- /*profile_changed*/false);
+ target_compiler_filter.c_str(),
+ newProfile == JNI_TRUE);
}
// public API
@@ -428,6 +428,34 @@
return (status != OatFileAssistant::kNoDexOptNeeded) ? JNI_TRUE : JNI_FALSE;
}
+static jboolean DexFile_isValidCompilerFilter(JNIEnv* env,
+ jclass javeDexFileClass ATTRIBUTE_UNUSED,
+ jstring javaCompilerFilter) {
+ ScopedUtfChars compiler_filter(env, javaCompilerFilter);
+ if (env->ExceptionCheck()) {
+ return -1;
+ }
+
+ CompilerFilter::Filter filter;
+ return CompilerFilter::ParseCompilerFilter(compiler_filter.c_str(), &filter)
+ ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean DexFile_isProfileGuidedCompilerFilter(JNIEnv* env,
+ jclass javeDexFileClass ATTRIBUTE_UNUSED,
+ jstring javaCompilerFilter) {
+ ScopedUtfChars compiler_filter(env, javaCompilerFilter);
+ if (env->ExceptionCheck()) {
+ return -1;
+ }
+
+ CompilerFilter::Filter filter;
+ if (!CompilerFilter::ParseCompilerFilter(compiler_filter.c_str(), &filter)) {
+ return JNI_FALSE;
+ }
+ return CompilerFilter::DependsOnProfile(filter) ? JNI_TRUE : JNI_FALSE;
+}
+
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(DexFile, closeDexFile, "(Ljava/lang/Object;)Z"),
NATIVE_METHOD(DexFile,
@@ -440,7 +468,7 @@
NATIVE_METHOD(DexFile, getClassNameList, "(Ljava/lang/Object;)[Ljava/lang/String;"),
NATIVE_METHOD(DexFile, isDexOptNeeded, "(Ljava/lang/String;)Z"),
NATIVE_METHOD(DexFile, getDexOptNeeded,
- "(Ljava/lang/String;Ljava/lang/String;I)I"),
+ "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)I"),
NATIVE_METHOD(DexFile, openDexFileNative,
"(Ljava/lang/String;"
"Ljava/lang/String;"
@@ -448,6 +476,8 @@
"Ljava/lang/ClassLoader;"
"[Ldalvik/system/DexPathList$Element;"
")Ljava/lang/Object;"),
+ NATIVE_METHOD(DexFile, isValidCompilerFilter, "(Ljava/lang/String;)Z"),
+ NATIVE_METHOD(DexFile, isProfileGuidedCompilerFilter, "(Ljava/lang/String;)Z"),
};
void register_dalvik_system_DexFile(JNIEnv* env) {
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 3d5f84e..83da6b7 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -724,13 +724,15 @@
// If there aren't any instructions, make sure that's expected, then exit successfully.
if (code_item_ == nullptr) {
+ // Only native or abstract methods may not have code.
+ if ((method_access_flags_ & (kAccNative | kAccAbstract)) == 0) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "zero-length code in concrete non-native method";
+ return false;
+ }
+
// This should have been rejected by the dex file verifier. Only do in debug build.
+ // Note: the above will also be rejected in the dex file verifier, starting in dex version 37.
if (kIsDebugBuild) {
- // Only native or abstract methods may not have code.
- if ((method_access_flags_ & (kAccNative | kAccAbstract)) == 0) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "zero-length code in concrete non-native method";
- return false;
- }
if ((method_access_flags_ & kAccAbstract) != 0) {
// Abstract methods are not allowed to have the following flags.
static constexpr uint32_t kForbidden =