diff options
author | 2022-12-12 09:12:21 +0000 | |
---|---|---|
committer | 2022-12-12 15:57:57 +0000 | |
commit | a20ec9bb10d7ded8ae7d95d4de1e190d22260c73 (patch) | |
tree | 50b5a757c4aa9f5aa7c663d47d96474f538476a2 | |
parent | b9df137d1dbdab1fdca13bb459705b6603e9328b (diff) |
Fix clinit debug check in instrumentation.
Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing --jit --interpreter
Change-Id: Idae620b0b8f9ed67574f5b785fdac3efcb534cfd
-rw-r--r-- | dex2oat/linker/image_writer.cc | 7 | ||||
-rw-r--r-- | runtime/art_method-inl.h | 26 | ||||
-rw-r--r-- | runtime/art_method.h | 26 | ||||
-rw-r--r-- | runtime/class_linker.cc | 8 | ||||
-rw-r--r-- | runtime/common_dex_operations.h | 8 | ||||
-rw-r--r-- | runtime/entrypoints/entrypoint_utils-inl.h | 7 | ||||
-rw-r--r-- | runtime/entrypoints/entrypoint_utils.h | 4 | ||||
-rw-r--r-- | runtime/entrypoints/jni/jni_entrypoints.cc | 2 | ||||
-rw-r--r-- | runtime/entrypoints/quick/quick_trampoline_entrypoints.cc | 28 | ||||
-rw-r--r-- | runtime/instrumentation.cc | 10 | ||||
-rw-r--r-- | runtime/jit/jit.cc | 3 | ||||
-rw-r--r-- | runtime/jit/jit_code_cache.cc | 43 | ||||
-rw-r--r-- | runtime/jni/java_vm_ext.cc | 2 | ||||
-rw-r--r-- | runtime/runtime.cc | 2 |
14 files changed, 95 insertions, 81 deletions
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc index 54f0d1142e..2c3c2206bf 100644 --- a/dex2oat/linker/image_writer.cc +++ b/dex2oat/linker/image_writer.cc @@ -3342,8 +3342,7 @@ const uint8_t* ImageWriter::GetQuickCode(ArtMethod* method, const ImageInfo& ima quick_code = GetOatAddressForOffset(quick_oat_code_offset, image_info); } - bool needs_clinit_check = NeedsClinitCheckBeforeCall(method) && - !method->GetDeclaringClass<kWithoutReadBarrier>()->IsVisiblyInitialized(); + bool still_needs_clinit_check = method->StillNeedsClinitCheck<kWithoutReadBarrier>(); if (quick_code == nullptr) { // If we don't have code, use generic jni / interpreter. @@ -3353,7 +3352,7 @@ const uint8_t* ImageWriter::GetQuickCode(ArtMethod* method, const ImageInfo& ima } else if (CanMethodUseNterp(method, compiler_options_.GetInstructionSet())) { // The nterp trampoline doesn't do initialization checks, so install the // resolution stub if needed. - if (needs_clinit_check) { + if (still_needs_clinit_check) { quick_code = GetOatAddress(StubType::kQuickResolutionTrampoline); } else { quick_code = GetOatAddress(StubType::kNterpTrampoline); @@ -3362,7 +3361,7 @@ const uint8_t* ImageWriter::GetQuickCode(ArtMethod* method, const ImageInfo& ima // The interpreter brige performs class initialization check if needed. quick_code = GetOatAddress(StubType::kQuickToInterpreterBridge); } - } else if (needs_clinit_check && !compiler_options_.ShouldCompileWithClinitCheck(method)) { + } else if (still_needs_clinit_check && !compiler_options_.ShouldCompileWithClinitCheck(method)) { // If we do have code but the method needs a class initialization check before calling // that code, install the resolution stub that will perform the check. quick_code = GetOatAddress(StubType::kQuickResolutionTrampoline); diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index 6e051424bd..b1d8a4ef54 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -669,6 +669,32 @@ inline void ArtMethod::UpdateEntrypoints(const Visitor& visitor, PointerSize poi } } +template <ReadBarrierOption kReadBarrierOption> +inline bool ArtMethod::StillNeedsClinitCheck() { + if (!NeedsClinitCheckBeforeCall()) { + return false; + } + ObjPtr<mirror::Class> klass = GetDeclaringClass<kReadBarrierOption>(); + return !klass->IsVisiblyInitialized(); +} + +inline bool ArtMethod::StillNeedsClinitCheckMayBeDead() { + if (!NeedsClinitCheckBeforeCall()) { + return false; + } + // To avoid resurrecting an unreachable object, or crashing the GC in some GC phases, + // we must not use a full read barrier. Therefore we read the declaring class without + // a read barrier and check if it's already marked. If yes, we check the status of the + // to-space class object as intended. Otherwise, there is no to-space object and the + // from-space class object contains the most recent value of the status field; even if + // this races with another thread doing a read barrier and updating the status, that's + // no different from a race with a thread that just updates the status. + ObjPtr<mirror::Class> klass = GetDeclaringClass<kWithoutReadBarrier>(); + ObjPtr<mirror::Class> marked = ReadBarrier::IsMarked(klass.Ptr()); + ObjPtr<mirror::Class> checked_klass = (marked != nullptr) ? marked : klass; + return !checked_klass->IsVisiblyInitialized(); +} + inline CodeItemInstructionAccessor ArtMethod::DexInstructions() { return CodeItemInstructionAccessor(*GetDexFile(), GetCodeItem()); } diff --git a/runtime/art_method.h b/runtime/art_method.h index 9e55f38779..e6dcb43ed0 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -711,6 +711,32 @@ class ArtMethod final { typename detail::HandleShortyTraits<ArgType>::Type... args) REQUIRES_SHARED(Locks::mutator_lock_); + // Returns true if the method needs a class initialization check according to access flags. + // Only static methods other than the class initializer need this check. + // The caller is responsible for performing the actual check. + bool NeedsClinitCheckBeforeCall() const { + return NeedsClinitCheckBeforeCall(GetAccessFlags()); + } + + static bool NeedsClinitCheckBeforeCall(uint32_t access_flags) { + // The class initializer is special as it is invoked during initialization + // and does not need the check. + return IsStatic(access_flags) && !IsConstructor(access_flags); + } + + // Check if the method needs a class initialization check before call + // and its declaring class is not yet visibly initialized. + // (The class needs to be visibly initialized before we can use entrypoints + // to compiled code for static methods. See b/18161648 .) + template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier> + bool StillNeedsClinitCheck() REQUIRES_SHARED(Locks::mutator_lock_); + + // Similar to `StillNeedsClinitCheck()` but the method's declaring class may + // be dead but not yet reclaimed by the GC, so we cannot do a full read barrier + // but we still want to check the class status in the to-space class if any. + // Note: JIT can hold and use such methods during managed heap GC. + bool StillNeedsClinitCheckMayBeDead() REQUIRES_SHARED(Locks::mutator_lock_); + const void* GetEntryPointFromQuickCompiledCode() const { return GetEntryPointFromQuickCompiledCodePtrSize(kRuntimePointerSize); } diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 08131c5d88..07300d2733 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -2128,8 +2128,7 @@ bool ClassLinker::AddImageSpace( // Set image methods' entry point that point to the nterp trampoline to the // nterp entry point. This allows taking the fast path when doing a // nterp->nterp call. - DCHECK_IMPLIES(NeedsClinitCheckBeforeCall(&method), - method.GetDeclaringClass()->IsVisiblyInitialized()); + DCHECK(!method.StillNeedsClinitCheck()); method.SetEntryPointFromQuickCompiledCode(interpreter::GetNterpEntryPoint()); } else { method.SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge()); @@ -3504,10 +3503,9 @@ void ClassLinker::FixupStaticTrampolines(Thread* self, ObjPtr<mirror::Class> kla instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation(); for (size_t method_index = 0; method_index < num_direct_methods; ++method_index) { ArtMethod* method = klass->GetDirectMethod(method_index, pointer_size); - if (!NeedsClinitCheckBeforeCall(method)) { - continue; + if (method->NeedsClinitCheckBeforeCall()) { + instrumentation->UpdateMethodsCode(method, instrumentation->GetCodeForInvoke(method)); } - instrumentation->UpdateMethodsCode(method, instrumentation->GetCodeForInvoke(method)); } // Ignore virtual methods on the iterator. } diff --git a/runtime/common_dex_operations.h b/runtime/common_dex_operations.h index b8d9cbe141..0b9d3fbb53 100644 --- a/runtime/common_dex_operations.h +++ b/runtime/common_dex_operations.h @@ -61,18 +61,14 @@ namespace interpreter { inline bool EnsureInitialized(Thread* self, ShadowFrame* shadow_frame) REQUIRES_SHARED(Locks::mutator_lock_) { - if (!NeedsClinitCheckBeforeCall(shadow_frame->GetMethod())) { - return true; - } - ObjPtr<mirror::Class> declaring_class = shadow_frame->GetMethod()->GetDeclaringClass(); - if (LIKELY(declaring_class->IsVisiblyInitialized())) { + if (LIKELY(!shadow_frame->GetMethod()->StillNeedsClinitCheck())) { return true; } // Save the shadow frame. ScopedStackedShadowFramePusher pusher(self, shadow_frame); StackHandleScope<1> hs(self); - Handle<mirror::Class> h_class(hs.NewHandle(declaring_class)); + Handle<mirror::Class> h_class = hs.NewHandle(shadow_frame->GetMethod()->GetDeclaringClass()); if (UNLIKELY(!Runtime::Current()->GetClassLinker()->EnsureInitialized( self, h_class, /*can_init_fields=*/ true, /*can_init_parents=*/ true))) { DCHECK(self->IsExceptionPending()); diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h index e2aa81f837..91163f4139 100644 --- a/runtime/entrypoints/entrypoint_utils-inl.h +++ b/runtime/entrypoints/entrypoint_utils-inl.h @@ -715,13 +715,6 @@ inline INT_TYPE art_float_to_integral(FLOAT_TYPE f) { } } -inline bool NeedsClinitCheckBeforeCall(ArtMethod* method) { - // The class needs to be visibly initialized before we can use entrypoints to - // compiled code for static methods. See b/18161648 . The class initializer is - // special as it is invoked during initialization and does not need the check. - return method->IsStatic() && !method->IsConstructor(); -} - inline ObjPtr<mirror::Object> GetGenericJniSynchronizationObject(Thread* self, ArtMethod* called) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(!called->IsCriticalNative()); diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h index ae5687506a..777fd9880d 100644 --- a/runtime/entrypoints/entrypoint_utils.h +++ b/runtime/entrypoints/entrypoint_utils.h @@ -204,10 +204,6 @@ CallerAndOuterMethod GetCalleeSaveMethodCallerAndOuterMethod(Thread* self, Calle ArtMethod* GetCalleeSaveOuterMethod(Thread* self, CalleeSaveType type) REQUIRES_SHARED(Locks::mutator_lock_); -// Returns whether we need to do class initialization check before invoking the method. -// The caller is responsible for performing that check. -bool NeedsClinitCheckBeforeCall(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_); - // Returns the synchronization object for a native method for a GenericJni frame // we have just created or are about to exit. The synchronization object is // the class object for static methods and the `this` object otherwise. diff --git a/runtime/entrypoints/jni/jni_entrypoints.cc b/runtime/entrypoints/jni/jni_entrypoints.cc index 80eb89f4e3..e606c2173a 100644 --- a/runtime/entrypoints/jni/jni_entrypoints.cc +++ b/runtime/entrypoints/jni/jni_entrypoints.cc @@ -75,7 +75,7 @@ extern "C" const void* artFindNativeMethodRunnable(Thread* self) // These calls do not have an explicit class initialization check, so do the check now. // (When going through the stub or GenericJNI, the check was already done.) - DCHECK(NeedsClinitCheckBeforeCall(target_method)); + DCHECK(target_method->NeedsClinitCheckBeforeCall()); ObjPtr<mirror::Class> declaring_class = target_method->GetDeclaringClass(); if (UNLIKELY(!declaring_class->IsVisiblyInitialized())) { StackHandleScope<1> hs(self); diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 87ef22a706..9475414f4e 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -1036,10 +1036,10 @@ extern "C" const void* artInstrumentationMethodEntryFromCode(ArtMethod* method, StackHandleScope<2> hs(self); Handle<mirror::Object> h_object(hs.NewHandle(is_static ? nullptr : this_object)); - Handle<mirror::Class> h_class(hs.NewHandle(method->GetDeclaringClass())); // Ensure that the called method's class is initialized. - if (NeedsClinitCheckBeforeCall(method) && !h_class->IsVisiblyInitialized()) { + if (method->StillNeedsClinitCheck()) { + Handle<mirror::Class> h_class = hs.NewHandle(method->GetDeclaringClass()); if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(self, h_class, true, true)) { visitor.FixupReferences(); DCHECK(self->IsExceptionPending()); @@ -1354,11 +1354,10 @@ extern "C" const void* artQuickResolutionTrampoline( // Static invokes need class initialization check but instance invokes can proceed even if // the class is erroneous, i.e. in the edge case of escaping instances of erroneous classes. bool success = true; - ObjPtr<mirror::Class> called_class = called->GetDeclaringClass(); - if (NeedsClinitCheckBeforeCall(called) && !called_class->IsVisiblyInitialized()) { + if (called->StillNeedsClinitCheck()) { // Ensure that the called method's class is initialized. StackHandleScope<1> hs(soa.Self()); - HandleWrapperObjPtr<mirror::Class> h_called_class(hs.NewHandleWrapper(&called_class)); + Handle<mirror::Class> h_called_class = hs.NewHandle(called->GetDeclaringClass()); success = linker->EnsureInitialized(soa.Self(), h_called_class, true, true); } if (success) { @@ -1373,7 +1372,7 @@ extern "C" const void* artQuickResolutionTrampoline( // stub. code = instrumentation->GetMaybeInstrumentedCodeForInvoke(called); } else { - DCHECK(called_class->IsErroneous()); + DCHECK(called->GetDeclaringClass()->IsErroneous()); DCHECK(self->IsExceptionPending()); } } @@ -2092,16 +2091,13 @@ extern "C" const void* artQuickGenericJniTrampoline(Thread* self, // We can set the entrypoint of a native method to generic JNI even when the // class hasn't been initialized, so we need to do the initialization check // before invoking the native code. - if (NeedsClinitCheckBeforeCall(called)) { - ObjPtr<mirror::Class> declaring_class = called->GetDeclaringClass(); - if (UNLIKELY(!declaring_class->IsVisiblyInitialized())) { - // Ensure static method's class is initialized. - StackHandleScope<1> hs(self); - Handle<mirror::Class> h_class(hs.NewHandle(declaring_class)); - if (!runtime->GetClassLinker()->EnsureInitialized(self, h_class, true, true)) { - DCHECK(Thread::Current()->IsExceptionPending()) << called->PrettyMethod(); - return nullptr; // Report error. - } + if (called->StillNeedsClinitCheck()) { + // Ensure static method's class is initialized. + StackHandleScope<1> hs(self); + Handle<mirror::Class> h_class = hs.NewHandle(called->GetDeclaringClass()); + if (!runtime->GetClassLinker()->EnsureInitialized(self, h_class, true, true)) { + DCHECK(Thread::Current()->IsExceptionPending()) << called->PrettyMethod(); + return nullptr; // Report error. } } diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index 6c68cdb701..1d0e22fd90 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -251,8 +251,7 @@ static bool CodeNeedsEntryExitStub(const void* entry_point, ArtMethod* method) static void UpdateEntryPoints(ArtMethod* method, const void* quick_code) REQUIRES_SHARED(Locks::mutator_lock_) { if (kIsDebugBuild) { - if (NeedsClinitCheckBeforeCall(method) && - !method->GetDeclaringClass()->IsVisiblyInitialized()) { + if (method->StillNeedsClinitCheckMayBeDead()) { CHECK(CanHandleInitializationCheck(quick_code)); } jit::Jit* jit = Runtime::Current()->GetJit(); @@ -379,7 +378,7 @@ void Instrumentation::InitializeMethodsCode(ArtMethod* method, const void* aot_c } // Special case if we need an initialization check. - if (NeedsClinitCheckBeforeCall(method) && !method->GetDeclaringClass()->IsVisiblyInitialized()) { + if (method->StillNeedsClinitCheck()) { // If we have code but the method needs a class initialization check before calling // that code, install the resolution stub that will perform the check. // It will be replaced by the proper entry point by ClassLinker::FixupStaticTrampolines @@ -449,7 +448,7 @@ void Instrumentation::InstallStubsForMethod(ArtMethod* method) { // We're being asked to restore the entrypoints after instrumentation. CHECK_EQ(instrumentation_level_, InstrumentationLevel::kInstrumentNothing); // We need to have the resolution stub still if the class is not initialized. - if (NeedsClinitCheckBeforeCall(method) && !method->GetDeclaringClass()->IsVisiblyInitialized()) { + if (method->StillNeedsClinitCheck()) { UpdateEntryPoints(method, GetQuickResolutionStub()); return; } @@ -1248,8 +1247,7 @@ void Instrumentation::Undeoptimize(ArtMethod* method) { // We still retain interpreter bridge if we need it for other reasons. if (InterpretOnly(method)) { UpdateEntryPoints(method, GetQuickToInterpreterBridge()); - } else if (NeedsClinitCheckBeforeCall(method) && - !method->GetDeclaringClass()->IsVisiblyInitialized()) { + } else if (method->StillNeedsClinitCheck()) { if (EntryExitStubsInstalled()) { UpdateEntryPoints(method, GetQuickInstrumentationEntryPoint()); } else { diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 2cc4aef27a..abade3a7d3 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -1808,8 +1808,7 @@ void Jit::MaybeEnqueueCompilation(ArtMethod* method, Thread* self) { // Check if we have precompiled this method. if (UNLIKELY(method->IsPreCompiled())) { - if (!NeedsClinitCheckBeforeCall(method) || - method->GetDeclaringClass()->IsVisiblyInitialized()) { + if (!method->StillNeedsClinitCheck()) { const void* entry_point = code_cache_->GetSavedEntryPointOfPreCompiledMethod(method); if (entry_point != nullptr) { Runtime::Current()->GetInstrumentation()->UpdateMethodsCode(method, entry_point); diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index ee5d5c7ce3..86eedca285 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -127,31 +127,19 @@ class JitCodeCache::JniStubData { DCHECK(entrypoint == OatQuickMethodHeader::FromCodePointer(GetCode())->GetEntryPoint()); instrumentation::Instrumentation* instrum = Runtime::Current()->GetInstrumentation(); for (ArtMethod* m : GetMethods()) { - // Because `m` might be in the process of being deleted: - // - Call the dedicated method instead of the more generic UpdateMethodsCode - // - Check the class status without a full read barrier; use ReadBarrier::IsMarked(). - bool can_set_entrypoint = true; - if (NeedsClinitCheckBeforeCall(m)) { - // To avoid resurrecting an unreachable object, we must not use a full read - // barrier but we do not want to miss updating an entrypoint under common - // circumstances, i.e. during a GC the class becomes visibly initialized, - // the method becomes hot, we compile the thunk and want to update the - // entrypoint while the method's declaring class field still points to the - // from-space class object with the old status. Therefore we read the - // declaring class without a read barrier and check if it's already marked. - // If yes, we check the status of the to-space class object as intended. - // Otherwise, there is no to-space object and the from-space class object - // contains the most recent value of the status field; even if this races - // with another thread doing a read barrier and updating the status, that's - // no different from a race with a thread that just updates the status. - // Such race can happen only for the zygote method pre-compilation, as we - // otherwise compile only thunks for methods of visibly initialized classes. - ObjPtr<mirror::Class> klass = m->GetDeclaringClass<kWithoutReadBarrier>(); - ObjPtr<mirror::Class> marked = ReadBarrier::IsMarked(klass.Ptr()); - ObjPtr<mirror::Class> checked_klass = (marked != nullptr) ? marked : klass; - can_set_entrypoint = checked_klass->IsVisiblyInitialized(); - } - if (can_set_entrypoint) { + // Because `m` might be in the process of being deleted, + // - use the `ArtMethod::StillNeedsClinitCheckMayBeDead()` to check if + // we can update the entrypoint, and + // - call `Instrumentation::UpdateNativeMethodsCodeToJitCode` instead of the + // more generic function `Instrumentation::UpdateMethodsCode()`. + // The `ArtMethod::StillNeedsClinitCheckMayBeDead()` checks the class status + // in the to-space object if any even if the method's declaring class points to + // the from-space class object. This way we do not miss updating an entrypoint + // even under uncommon circumstances, when during a GC the class becomes visibly + // initialized, the method becomes hot, we compile the thunk and want to update + // the entrypoint while the method's declaring class field still points to the + // from-space class object with the old status. + if (!m->StillNeedsClinitCheckMayBeDead()) { instrum->UpdateNativeMethodsCodeToJitCode(m, entrypoint); } } @@ -741,8 +729,7 @@ bool JitCodeCache::Commit(Thread* self, } if (compilation_kind == CompilationKind::kOsr) { osr_code_map_.Put(method, code_ptr); - } else if (NeedsClinitCheckBeforeCall(method) && - !method->GetDeclaringClass()->IsVisiblyInitialized()) { + } else if (method->StillNeedsClinitCheck()) { // This situation currently only occurs in the jit-zygote mode. DCHECK(!garbage_collect_code_); DCHECK(method->IsPreCompiled()); @@ -1636,7 +1623,7 @@ bool JitCodeCache::NotifyCompilationOf(ArtMethod* method, } } - if (NeedsClinitCheckBeforeCall(method) && !prejit) { + if (method->NeedsClinitCheckBeforeCall() && !prejit) { // We do not need a synchronization barrier for checking the visibly initialized status // or checking the initialized status just for requesting visible initialization. ClassStatus status = method->GetDeclaringClass() diff --git a/runtime/jni/java_vm_ext.cc b/runtime/jni/java_vm_ext.cc index 9c695ca513..b4e7938e91 100644 --- a/runtime/jni/java_vm_ext.cc +++ b/runtime/jni/java_vm_ext.cc @@ -1157,7 +1157,7 @@ void* JavaVMExt::FindCodeForNativeMethod(ArtMethod* m, std::string* error_msg, b CHECK(m->IsNative()); ObjPtr<mirror::Class> c = m->GetDeclaringClass(); // If this is a static method, it could be called before the class has been initialized. - CHECK(c->IsInitializing() || !NeedsClinitCheckBeforeCall(m)) + CHECK(c->IsInitializing() || !m->NeedsClinitCheckBeforeCall()) << c->GetStatus() << " " << m->PrettyMethod(); Thread* const self = Thread::Current(); void* native_method = libraries_->FindNativeMethod(self, m, error_msg, can_suspend); diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 521e7c6764..f60d016230 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -726,7 +726,7 @@ class UpdateMethodsPreFirstForkVisitor : public ClassVisitor { bool operator()(ObjPtr<mirror::Class> klass) override REQUIRES_SHARED(Locks::mutator_lock_) { bool is_initialized = klass->IsVisiblyInitialized(); for (ArtMethod& method : klass->GetDeclaredMethods(kRuntimePointerSize)) { - if (is_initialized || !NeedsClinitCheckBeforeCall(&method)) { + if (is_initialized || !method.NeedsClinitCheckBeforeCall()) { if (method.IsNative()) { const void* existing = method.GetEntryPointFromJni(); if (method.IsCriticalNative() |