Guard Kotlin code from foreign exceptions when producing framework
This commit is contained in:
committed by
SvyatoslavScherbina
parent
03ef9e4855
commit
b705fa9656
+41
-1
@@ -21,12 +21,12 @@ import kotlinx.cinterop.*
|
|||||||
import llvm.*
|
import llvm.*
|
||||||
import org.jetbrains.kotlin.backend.konan.Context
|
import org.jetbrains.kotlin.backend.konan.Context
|
||||||
import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
|
import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
|
||||||
|
import org.jetbrains.kotlin.backend.konan.descriptors.stdlibModule
|
||||||
import org.jetbrains.kotlin.backend.konan.isObjCClass
|
import org.jetbrains.kotlin.backend.konan.isObjCClass
|
||||||
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
|
||||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
|
|
||||||
|
|
||||||
internal class CodeGenerator(override val context: Context) : ContextUtils {
|
internal class CodeGenerator(override val context: Context) : ContextUtils {
|
||||||
|
|
||||||
@@ -132,6 +132,8 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
|
|||||||
private val epilogueBb = basicBlockInFunction("epilogue", endLocation)
|
private val epilogueBb = basicBlockInFunction("epilogue", endLocation)
|
||||||
private val cleanupLandingpad = basicBlockInFunction("cleanup_landingpad", endLocation)
|
private val cleanupLandingpad = basicBlockInFunction("cleanup_landingpad", endLocation)
|
||||||
|
|
||||||
|
var forwardingForeignExceptionsTerminatedWith: LLVMValueRef? = null
|
||||||
|
|
||||||
init {
|
init {
|
||||||
functionDescriptor?.let {
|
functionDescriptor?.let {
|
||||||
if (!functionDescriptor.isExported()) {
|
if (!functionDescriptor.isExported()) {
|
||||||
@@ -420,6 +422,9 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
|
|||||||
fun structGep(base: LLVMValueRef, index: Int, name: String = ""): LLVMValueRef =
|
fun structGep(base: LLVMValueRef, index: Int, name: String = ""): LLVMValueRef =
|
||||||
LLVMBuildStructGEP(builder, base, index, name)!!
|
LLVMBuildStructGEP(builder, base, index, name)!!
|
||||||
|
|
||||||
|
fun extractValue(aggregate: LLVMValueRef, index: Int, name: String = ""): LLVMValueRef =
|
||||||
|
LLVMBuildExtractValue(builder, aggregate, index, name)!!
|
||||||
|
|
||||||
fun gxxLandingpad(numClauses: Int, name: String = ""): LLVMValueRef {
|
fun gxxLandingpad(numClauses: Int, name: String = ""): LLVMValueRef {
|
||||||
val personalityFunction = LLVMConstBitCast(context.llvm.gxxPersonalityFunction, int8TypePtr)
|
val personalityFunction = LLVMConstBitCast(context.llvm.gxxPersonalityFunction, int8TypePtr)
|
||||||
|
|
||||||
@@ -605,6 +610,41 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
|
|||||||
appendingTo(cleanupLandingpad) {
|
appendingTo(cleanupLandingpad) {
|
||||||
val landingpad = gxxLandingpad(numClauses = 0)
|
val landingpad = gxxLandingpad(numClauses = 0)
|
||||||
LLVMSetCleanup(landingpad, 1)
|
LLVMSetCleanup(landingpad, 1)
|
||||||
|
|
||||||
|
forwardingForeignExceptionsTerminatedWith?.let { terminator ->
|
||||||
|
val kotlinExceptionRtti = constPointer(importGlobal(
|
||||||
|
"_ZTI9ObjHolder", // typeinfo for ObjHolder
|
||||||
|
int8TypePtr,
|
||||||
|
origin = context.stdlibModule.llvmSymbolOrigin
|
||||||
|
))
|
||||||
|
|
||||||
|
// Catch all but Kotlin exceptions.
|
||||||
|
val clause = ConstArray(int8TypePtr, listOf(kotlinExceptionRtti.bitcast(int8TypePtr)))
|
||||||
|
LLVMAddClause(landingpad, clause.llvm)
|
||||||
|
|
||||||
|
val bbCleanup = basicBlock("forwardException", null)
|
||||||
|
val bbUnexpected = basicBlock("unexpectedException", null)
|
||||||
|
|
||||||
|
val selector = extractValue(landingpad, 1)
|
||||||
|
condBr(
|
||||||
|
icmpLt(selector, Int32(0).llvm),
|
||||||
|
bbUnexpected,
|
||||||
|
bbCleanup
|
||||||
|
)
|
||||||
|
|
||||||
|
appendingTo(bbUnexpected) {
|
||||||
|
val exceptionRecord = extractValue(landingpad, 0)
|
||||||
|
|
||||||
|
val beginCatch = context.llvm.cxaBeginCatchFunction
|
||||||
|
// So `terminator` is called from C++ catch block:
|
||||||
|
call(beginCatch, listOf(exceptionRecord))
|
||||||
|
call(terminator, emptyList())
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
|
||||||
|
positionAtEnd(bbCleanup)
|
||||||
|
}
|
||||||
|
|
||||||
releaseVars()
|
releaseVars()
|
||||||
LLVMBuildResume(builder, landingpad)
|
LLVMBuildResume(builder, landingpad)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -917,7 +917,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
|||||||
// FIXME: properly handle C++ exceptions: currently C++ exception can be thrown out from try-finally
|
// FIXME: properly handle C++ exceptions: currently C++ exception can be thrown out from try-finally
|
||||||
// bypassing the finally block.
|
// bypassing the finally block.
|
||||||
|
|
||||||
val exceptionRecord = LLVMBuildExtractValue(functionGenerationContext.builder, landingpadResult, 0, "er")!!
|
val exceptionRecord = extractValue(landingpadResult, 0, "er")
|
||||||
|
|
||||||
// __cxa_begin_catch returns pointer to C++ exception object.
|
// __cxa_begin_catch returns pointer to C++ exception object.
|
||||||
val beginCatch = context.llvm.cxaBeginCatchFunction
|
val beginCatch = context.llvm.cxaBeginCatchFunction
|
||||||
|
|||||||
+17
-1
@@ -41,6 +41,16 @@ internal class ObjCExportCodeGenerator(
|
|||||||
|
|
||||||
val rttiGenerator = RTTIGenerator(context)
|
val rttiGenerator = RTTIGenerator(context)
|
||||||
|
|
||||||
|
private val objcTerminate: LLVMValueRef by lazy {
|
||||||
|
context.llvm.externalFunction(
|
||||||
|
"objc_terminate",
|
||||||
|
functionType(voidType, false),
|
||||||
|
CurrentKonanModule
|
||||||
|
).also {
|
||||||
|
setFunctionNoUnwind(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: currently bridges don't have any custom `landingpad`s,
|
// TODO: currently bridges don't have any custom `landingpad`s,
|
||||||
// so it is correct to use [callAtFunctionScope] here.
|
// so it is correct to use [callAtFunctionScope] here.
|
||||||
// However, exception handling probably should be refactored
|
// However, exception handling probably should be refactored
|
||||||
@@ -49,7 +59,13 @@ internal class ObjCExportCodeGenerator(
|
|||||||
function: LLVMValueRef,
|
function: LLVMValueRef,
|
||||||
args: List<LLVMValueRef>,
|
args: List<LLVMValueRef>,
|
||||||
resultLifetime: Lifetime = Lifetime.IRRELEVANT
|
resultLifetime: Lifetime = Lifetime.IRRELEVANT
|
||||||
): LLVMValueRef = callAtFunctionScope(function, args, resultLifetime)
|
): LLVMValueRef {
|
||||||
|
|
||||||
|
// TODO: it is required only for Kotlin-to-Objective-C bridges.
|
||||||
|
this.forwardingForeignExceptionsTerminatedWith = objcTerminate
|
||||||
|
|
||||||
|
return callAtFunctionScope(function, args, resultLifetime)
|
||||||
|
}
|
||||||
|
|
||||||
fun FunctionGenerationContext.genSendMessage(
|
fun FunctionGenerationContext.genSendMessage(
|
||||||
returnType: LLVMTypeRef,
|
returnType: LLVMTypeRef,
|
||||||
|
|||||||
Reference in New Issue
Block a user