Guard Kotlin code from foreign exceptions when producing framework

This commit is contained in:
Svyatoslav Scherbina
2017-12-06 11:14:06 +03:00
committed by SvyatoslavScherbina
parent 03ef9e4855
commit b705fa9656
3 changed files with 59 additions and 3 deletions
@@ -21,12 +21,12 @@ import kotlinx.cinterop.*
import llvm.*
import org.jetbrains.kotlin.backend.konan.Context
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.descriptors.ClassConstructorDescriptor
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
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 cleanupLandingpad = basicBlockInFunction("cleanup_landingpad", endLocation)
var forwardingForeignExceptionsTerminatedWith: LLVMValueRef? = null
init {
functionDescriptor?.let {
if (!functionDescriptor.isExported()) {
@@ -420,6 +422,9 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
fun structGep(base: LLVMValueRef, index: Int, name: String = ""): LLVMValueRef =
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 {
val personalityFunction = LLVMConstBitCast(context.llvm.gxxPersonalityFunction, int8TypePtr)
@@ -605,6 +610,41 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
appendingTo(cleanupLandingpad) {
val landingpad = gxxLandingpad(numClauses = 0)
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()
LLVMBuildResume(builder, landingpad)
}
@@ -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
// 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.
val beginCatch = context.llvm.cxaBeginCatchFunction
@@ -41,6 +41,16 @@ internal class ObjCExportCodeGenerator(
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,
// so it is correct to use [callAtFunctionScope] here.
// However, exception handling probably should be refactored
@@ -49,7 +59,13 @@ internal class ObjCExportCodeGenerator(
function: LLVMValueRef,
args: List<LLVMValueRef>,
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(
returnType: LLVMTypeRef,