Use ForeignException wrapper to handle native exception [KT-35056] (#4307)
* Use ForeignException wrapper to handle native exception [KT-35056] * Add test * Use Any? instead of NativePtr for automagic memory management * Use clauseNSException specific clause, similar to `catch (NSException* e) * cleanup + test fix * Terminate on anything else but NSException * Use ForeignException message for NSException details * Code generated for AppleFamily only + move stuff to kotlinx.cinterop package * Cleanup * Fix platform-dependent code generation * Fix test allocException * wip: ForeignExceptionMode option: cinterop part * ForeignExceptionMode support in compiler * rework test for foreignExceptionMode option * More tests on foreignExceptionMode option * PR feedback: resolveFakeOverride and more * Test reworked and extended Co-authored-by: Vladimir Ivanov <vladimir.d.ivanov@jetbrains.com>
This commit is contained in:
@@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2018 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.
|
||||||
|
*/
|
||||||
|
package kotlinx.cinterop
|
||||||
|
|
||||||
|
import kotlin.native.internal.ExportForCppRuntime
|
||||||
|
|
||||||
|
public class ForeignException internal constructor(val nativeException: Any?): Exception() {
|
||||||
|
override val message: String = nativeException?.let {
|
||||||
|
kotlin_ObjCExport_ExceptionDetails(nativeException)
|
||||||
|
}?: ""
|
||||||
|
|
||||||
|
// Current implementation expects NSException type only, which is ensured by CodeGenerator.
|
||||||
|
@SymbolName("Kotlin_ObjCExport_ExceptionDetails")
|
||||||
|
private external fun kotlin_ObjCExport_ExceptionDetails(nativeException: Any): String?
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExportForCppRuntime
|
||||||
|
internal fun CreateForeignException(payload: NativePtr): Throwable
|
||||||
|
= ForeignException(interpretObjCPointerOrNull<Any?>(payload))
|
||||||
+4
@@ -29,6 +29,7 @@ const val TEMP_DIR = "Xtemporary-files-dir"
|
|||||||
const val NOPACK = "nopack"
|
const val NOPACK = "nopack"
|
||||||
const val COMPILE_SOURCES = "Xcompile-source"
|
const val COMPILE_SOURCES = "Xcompile-source"
|
||||||
const val SHORT_MODULE_NAME = "Xshort-module-name"
|
const val SHORT_MODULE_NAME = "Xshort-module-name"
|
||||||
|
const val FOREIGN_EXCEPTION_MODE = "Xforeign-exception-mode"
|
||||||
|
|
||||||
// TODO: unify camel and snake cases.
|
// TODO: unify camel and snake cases.
|
||||||
// Possible solution is to accept both cases
|
// Possible solution is to accept both cases
|
||||||
@@ -119,6 +120,9 @@ open class CInteropArguments(argParser: ArgParser =
|
|||||||
fullName = "Xmodule-name",
|
fullName = "Xmodule-name",
|
||||||
description = "A full name of the library used for dependency resolution"
|
description = "A full name of the library used for dependency resolution"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val foreignExceptionMode by argParser.option(ArgType.String, FOREIGN_EXCEPTION_MODE,
|
||||||
|
description = "Handle native exception in Kotlin: <terminate|objc-wrap>")
|
||||||
}
|
}
|
||||||
|
|
||||||
class JSInteropArguments(argParser: ArgParser = ArgParser("jsinterop",
|
class JSInteropArguments(argParser: ArgParser = ArgParser("jsinterop",
|
||||||
|
|||||||
+9
-1
@@ -27,6 +27,7 @@ import kotlinx.cli.ArgParser
|
|||||||
import kotlinx.cli.ArgType
|
import kotlinx.cli.ArgType
|
||||||
import kotlinx.cli.default
|
import kotlinx.cli.default
|
||||||
import kotlinx.cli.required
|
import kotlinx.cli.required
|
||||||
|
import org.jetbrains.kotlin.konan.ForeignExceptionMode
|
||||||
import org.jetbrains.kotlin.konan.library.*
|
import org.jetbrains.kotlin.konan.library.*
|
||||||
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
|
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
|
||||||
import org.jetbrains.kotlin.konan.target.Distribution
|
import org.jetbrains.kotlin.konan.target.Distribution
|
||||||
@@ -200,7 +201,7 @@ private fun processCLib(flavorName: String, cinteropArguments: CInteropArguments
|
|||||||
val manifestAddend = additionalArgs.manifest?.let { File(it) }
|
val manifestAddend = additionalArgs.manifest?.let { File(it) }
|
||||||
|
|
||||||
if (defFile == null && cinteropArguments.pkg == null) {
|
if (defFile == null && cinteropArguments.pkg == null) {
|
||||||
cinteropArguments.argParser.printError("-def or -pkg should provided!")
|
cinteropArguments.argParser.printError("-def or -pkg should be provided!")
|
||||||
}
|
}
|
||||||
|
|
||||||
val tool = prepareTool(cinteropArguments.target, flavor)
|
val tool = prepareTool(cinteropArguments.target, flavor)
|
||||||
@@ -327,6 +328,13 @@ private fun processCLib(flavorName: String, cinteropArguments: CInteropArguments
|
|||||||
}
|
}
|
||||||
stubIrContext.addManifestProperties(def.manifestAddendProperties)
|
stubIrContext.addManifestProperties(def.manifestAddendProperties)
|
||||||
|
|
||||||
|
// cinterop command line option overrides def file property
|
||||||
|
val foreignExceptionMode = cinteropArguments.foreignExceptionMode?: def.config.foreignExceptionMode
|
||||||
|
foreignExceptionMode?.let {
|
||||||
|
def.manifestAddendProperties[ForeignExceptionMode.manifestKey] =
|
||||||
|
ForeignExceptionMode.byValue(it).value // may throw IllegalArgumentException
|
||||||
|
}
|
||||||
|
|
||||||
manifestAddend?.parentFile?.mkdirs()
|
manifestAddend?.parentFile?.mkdirs()
|
||||||
manifestAddend?.let { def.manifestAddendProperties.storeProperties(it) }
|
manifestAddend?.let { def.manifestAddendProperties.storeProperties(it) }
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -137,7 +137,7 @@ private fun isExportedClass(descriptor: ClassDescriptor): Boolean {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun AnnotationDescriptor.properValue(key: String) =
|
internal fun AnnotationDescriptor.properValue(key: String) =
|
||||||
this.argumentValue(key)?.toString()?.removeSurrounding("\"")
|
this.argumentValue(key)?.toString()?.removeSurrounding("\"")
|
||||||
|
|
||||||
private fun functionImplName(descriptor: DeclarationDescriptor, default: String, shortName: Boolean): String {
|
private fun functionImplName(descriptor: DeclarationDescriptor, default: String, shortName: Boolean): String {
|
||||||
|
|||||||
+16
-6
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.backend.common.lower.irNot
|
|||||||
import org.jetbrains.kotlin.backend.konan.KonanFqNames
|
import org.jetbrains.kotlin.backend.konan.KonanFqNames
|
||||||
import org.jetbrains.kotlin.backend.konan.PrimitiveBinaryType
|
import org.jetbrains.kotlin.backend.konan.PrimitiveBinaryType
|
||||||
import org.jetbrains.kotlin.backend.konan.RuntimeNames
|
import org.jetbrains.kotlin.backend.konan.RuntimeNames
|
||||||
|
import org.jetbrains.kotlin.backend.konan.descriptors.konanLibrary
|
||||||
import org.jetbrains.kotlin.backend.konan.ir.*
|
import org.jetbrains.kotlin.backend.konan.ir.*
|
||||||
import org.jetbrains.kotlin.backend.konan.isObjCMetaClass
|
import org.jetbrains.kotlin.backend.konan.isObjCMetaClass
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
@@ -45,6 +46,7 @@ import org.jetbrains.kotlin.backend.konan.getObjCMethodInfo
|
|||||||
import org.jetbrains.kotlin.backend.konan.lower.FunctionReferenceLowering
|
import org.jetbrains.kotlin.backend.konan.lower.FunctionReferenceLowering
|
||||||
import org.jetbrains.kotlin.builtins.StandardNames
|
import org.jetbrains.kotlin.builtins.StandardNames
|
||||||
import org.jetbrains.kotlin.ir.descriptors.*
|
import org.jetbrains.kotlin.ir.descriptors.*
|
||||||
|
import org.jetbrains.kotlin.konan.ForeignExceptionMode
|
||||||
|
|
||||||
internal interface KotlinStubs {
|
internal interface KotlinStubs {
|
||||||
val irBuiltIns: IrBuiltIns
|
val irBuiltIns: IrBuiltIns
|
||||||
@@ -62,7 +64,8 @@ internal interface KotlinStubs {
|
|||||||
private class KotlinToCCallBuilder(
|
private class KotlinToCCallBuilder(
|
||||||
val irBuilder: IrBuilderWithScope,
|
val irBuilder: IrBuilderWithScope,
|
||||||
val stubs: KotlinStubs,
|
val stubs: KotlinStubs,
|
||||||
val isObjCMethod: Boolean
|
val isObjCMethod: Boolean,
|
||||||
|
foreignExceptionMode: ForeignExceptionMode.Mode
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val cBridgeName = stubs.getUniqueCName("knbridge")
|
val cBridgeName = stubs.getUniqueCName("knbridge")
|
||||||
@@ -70,7 +73,7 @@ private class KotlinToCCallBuilder(
|
|||||||
val symbols: KonanSymbols get() = stubs.symbols
|
val symbols: KonanSymbols get() = stubs.symbols
|
||||||
|
|
||||||
val bridgeCallBuilder = KotlinCallBuilder(irBuilder, symbols)
|
val bridgeCallBuilder = KotlinCallBuilder(irBuilder, symbols)
|
||||||
val bridgeBuilder = KotlinCBridgeBuilder(irBuilder.startOffset, irBuilder.endOffset, cBridgeName, stubs, isKotlinToC = true)
|
val bridgeBuilder = KotlinCBridgeBuilder(irBuilder.startOffset, irBuilder.endOffset, cBridgeName, stubs, isKotlinToC = true, foreignExceptionMode)
|
||||||
val cBridgeBodyLines = mutableListOf<String>()
|
val cBridgeBodyLines = mutableListOf<String>()
|
||||||
val cCallBuilder = CCallBuilder()
|
val cCallBuilder = CCallBuilder()
|
||||||
val cFunctionBuilder = CFunctionBuilder()
|
val cFunctionBuilder = CFunctionBuilder()
|
||||||
@@ -110,10 +113,11 @@ private fun KotlinToCCallBuilder.buildKotlinBridgeCall(transformCall: (IrMemberA
|
|||||||
transformCall
|
transformCall
|
||||||
)
|
)
|
||||||
|
|
||||||
internal fun KotlinStubs.generateCCall(expression: IrCall, builder: IrBuilderWithScope, isInvoke: Boolean): IrExpression {
|
internal fun KotlinStubs.generateCCall(expression: IrCall, builder: IrBuilderWithScope, isInvoke: Boolean,
|
||||||
|
foreignExceptionMode: ForeignExceptionMode.Mode = ForeignExceptionMode.default): IrExpression {
|
||||||
require(expression.dispatchReceiver == null)
|
require(expression.dispatchReceiver == null)
|
||||||
|
|
||||||
val callBuilder = KotlinToCCallBuilder(builder, this, isObjCMethod = false)
|
val callBuilder = KotlinToCCallBuilder(builder, this, isObjCMethod = false, foreignExceptionMode)
|
||||||
|
|
||||||
val callee = expression.symbol.owner
|
val callee = expression.symbol.owner
|
||||||
|
|
||||||
@@ -307,7 +311,13 @@ internal fun KotlinStubs.generateObjCCall(
|
|||||||
receiver: ObjCCallReceiver,
|
receiver: ObjCCallReceiver,
|
||||||
arguments: List<IrExpression?>
|
arguments: List<IrExpression?>
|
||||||
) = builder.irBlock {
|
) = builder.irBlock {
|
||||||
val callBuilder = KotlinToCCallBuilder(builder, this@generateObjCCall, isObjCMethod = true)
|
val resolved = method.resolveFakeOverride(allowAbstract = true)?: method
|
||||||
|
val exceptionMode = ForeignExceptionMode.byValue(
|
||||||
|
resolved.module.konanLibrary?.manifestProperties
|
||||||
|
?.getProperty(ForeignExceptionMode.manifestKey)
|
||||||
|
)
|
||||||
|
|
||||||
|
val callBuilder = KotlinToCCallBuilder(builder, this@generateObjCCall, isObjCMethod = true, exceptionMode)
|
||||||
|
|
||||||
val superClass = irTemporaryVar(
|
val superClass = irTemporaryVar(
|
||||||
superQualifier?.let { getObjCClass(symbols, it) } ?: irNullNativePtr(symbols)
|
superQualifier?.let { getObjCClass(symbols, it) } ?: irNullNativePtr(symbols)
|
||||||
@@ -1327,7 +1337,7 @@ private class ObjCBlockPointerValuePassing(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun IrBuilderWithScope.callBlock(blockPtr: IrExpression, arguments: List<IrExpression>): IrExpression {
|
private fun IrBuilderWithScope.callBlock(blockPtr: IrExpression, arguments: List<IrExpression>): IrExpression {
|
||||||
val callBuilder = KotlinToCCallBuilder(this, stubs, isObjCMethod = false)
|
val callBuilder = KotlinToCCallBuilder(this, stubs, isObjCMethod = false, ForeignExceptionMode.default)
|
||||||
|
|
||||||
val rawBlockPointerParameter = callBuilder.passThroughBridge(blockPtr, blockPtr.type, CTypes.id)
|
val rawBlockPointerParameter = callBuilder.passThroughBridge(blockPtr, blockPtr.type, CTypes.id)
|
||||||
val blockVariableName = "block"
|
val blockVariableName = "block"
|
||||||
|
|||||||
+11
-6
@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.ir.types.impl.IrUninitializedType
|
|||||||
import org.jetbrains.kotlin.ir.util.constructors
|
import org.jetbrains.kotlin.ir.util.constructors
|
||||||
import org.jetbrains.kotlin.ir.util.irBuilder
|
import org.jetbrains.kotlin.ir.util.irBuilder
|
||||||
import org.jetbrains.kotlin.ir.util.irCatch
|
import org.jetbrains.kotlin.ir.util.irCatch
|
||||||
|
import org.jetbrains.kotlin.konan.ForeignExceptionMode
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
|
||||||
internal class CFunctionBuilder {
|
internal class CFunctionBuilder {
|
||||||
@@ -71,10 +72,11 @@ internal class KotlinBridgeBuilder(
|
|||||||
endOffset: Int,
|
endOffset: Int,
|
||||||
cName: String,
|
cName: String,
|
||||||
stubs: KotlinStubs,
|
stubs: KotlinStubs,
|
||||||
isExternal: Boolean
|
isExternal: Boolean,
|
||||||
|
foreignExceptionMode: ForeignExceptionMode.Mode
|
||||||
) {
|
) {
|
||||||
private var counter = 0
|
private var counter = 0
|
||||||
private val bridge: IrFunction = createKotlinBridge(startOffset, endOffset, cName, stubs, isExternal)
|
private val bridge: IrFunction = createKotlinBridge(startOffset, endOffset, cName, stubs, isExternal, foreignExceptionMode)
|
||||||
val irBuilder: IrBuilderWithScope = irBuilder(stubs.irBuiltIns, bridge.symbol).at(startOffset, endOffset)
|
val irBuilder: IrBuilderWithScope = irBuilder(stubs.irBuiltIns, bridge.symbol).at(startOffset, endOffset)
|
||||||
|
|
||||||
fun addParameter(type: IrType): IrValueParameter {
|
fun addParameter(type: IrType): IrValueParameter {
|
||||||
@@ -105,7 +107,8 @@ private fun createKotlinBridge(
|
|||||||
endOffset: Int,
|
endOffset: Int,
|
||||||
cBridgeName: String,
|
cBridgeName: String,
|
||||||
stubs: KotlinStubs,
|
stubs: KotlinStubs,
|
||||||
isExternal: Boolean
|
isExternal: Boolean,
|
||||||
|
foreignExceptionMode: ForeignExceptionMode.Mode
|
||||||
): IrFunction {
|
): IrFunction {
|
||||||
val bridgeDescriptor = WrappedSimpleFunctionDescriptor()
|
val bridgeDescriptor = WrappedSimpleFunctionDescriptor()
|
||||||
val bridge = IrFunctionImpl(
|
val bridge = IrFunctionImpl(
|
||||||
@@ -131,7 +134,8 @@ private fun createKotlinBridge(
|
|||||||
bridge.annotations += buildSimpleAnnotation(stubs.irBuiltIns, startOffset, endOffset,
|
bridge.annotations += buildSimpleAnnotation(stubs.irBuiltIns, startOffset, endOffset,
|
||||||
stubs.symbols.symbolName.owner, cBridgeName)
|
stubs.symbols.symbolName.owner, cBridgeName)
|
||||||
bridge.annotations += buildSimpleAnnotation(stubs.irBuiltIns, startOffset, endOffset,
|
bridge.annotations += buildSimpleAnnotation(stubs.irBuiltIns, startOffset, endOffset,
|
||||||
stubs.symbols.filterExceptions.owner)
|
stubs.symbols.filterExceptions.owner,
|
||||||
|
foreignExceptionMode.value)
|
||||||
} else {
|
} else {
|
||||||
bridge.annotations += buildSimpleAnnotation(stubs.irBuiltIns, startOffset, endOffset,
|
bridge.annotations += buildSimpleAnnotation(stubs.irBuiltIns, startOffset, endOffset,
|
||||||
stubs.symbols.exportForCppRuntime.owner, cBridgeName)
|
stubs.symbols.exportForCppRuntime.owner, cBridgeName)
|
||||||
@@ -144,9 +148,10 @@ internal class KotlinCBridgeBuilder(
|
|||||||
endOffset: Int,
|
endOffset: Int,
|
||||||
cName: String,
|
cName: String,
|
||||||
stubs: KotlinStubs,
|
stubs: KotlinStubs,
|
||||||
isKotlinToC: Boolean
|
isKotlinToC: Boolean,
|
||||||
|
foreignExceptionMode: ForeignExceptionMode.Mode = ForeignExceptionMode.default
|
||||||
) {
|
) {
|
||||||
private val kotlinBridgeBuilder = KotlinBridgeBuilder(startOffset, endOffset, cName, stubs, isExternal = isKotlinToC)
|
private val kotlinBridgeBuilder = KotlinBridgeBuilder(startOffset, endOffset, cName, stubs, isExternal = isKotlinToC, foreignExceptionMode)
|
||||||
private val cBridgeBuilder = CFunctionBuilder()
|
private val cBridgeBuilder = CFunctionBuilder()
|
||||||
|
|
||||||
val kotlinIrBuilder: IrBuilderWithScope get() = kotlinBridgeBuilder.irBuilder
|
val kotlinIrBuilder: IrBuilderWithScope get() = kotlinBridgeBuilder.irBuilder
|
||||||
|
|||||||
+2
@@ -195,6 +195,8 @@ internal class KonanSymbols(
|
|||||||
val interopCreateNSStringFromKString =
|
val interopCreateNSStringFromKString =
|
||||||
symbolTable.referenceSimpleFunction(context.interopBuiltIns.CreateNSStringFromKString)
|
symbolTable.referenceSimpleFunction(context.interopBuiltIns.CreateNSStringFromKString)
|
||||||
|
|
||||||
|
val createForeignException = interopFunction("CreateForeignException")
|
||||||
|
|
||||||
val interopObjCGetSelector = interopFunction("objCGetSelector")
|
val interopObjCGetSelector = interopFunction("objCGetSelector")
|
||||||
|
|
||||||
val interopCEnumVar = interopClass("CEnumVar")
|
val interopCEnumVar = interopClass("CEnumVar")
|
||||||
|
|||||||
+55
-4
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.descriptors.konan.CompiledKlibModuleOrigin
|
|||||||
import org.jetbrains.kotlin.ir.expressions.IrDelegatingConstructorCall
|
import org.jetbrains.kotlin.ir.expressions.IrDelegatingConstructorCall
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrGetObjectValue
|
import org.jetbrains.kotlin.ir.expressions.IrGetObjectValue
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrReturn
|
import org.jetbrains.kotlin.ir.expressions.IrReturn
|
||||||
|
import org.jetbrains.kotlin.konan.ForeignExceptionMode
|
||||||
|
|
||||||
private fun IrConstructor.isAnyConstructorDelegation(context: Context): Boolean {
|
private fun IrConstructor.isAnyConstructorDelegation(context: Context): Boolean {
|
||||||
val statements = this.body?.statements ?: return false
|
val statements = this.body?.statements ?: return false
|
||||||
@@ -703,23 +704,49 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
|
|||||||
return LLVMBuildExtractElement(builder, vector, index, name)!!
|
return LLVMBuildExtractElement(builder, vector, index, name)!!
|
||||||
}
|
}
|
||||||
|
|
||||||
fun filteringExceptionHandler(codeContext: CodeContext): ExceptionHandler {
|
fun filteringExceptionHandler(codeContext: CodeContext, foreignExceptionMode: ForeignExceptionMode.Mode): ExceptionHandler {
|
||||||
val lpBlock = basicBlockInFunction("filteringExceptionHandler", position()?.start)
|
val lpBlock = basicBlockInFunction("filteringExceptionHandler", position()?.start)
|
||||||
|
|
||||||
|
val wrapExceptionMode = context.config.target.family.isAppleFamily &&
|
||||||
|
foreignExceptionMode == ForeignExceptionMode.Mode.OBJC_WRAP
|
||||||
|
|
||||||
appendingTo(lpBlock) {
|
appendingTo(lpBlock) {
|
||||||
val landingpad = gxxLandingpad(2)
|
val landingpad = gxxLandingpad(2)
|
||||||
LLVMAddClause(landingpad, kotlinExceptionRtti.llvm)
|
LLVMAddClause(landingpad, kotlinExceptionRtti.llvm)
|
||||||
|
if (wrapExceptionMode) {
|
||||||
|
LLVMAddClause(landingpad, objcNSExceptionRtti.llvm)
|
||||||
|
}
|
||||||
LLVMAddClause(landingpad, LLVMConstNull(kInt8Ptr))
|
LLVMAddClause(landingpad, LLVMConstNull(kInt8Ptr))
|
||||||
|
|
||||||
val fatalForeignExceptionBlock = basicBlock("fatalForeignException", position()?.start)
|
val fatalForeignExceptionBlock = basicBlock("fatalForeignException", position()?.start)
|
||||||
val forwardKotlinExceptionBlock = basicBlock("forwardKotlinException", position()?.start)
|
val forwardKotlinExceptionBlock = basicBlock("forwardKotlinException", position()?.start)
|
||||||
|
|
||||||
|
val typeId = extractValue(landingpad, 1)
|
||||||
val isKotlinException = icmpEq(
|
val isKotlinException = icmpEq(
|
||||||
extractValue(landingpad, 1),
|
typeId,
|
||||||
call(context.llvm.llvmEhTypeidFor, listOf(kotlinExceptionRtti.llvm))
|
call(context.llvm.llvmEhTypeidFor, listOf(kotlinExceptionRtti.llvm))
|
||||||
)
|
)
|
||||||
|
|
||||||
condBr(isKotlinException, forwardKotlinExceptionBlock, fatalForeignExceptionBlock)
|
if (wrapExceptionMode) {
|
||||||
|
val foreignExceptionBlock = basicBlock("foreignException", position()?.start)
|
||||||
|
val forwardNativeExceptionBlock = basicBlock("forwardNativeException", position()?.start)
|
||||||
|
|
||||||
|
condBr(isKotlinException, forwardKotlinExceptionBlock, foreignExceptionBlock)
|
||||||
|
appendingTo(foreignExceptionBlock) {
|
||||||
|
val isObjCException = icmpEq(
|
||||||
|
typeId,
|
||||||
|
call(context.llvm.llvmEhTypeidFor, listOf(objcNSExceptionRtti.llvm))
|
||||||
|
)
|
||||||
|
condBr(isObjCException, forwardNativeExceptionBlock, fatalForeignExceptionBlock)
|
||||||
|
|
||||||
|
appendingTo(forwardNativeExceptionBlock) {
|
||||||
|
val exception = createForeignException(landingpad, codeContext.exceptionHandler)
|
||||||
|
codeContext.genThrow(exception)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
condBr(isKotlinException, forwardKotlinExceptionBlock, fatalForeignExceptionBlock)
|
||||||
|
}
|
||||||
|
|
||||||
appendingTo(forwardKotlinExceptionBlock) {
|
appendingTo(forwardKotlinExceptionBlock) {
|
||||||
// Rethrow Kotlin exception to real handler.
|
// Rethrow Kotlin exception to real handler.
|
||||||
@@ -731,6 +758,7 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
|
|||||||
call(context.llvm.cxaBeginCatchFunction, listOf(exceptionRecord))
|
call(context.llvm.cxaBeginCatchFunction, listOf(exceptionRecord))
|
||||||
terminate()
|
terminate()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return object : ExceptionHandler.Local() {
|
return object : ExceptionHandler.Local() {
|
||||||
@@ -770,7 +798,7 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
|
|||||||
|
|
||||||
LLVMAddClause(landingpadResult, LLVMConstNull(kInt8Ptr))
|
LLVMAddClause(landingpadResult, LLVMConstNull(kInt8Ptr))
|
||||||
|
|
||||||
// FIXME: properly handle C++ exceptions: currently C++ exception can be thrown out from try-finally
|
// TODO: properly handle C++ exceptions: currently C++ exception can be thrown out from try-finally
|
||||||
// bypassing the finally block.
|
// bypassing the finally block.
|
||||||
|
|
||||||
return extractKotlinException(landingpadResult)
|
return extractKotlinException(landingpadResult)
|
||||||
@@ -797,6 +825,21 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
|
|||||||
return exceptionPtr
|
return exceptionPtr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun createForeignException(landingpadResult: LLVMValueRef, exceptionHandler: ExceptionHandler): LLVMValueRef {
|
||||||
|
val exceptionRecord = extractValue(landingpadResult, 0, "er")
|
||||||
|
|
||||||
|
// __cxa_begin_catch returns pointer to C++ exception object.
|
||||||
|
val exceptionRawPtr = call(context.llvm.cxaBeginCatchFunction, listOf(exceptionRecord))
|
||||||
|
|
||||||
|
// This will take care of ARC - need to be done in the catching scope, i.e. before __cxa_end_catch
|
||||||
|
val exception = call(context.ir.symbols.createForeignException.owner.llvmFunction,
|
||||||
|
listOf(exceptionRawPtr),
|
||||||
|
Lifetime.GLOBAL, exceptionHandler)
|
||||||
|
|
||||||
|
call(context.llvm.cxaEndCatchFunction, listOf())
|
||||||
|
return exception
|
||||||
|
}
|
||||||
|
|
||||||
inline fun ifThenElse(
|
inline fun ifThenElse(
|
||||||
condition: LLVMValueRef,
|
condition: LLVMValueRef,
|
||||||
thenValue: LLVMValueRef,
|
thenValue: LLVMValueRef,
|
||||||
@@ -1273,6 +1316,14 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
|
|||||||
origin = context.stdlibModule.llvmSymbolOrigin
|
origin = context.stdlibModule.llvmSymbolOrigin
|
||||||
)).bitcast(int8TypePtr)
|
)).bitcast(int8TypePtr)
|
||||||
|
|
||||||
|
private val objcNSExceptionRtti: ConstPointer by lazy {
|
||||||
|
constPointer(importGlobal(
|
||||||
|
"OBJC_EHTYPE_\$_NSException", // typeinfo for NSException*
|
||||||
|
int8TypePtr,
|
||||||
|
origin = context.stdlibModule.llvmSymbolOrigin
|
||||||
|
)).bitcast(int8TypePtr)
|
||||||
|
}
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
//-------------------------------------------------------------------------//
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+6
-5
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.ir.util.*
|
|||||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||||
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
||||||
|
import org.jetbrains.kotlin.konan.ForeignExceptionMode
|
||||||
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
|
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
|
||||||
import org.jetbrains.kotlin.konan.target.Family
|
import org.jetbrains.kotlin.konan.target.Family
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
@@ -2311,11 +2312,11 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
|||||||
|
|
||||||
private fun call(function: IrFunction, llvmFunction: LLVMValueRef, args: List<LLVMValueRef>,
|
private fun call(function: IrFunction, llvmFunction: LLVMValueRef, args: List<LLVMValueRef>,
|
||||||
resultLifetime: Lifetime): LLVMValueRef {
|
resultLifetime: Lifetime): LLVMValueRef {
|
||||||
val exceptionHandler = if (function.hasAnnotation(RuntimeNames.filterExceptions)) {
|
|
||||||
functionGenerationContext.filteringExceptionHandler(currentCodeContext)
|
val exceptionHandler = function.annotations.findAnnotation(RuntimeNames.filterExceptions)?.let {
|
||||||
} else {
|
val foreignExceptionMode = ForeignExceptionMode.byValue(it.getAnnotationValueOrNull<String>("mode"))
|
||||||
currentCodeContext.exceptionHandler
|
functionGenerationContext.filteringExceptionHandler(currentCodeContext, foreignExceptionMode)
|
||||||
}
|
} ?: currentCodeContext.exceptionHandler
|
||||||
|
|
||||||
val result = call(llvmFunction, args, resultLifetime, exceptionHandler)
|
val result = call(llvmFunction, args, resultLifetime, exceptionHandler)
|
||||||
if (!function.isSuspend && function.returnType.isNothing()) {
|
if (!function.isSuspend && function.returnType.isNothing()) {
|
||||||
|
|||||||
+6
-1
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.backend.konan.*
|
|||||||
import org.jetbrains.kotlin.backend.konan.cgen.*
|
import org.jetbrains.kotlin.backend.konan.cgen.*
|
||||||
import org.jetbrains.kotlin.backend.konan.descriptors.allOverriddenFunctions
|
import org.jetbrains.kotlin.backend.konan.descriptors.allOverriddenFunctions
|
||||||
import org.jetbrains.kotlin.backend.konan.descriptors.isFromInteropLibrary
|
import org.jetbrains.kotlin.backend.konan.descriptors.isFromInteropLibrary
|
||||||
|
import org.jetbrains.kotlin.backend.konan.descriptors.konanLibrary
|
||||||
import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName
|
import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName
|
||||||
import org.jetbrains.kotlin.backend.konan.ir.*
|
import org.jetbrains.kotlin.backend.konan.ir.*
|
||||||
import org.jetbrains.kotlin.backend.konan.ir.companionObject
|
import org.jetbrains.kotlin.backend.konan.ir.companionObject
|
||||||
@@ -50,6 +51,7 @@ import org.jetbrains.kotlin.name.FqName
|
|||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||||
|
import org.jetbrains.kotlin.konan.ForeignExceptionMode
|
||||||
|
|
||||||
internal class InteropLowering(context: Context) : FileLoweringPass {
|
internal class InteropLowering(context: Context) : FileLoweringPass {
|
||||||
// TODO: merge these lowerings.
|
// TODO: merge these lowerings.
|
||||||
@@ -1006,7 +1008,10 @@ private class InteropTransformer(val context: Context, override val irFile: IrFi
|
|||||||
|
|
||||||
if (function.annotations.hasAnnotation(RuntimeNames.cCall)) {
|
if (function.annotations.hasAnnotation(RuntimeNames.cCall)) {
|
||||||
context.llvmImports.add(function.llvmSymbolOrigin)
|
context.llvmImports.add(function.llvmSymbolOrigin)
|
||||||
return generateWithStubs { generateCCall(expression, builder, isInvoke = false) }
|
val exceptionMode = ForeignExceptionMode.byValue(
|
||||||
|
function.module.konanLibrary?.manifestProperties?.getProperty(ForeignExceptionMode.manifestKey)
|
||||||
|
)
|
||||||
|
return generateWithStubs { generateCCall(expression, builder, isInvoke = false, exceptionMode) }
|
||||||
}
|
}
|
||||||
|
|
||||||
val failCompilation = { msg: String -> context.reportCompilationError(msg, irFile, expression) }
|
val failCompilation = { msg: String -> context.reportCompilationError(msg, irFile, expression) }
|
||||||
|
|||||||
@@ -3626,6 +3626,22 @@ if (PlatformInfo.isAppleTarget(project)) {
|
|||||||
it.headers "$projectDir/interop/objc/msg_send/messaging.h"
|
it.headers "$projectDir/interop/objc/msg_send/messaging.h"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createInterop("foreignException") {
|
||||||
|
it.defFile 'interop/objc/foreignException/objc_wrap.def'
|
||||||
|
it.headers "$projectDir/interop/objc/foreignException/objc_wrap.h"
|
||||||
|
}
|
||||||
|
|
||||||
|
createInterop("foreignExceptionMode_default") {
|
||||||
|
it.defFile 'interop/objc/foreignException/objcExceptionMode.def'
|
||||||
|
it.headers "$projectDir/interop/objc/foreignException/objc_wrap.h"
|
||||||
|
}
|
||||||
|
|
||||||
|
createInterop("foreignExceptionMode_wrap") {
|
||||||
|
it.defFile 'interop/objc/foreignException/objcExceptionMode.def'
|
||||||
|
it.headers "$projectDir/interop/objc/foreignException/objc_wrap.h"
|
||||||
|
it.extraOpts '-Xforeign-exception-mode', "objc-wrap"
|
||||||
|
}
|
||||||
|
|
||||||
createInterop("objcKt34467") {
|
createInterop("objcKt34467") {
|
||||||
it.defFile 'interop/objc/kt34467/module_library.def'
|
it.defFile 'interop/objc/kt34467/module_library.def'
|
||||||
def moduleMap = file("interop/objc/kt34467/module_library.modulemap").absolutePath
|
def moduleMap = file("interop/objc/kt34467/module_library.modulemap").absolutePath
|
||||||
@@ -3995,6 +4011,59 @@ if (PlatformInfo.isAppleTarget(project)) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interopTest("interop_objc_foreignException") {
|
||||||
|
source = "interop/objc/foreignException/objc_wrap.kt"
|
||||||
|
interop = 'foreignException'
|
||||||
|
|
||||||
|
doBeforeBuild {
|
||||||
|
mkdir(buildDir)
|
||||||
|
execKonanClang(project.target) {
|
||||||
|
args "$projectDir/interop/objc/foreignException/objc_wrap.m"
|
||||||
|
args "-lobjc", '-fobjc-arc'
|
||||||
|
args '-fPIC', '-shared', '-o', "$buildDir/libobjcexception.dylib"
|
||||||
|
}
|
||||||
|
if (project.target instanceof KonanTarget.IOS_X64) {
|
||||||
|
UtilsKt.codesign(project, "$buildDir/libobjcexception.dylib")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interopTest("interop_objc_foreignExceptionMode_wrap") {
|
||||||
|
source = "interop/objc/foreignException/objcExceptionMode.kt"
|
||||||
|
interop = 'foreignExceptionMode_wrap'
|
||||||
|
goldValue = "OK: ForeignException\n"
|
||||||
|
|
||||||
|
doBeforeBuild {
|
||||||
|
mkdir(buildDir)
|
||||||
|
execKonanClang(project.target) {
|
||||||
|
args "$projectDir/interop/objc/foreignException/objc_wrap.m"
|
||||||
|
args "-lobjc", '-fobjc-arc'
|
||||||
|
args '-fPIC', '-shared', '-o', "$buildDir/libobjcexception.dylib"
|
||||||
|
}
|
||||||
|
if (project.target instanceof KonanTarget.IOS_X64) {
|
||||||
|
UtilsKt.codesign(project, "$buildDir/libobjcexception.dylib")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interopTest("interop_objc_foreignExceptionMode_default") {
|
||||||
|
source = "interop/objc/foreignException/objcExceptionMode.kt"
|
||||||
|
interop = 'foreignExceptionMode_default'
|
||||||
|
goldValue = "OK: Ends with uncaught exception handler\n"
|
||||||
|
|
||||||
|
doBeforeBuild {
|
||||||
|
mkdir(buildDir)
|
||||||
|
execKonanClang(project.target) {
|
||||||
|
args "$projectDir/interop/objc/foreignException/objc_wrap.m"
|
||||||
|
args "-lobjc", '-fobjc-arc'
|
||||||
|
args '-fPIC', '-shared', '-o', "$buildDir/libobjcexception.dylib"
|
||||||
|
}
|
||||||
|
if (project.target instanceof KonanTarget.IOS_X64) {
|
||||||
|
UtilsKt.codesign(project, "$buildDir/libobjcexception.dylib")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
interopTest("interop_objc_kt34467") {
|
interopTest("interop_objc_kt34467") {
|
||||||
source = "interop/objc/kt34467/foo.kt"
|
source = "interop/objc/kt34467/foo.kt"
|
||||||
interop = 'objcKt34467'
|
interop = 'objcKt34467'
|
||||||
|
|||||||
@@ -18,7 +18,13 @@ fun exc_handler(x: Any?) : Unit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun main() {
|
fun main() {
|
||||||
|
// This does not work anymore, as NSException propagated as ForeignException
|
||||||
|
// so we got `Uncaught Kotlin exception` instead. Use normal try/catch.
|
||||||
objc_setUncaughtExceptionHandler(staticCFunction(::exc_handler))
|
objc_setUncaughtExceptionHandler(staticCFunction(::exc_handler))
|
||||||
|
|
||||||
println(NSJSONSerialization())
|
try {
|
||||||
|
println(NSJSONSerialization())
|
||||||
|
} catch (e: Exception) { // ForeignException expected
|
||||||
|
println(e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
language = Objective-C
|
||||||
|
headerFilter = **/objc_wrap.h
|
||||||
|
linkerOpts = -lobjcexception
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Test different behavior depending on foreignExceptionMode option
|
||||||
|
*/
|
||||||
|
|
||||||
|
import kotlin.test.*
|
||||||
|
import objcExceptionMode.*
|
||||||
|
import kotlinx.cinterop.*
|
||||||
|
import platform.objc.*
|
||||||
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
|
@Suppress("VARIABLE_WITH_REDUNDANT_INITIALIZER")
|
||||||
|
@Test fun testKT35056() {
|
||||||
|
val name = "Some native exception"
|
||||||
|
val reason = "Illegal value"
|
||||||
|
var finallyBlockTest = "FAILED"
|
||||||
|
var catchBlockTest = "FAILED"
|
||||||
|
try {
|
||||||
|
raiseExc(name, reason)
|
||||||
|
assertNotEquals("FAILED", catchBlockTest) // shall not get here anyway
|
||||||
|
} catch (e: ForeignException) {
|
||||||
|
val ret = logExc(e.nativeException) // return NSException name
|
||||||
|
assertEquals(name, ret)
|
||||||
|
assertEquals("$name:: $reason", e.message)
|
||||||
|
println("OK: ForeignException")
|
||||||
|
catchBlockTest = "PASSED"
|
||||||
|
} finally {
|
||||||
|
finallyBlockTest = "PASSED"
|
||||||
|
}
|
||||||
|
assertEquals("PASSED", catchBlockTest)
|
||||||
|
assertEquals("PASSED", finallyBlockTest)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("UNUSED_PARAMETER")
|
||||||
|
fun abnormal_handler(x: Any?) : Unit {
|
||||||
|
println("OK: Ends with uncaught exception handler")
|
||||||
|
exitProcess(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun main() {
|
||||||
|
// Depending on the `foreignxceptionMode` option (def file or cinterop cli) this test should ends
|
||||||
|
// normally with `ForeignException` handled or abnormally with `abnormal_handler`.
|
||||||
|
// Test shall validate output (golden value) from `abnormal_handler`.
|
||||||
|
|
||||||
|
objc_setUncaughtExceptionHandler(staticCFunction(::abnormal_handler))
|
||||||
|
|
||||||
|
testKT35056()
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
language = Objective-C
|
||||||
|
headerFilter = **/objc_wrap.h
|
||||||
|
linkerOpts = -lobjcexception
|
||||||
|
foreignExceptionMode = objc-wrap
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
void raiseExc(id name, id reason);
|
||||||
|
id logExc(id exception);
|
||||||
|
|
||||||
|
@interface Foo : NSObject
|
||||||
|
- (void)instanceMethodThrow:(id)name reason:(id)reason;
|
||||||
|
+ (void)classMethodThrow:(id)name reason:(id)reason;
|
||||||
|
@end
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Test different types of callable with foreignExceptionMode=objc-wrap
|
||||||
|
*/
|
||||||
|
|
||||||
|
import kotlin.test.*
|
||||||
|
//import objcTests.*
|
||||||
|
import objc_wrap.*
|
||||||
|
import kotlinx.cinterop.*
|
||||||
|
|
||||||
|
fun testInner(name: String, reason: String) {
|
||||||
|
var finallyBlockTest = "FAILED"
|
||||||
|
var catchBlockTest = "NOT EXPECTED"
|
||||||
|
try {
|
||||||
|
raiseExc(name, reason)
|
||||||
|
} catch (e: RuntimeException) {
|
||||||
|
catchBlockTest = "This shouldn't happen"
|
||||||
|
} finally {
|
||||||
|
finallyBlockTest = "PASSED"
|
||||||
|
}
|
||||||
|
assertEquals("NOT EXPECTED", catchBlockTest)
|
||||||
|
assertEquals("PASSED", finallyBlockTest)
|
||||||
|
}
|
||||||
|
|
||||||
|
typealias CallMe = (String, String) -> Unit
|
||||||
|
|
||||||
|
@Test fun testExceptionWrap(raise: CallMe) {
|
||||||
|
val name = "Some native exception"
|
||||||
|
val reason = "Illegal value"
|
||||||
|
var finallyBlockTest = "FAILED"
|
||||||
|
var catchBlockTest = "FAILED"
|
||||||
|
try {
|
||||||
|
raise(name, reason)
|
||||||
|
} catch (e: ForeignException) {
|
||||||
|
val ret = logExc(e.nativeException) // return NSException name
|
||||||
|
assertEquals(name, ret)
|
||||||
|
assertEquals("$name:: $reason", e.message)
|
||||||
|
catchBlockTest = "PASSED"
|
||||||
|
} finally {
|
||||||
|
finallyBlockTest = "PASSED"
|
||||||
|
}
|
||||||
|
assertEquals("PASSED", catchBlockTest)
|
||||||
|
assertEquals("PASSED", finallyBlockTest)
|
||||||
|
}
|
||||||
|
|
||||||
|
class Bar() : Foo()
|
||||||
|
|
||||||
|
fun main() {
|
||||||
|
testExceptionWrap(::raiseExc) // simple
|
||||||
|
testExceptionWrap(::testInner) // nested try block
|
||||||
|
testExceptionWrap(Foo::classMethodThrow) // class method
|
||||||
|
testExceptionWrap(Foo()::instanceMethodThrow) // instance method
|
||||||
|
testExceptionWrap(Bar()::instanceMethodThrow) // fake override
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#include "objc_wrap.h"
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
void raiseExc(id name, id reason) {
|
||||||
|
[NSException raise:name format:@"%@", reason];
|
||||||
|
}
|
||||||
|
|
||||||
|
id logExc(id exc) {
|
||||||
|
assert([exc isKindOfClass:[NSException class]]);
|
||||||
|
return ((NSException*)exc).name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@implementation Foo : NSObject
|
||||||
|
- (void) instanceMethodThrow:(id)name reason:(id)reason {
|
||||||
|
raiseExc(name, reason);
|
||||||
|
}
|
||||||
|
+ (void) classMethodThrow:(id)name reason:(id)reason {
|
||||||
|
raiseExc(name, reason);
|
||||||
|
}
|
||||||
|
@end
|
||||||
@@ -69,6 +69,8 @@ add_executable(runtime
|
|||||||
src/main/cpp/ObjCInteropUtils.mm
|
src/main/cpp/ObjCInteropUtils.mm
|
||||||
src/main/cpp/ObjCExportCollectionUtils.mm
|
src/main/cpp/ObjCExportCollectionUtils.mm
|
||||||
src/main/cpp/ObjCExportErrors.mm
|
src/main/cpp/ObjCExportErrors.mm
|
||||||
|
src/main/cpp/ObjCExportExceptionDetails.mm
|
||||||
|
|
||||||
src/objc/cpp/ObjCExportNumbers.mm
|
src/objc/cpp/ObjCExportNumbers.mm
|
||||||
src/objc/cpp/ObjCExportClasses.mm
|
src/objc/cpp/ObjCExportClasses.mm
|
||||||
src/objc/cpp/ObjCExportCollections.mm
|
src/objc/cpp/ObjCExportCollections.mm
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import "Memory.h"
|
||||||
|
#import "Types.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
OBJ_GETTER(Kotlin_ObjCExport_ExceptionDetails, KRef thiz, KRef exceptionHolder);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import "Memory.h"
|
||||||
|
#import "ObjCExportExceptionDetails.h"
|
||||||
|
#import "ObjCExport.h"
|
||||||
|
|
||||||
|
#if KONAN_OBJC_INTEROP
|
||||||
|
|
||||||
|
#import <Foundation/NSException.h>
|
||||||
|
|
||||||
|
//! TODO: Use not_null signature.
|
||||||
|
OBJ_GETTER(Kotlin_ObjCExport_ExceptionDetails, KRef /*thiz*/, KRef exceptionHolder) {
|
||||||
|
if (NSException* exception = (NSException*)Kotlin_ObjCExport_refToObjC(exceptionHolder)) {
|
||||||
|
RuntimeAssert([exception isKindOfClass:[NSException class]], "Illegal type: NSException expected");
|
||||||
|
NSString* ret = [NSString stringWithFormat: @"%@:: %@", exception.name, exception.reason];
|
||||||
|
RETURN_RESULT_OF(Kotlin_Interop_CreateKStringFromNSString, ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_OBJ(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else // KONAN_OBJC_INTEROP
|
||||||
|
|
||||||
|
OBJ_GETTER(Kotlin_ObjCExport_ExceptionDetails, KRef /*thiz*/, KRef /*exceptionHolder*/) {
|
||||||
|
RETURN_OBJ(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -116,7 +116,7 @@ annotation class Independent
|
|||||||
*/
|
*/
|
||||||
@Target(AnnotationTarget.FUNCTION)
|
@Target(AnnotationTarget.FUNCTION)
|
||||||
@Retention(AnnotationRetention.BINARY)
|
@Retention(AnnotationRetention.BINARY)
|
||||||
@PublishedApi internal annotation class FilterExceptions
|
@PublishedApi internal annotation class FilterExceptions(val mode: String = "terminate")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks a class whose instances to be added to the list of leak detector candidates.
|
* Marks a class whose instances to be added to the list of leak detector candidates.
|
||||||
|
|||||||
@@ -31,3 +31,22 @@ class KonanExternalToolFailure(message: String, val toolName: String, cause: Thr
|
|||||||
*/
|
*/
|
||||||
class MissingXcodeException(message: String, cause: Throwable? = null) : KonanException(message, cause)
|
class MissingXcodeException(message: String, cause: Throwable? = null) : KonanException(message, cause)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Native exception handling in Kotlin: terminate, wrap, etc.
|
||||||
|
* Foreign exceptionMode mode is per library option: controlled by cinterop command-line option or def file property
|
||||||
|
* than stored in klib manifest and used by compiler to generate appropriate handler.
|
||||||
|
*/
|
||||||
|
class ForeignExceptionMode {
|
||||||
|
companion object {
|
||||||
|
val manifestKey = "foreignExceptionMode"
|
||||||
|
val default = Mode.TERMINATE
|
||||||
|
fun byValue(value: String?): Mode = value?.let {
|
||||||
|
Mode.values().find { it.value == value }
|
||||||
|
?: throw IllegalArgumentException("Illegal ForeignExceptionMode $value")
|
||||||
|
} ?: default
|
||||||
|
}
|
||||||
|
enum class Mode(val value: String) {
|
||||||
|
TERMINATE("terminate"),
|
||||||
|
OBJC_WRAP("objc-wrap")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -113,6 +113,11 @@ class DefFile(val file:File?, val config:DefFileConfig, val manifestAddendProper
|
|||||||
val disableDesignatedInitializerChecks by lazy {
|
val disableDesignatedInitializerChecks by lazy {
|
||||||
properties.getProperty("disableDesignatedInitializerChecks")?.toBoolean() ?: false
|
properties.getProperty("disableDesignatedInitializerChecks")?.toBoolean() ?: false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val foreignExceptionMode by lazy {
|
||||||
|
properties.getProperty("foreignExceptionMode")
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user