/* * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license * that can be found in the LICENSE file. */ #include "Exceptions.h" #include "MemorySharedRefs.hpp" #include "Runtime.h" #include "Types.h" extern "C" { // Returns a string describing object at `address` of type `typeInfo`. OBJ_GETTER(DescribeObjectForDebugging, KConstNativePtr typeInfo, KConstNativePtr address); } // extern "C" namespace { inline bool isForeignRefAccessible(ObjHeader* object, ForeignRefContext context) { // If runtime has not been initialized on this thread, then the object is either unowned or shared. // In the former case initialized runtime is required to throw exceptions // in the latter case -- to provide proper execution context for caller. // TODO: this probably can't be called in uninitialized state in the new MM. Kotlin_initRuntimeIfNeeded(); return IsForeignRefAccessible(object, context); } RUNTIME_NORETURN inline void throwIllegalSharingException(ObjHeader* object) { // TODO: add some info about the context. // Note: retrieving 'type_info()' is supposed to be correct even for unowned object. ThrowIllegalObjectSharingException(object->type_info(), object); } RUNTIME_NORETURN inline void terminateWithIllegalSharingException(ObjHeader* object) { #if KONAN_NO_EXCEPTIONS // This will terminate. throwIllegalSharingException(object); #else try { throwIllegalSharingException(object); } catch (...) { // A trick to terminate with unhandled exception. This will print a stack trace // and write to iOS crash log. std::terminate(); } #endif } template bool ensureRefAccessible(ObjHeader* object, ForeignRefContext context) { static_assert(errorPolicy != ErrorPolicy::kIgnore, "Must've been handled by specialization"); if (isForeignRefAccessible(object, context)) { return true; } switch (errorPolicy) { case ErrorPolicy::kDefaultValue: return false; case ErrorPolicy::kThrow: throwIllegalSharingException(object); case ErrorPolicy::kTerminate: terminateWithIllegalSharingException(object); } } template <> bool ensureRefAccessible(ObjHeader* object, ForeignRefContext context) { return true; } } // namespace void KRefSharedHolder::initLocal(ObjHeader* obj) { RuntimeAssert(obj != nullptr, "must not be null"); context_ = InitLocalForeignRef(obj); obj_ = obj; } void KRefSharedHolder::init(ObjHeader* obj) { RuntimeAssert(obj != nullptr, "must not be null"); context_ = InitForeignRef(obj); obj_ = obj; } template ObjHeader* KRefSharedHolder::ref() const { kotlin::AssertThreadState(kotlin::ThreadState::kRunnable); if (!ensureRefAccessible(obj_, context_)) { return nullptr; } AdoptReferenceFromSharedVariable(obj_); return obj_; } template ObjHeader* KRefSharedHolder::ref() const; template ObjHeader* KRefSharedHolder::ref() const; template ObjHeader* KRefSharedHolder::ref() const; void KRefSharedHolder::dispose() const { if (obj_ == nullptr) { // To handle the case when it is not initialized. See [KotlinMutableSet/Dictionary dealloc]. return; } DeinitForeignRef(obj_, context_); } OBJ_GETTER0(KRefSharedHolder::describe) const { // Note: retrieving 'type_info()' is supposed to be correct even for unowned object. RETURN_RESULT_OF(DescribeObjectForDebugging, obj_->type_info(), obj_); } void BackRefFromAssociatedObject::initAndAddRef(ObjHeader* obj) { RuntimeAssert(obj != nullptr, "must not be null"); obj_ = obj; // Generally a specialized addRef below: context_ = InitForeignRef(obj); refCount = 1; } template void BackRefFromAssociatedObject::addRef() { static_assert(errorPolicy != ErrorPolicy::kDefaultValue, "Cannot use default return value here"); if (atomicAdd(&refCount, 1) == 1) { if (obj_ == nullptr) return; // E.g. after [detach]. // There are no references to the associated object itself, so Kotlin object is being passed from Kotlin, // and it is owned therefore. kotlin::AssertThreadState(kotlin::ThreadState::kRunnable); ensureRefAccessible(obj_, context_); // TODO: consider removing explicit verification. // Foreign reference has already been deinitialized (see [releaseRef]). // Create a new one: context_ = InitForeignRef(obj_); } else { // Can be called both from Native state (if ObjC or Swift code adds RC) // and from Runnable state (Kotlin_ObjCExport_refToObjC). } } template void BackRefFromAssociatedObject::addRef(); template void BackRefFromAssociatedObject::addRef(); template bool BackRefFromAssociatedObject::tryAddRef() { static_assert(errorPolicy != ErrorPolicy::kDefaultValue, "Cannot use default return value here"); kotlin::CalledFromNativeGuard guard; if (obj_ == nullptr) return false; // E.g. after [detach]. if (CurrentMemoryModel == MemoryModel::kExperimental) { ObjHolder holder; ObjHeader* obj = TryRef(obj_, holder.slot()); // Failed to lock weak reference. if (obj == nullptr) return false; RuntimeAssert(obj == obj_, "Mismatched locked weak. obj=%p obj_=%p", obj, obj_); // TODO: This is a very weird way to ask for "unsafe" addRef. addRef(); return true; } else { // Suboptimal but simple: ensureRefAccessible(obj_, context_); ObjHeader* obj = obj_; if (!TryAddHeapRef(obj)) return false; RuntimeAssert(isForeignRefAccessible(obj_, context_), "Cannot be inaccessible because of the check above"); // TODO: This is a very weird way to ask for "unsafe" addRef. addRef(); ReleaseHeapRefNoCollect(obj); // Balance TryAddHeapRef. // TODO: consider optimizing for non-shared objects. return true; } } template bool BackRefFromAssociatedObject::tryAddRef(); template bool BackRefFromAssociatedObject::tryAddRef(); void BackRefFromAssociatedObject::releaseRef() { ForeignRefContext context = context_; if (atomicAdd(&refCount, -1) == 0) { if (obj_ == nullptr) return; // E.g. after [detach]. kotlin::CalledFromNativeGuard guard; // Note: by this moment "subsequent" addRef may have already happened and patched context_. // So use the value loaded before refCount update: DeinitForeignRef(obj_, context); // From this moment [context] is generally a dangling pointer. // This is handled in [IsForeignRefAccessible] and [addRef]. // TODO: This probably isn't fine in new MM. Make sure it works. } } void BackRefFromAssociatedObject::detach() { RuntimeAssert(atomicGet(&refCount) == 0, "unexpected refCount"); obj_ = nullptr; // Handled in addRef/tryAddRef/releaseRef/ref. } ALWAYS_INLINE void BackRefFromAssociatedObject::assertDetached() { RuntimeAssert(obj_ == nullptr, "Expecting this=%p to be detached, but found obj_=%p", this, obj_); } template ObjHeader* BackRefFromAssociatedObject::ref() const { kotlin::AssertThreadState(kotlin::ThreadState::kRunnable); RuntimeAssert(obj_ != nullptr, "no valid Kotlin object found"); if (!ensureRefAccessible(obj_, context_)) { return nullptr; } AdoptReferenceFromSharedVariable(obj_); return obj_; } template ObjHeader* BackRefFromAssociatedObject::ref() const; template ObjHeader* BackRefFromAssociatedObject::ref() const; template ObjHeader* BackRefFromAssociatedObject::ref() const; extern "C" { RUNTIME_NOTHROW void KRefSharedHolder_initLocal(KRefSharedHolder* holder, ObjHeader* obj) { holder->initLocal(obj); } RUNTIME_NOTHROW void KRefSharedHolder_init(KRefSharedHolder* holder, ObjHeader* obj) { holder->init(obj); } RUNTIME_NOTHROW void KRefSharedHolder_dispose(const KRefSharedHolder* holder) { holder->dispose(); } RUNTIME_NOTHROW ObjHeader* KRefSharedHolder_ref(const KRefSharedHolder* holder) { return holder->ref(); } } // extern "C"