diff --git a/translator/src/main/kotlin/org/kotlinnative/translator/BlockCodegen.kt b/translator/src/main/kotlin/org/kotlinnative/translator/BlockCodegen.kt index e3b12a32f55..0af24d3fae3 100644 --- a/translator/src/main/kotlin/org/kotlinnative/translator/BlockCodegen.kt +++ b/translator/src/main/kotlin/org/kotlinnative/translator/BlockCodegen.kt @@ -90,7 +90,7 @@ abstract class BlockCodegen(val state: TranslationState, val variableManager: Va fun evaluateExpression(expr: PsiElement?, scopeDepth: Int): LLVMSingleValue? { return when (expr) { is KtBlockExpression -> { - expressionWalker(expr.firstChild, null, scopeDepth + 1) + expressionWalker(expr.firstChild, breakLabel = null, scopeDepth = scopeDepth + 1) return null } is KtBinaryExpression -> evaluateBinaryExpression(expr, scopeDepth) @@ -636,6 +636,18 @@ abstract class BlockCodegen(val state: TranslationState, val variableManager: Va codeBuilder.storeVariable(firstOp, resultOp) return oldValue } + KtTokens.EXCLEXCL -> { + val nullLabel = codeBuilder.getNewLabel(prefix = "nullCheck") + val notNullLabel = codeBuilder.getNewLabel(prefix = "nullCheck") + val nullCheck = codeBuilder.nullCheck(firstOp) + codeBuilder.addCondition(nullCheck, nullLabel, notNullLabel) + + codeBuilder.markWithLabel(nullLabel) + codeBuilder.addExceptionCall("KotlinNullPointerException") + codeBuilder.addUnconditionalJump(notNullLabel) + codeBuilder.markWithLabel(notNullLabel) + return firstOp + } else -> throw UnsupportedOperationException() } } @@ -895,7 +907,8 @@ abstract class BlockCodegen(val state: TranslationState, val variableManager: Va when (expressionType) { is LLVMVoidType, - is LLVMNullType -> {} + is LLVMNullType -> { + } is LLVMReferenceType -> codeBuilder.allocStaticVar(resultVariable, asValue = true) else -> codeBuilder.allocStackVar(resultVariable, asValue = true) } diff --git a/translator/src/main/kotlin/org/kotlinnative/translator/llvm/LLVMBuilder.kt b/translator/src/main/kotlin/org/kotlinnative/translator/llvm/LLVMBuilder.kt index 63d994de64d..7207507eef3 100644 --- a/translator/src/main/kotlin/org/kotlinnative/translator/llvm/LLVMBuilder.kt +++ b/translator/src/main/kotlin/org/kotlinnative/translator/llvm/LLVMBuilder.kt @@ -1,9 +1,6 @@ package org.kotlinnative.translator.llvm -import org.kotlinnative.translator.llvm.types.LLVMCharType -import org.kotlinnative.translator.llvm.types.LLVMIntType -import org.kotlinnative.translator.llvm.types.LLVMStringType -import org.kotlinnative.translator.llvm.types.LLVMType +import org.kotlinnative.translator.llvm.types.* import java.util.* class LLVMBuilder(val arm: Boolean = false) { @@ -18,22 +15,29 @@ class LLVMBuilder(val arm: Boolean = false) { initBuilder() } + var exceptions: Map = mapOf() + private fun initBuilder() { val declares = arrayOf( "declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture, i8* nocapture readonly, i64, i32, i1)", - "declare i8* @malloc_static(i32)") + "declare i8* @malloc_static(i32)", + "declare i32 @printf(i8*, ...)", + "declare void @abort()") declares.forEach { globalCode.appendln(it) } + exceptions = mapOf( + Pair("KotlinNullPointerException", initializeString("Exception in thread main kotlin.KotlinNullPointerException"))) + val funcAttributes = """attributes #0 = { nounwind "stack-protector-buffer-size"="8" "target-cpu"="cortex-m3" "target-features"="+hwdiv,+strict-align" }""" if (arm) { globalCode.appendln(funcAttributes) } } - fun getNewVariable(type: LLVMType, pointer: Int = 0, kotlinName: String? = null, scope: LLVMScope = LLVMRegisterScope()): LLVMVariable { + fun getNewVariable(type: LLVMType, pointer: Int = 0, kotlinName: String? = null, scope: LLVMScope = LLVMRegisterScope(), prefix: String = "var"): LLVMVariable { variableCount++ - return LLVMVariable("var$variableCount", type, kotlinName, scope, pointer) + return LLVMVariable("${prefix}$variableCount", type, kotlinName, scope, pointer) } fun getNewLabel(scope: LLVMScope = LLVMRegisterScope(), prefix: String): LLVMLabel { @@ -77,9 +81,15 @@ class LLVMBuilder(val arm: Boolean = false) { localCode.appendln("ret $type $value") } + private fun initializeString(string: String): LLVMVariable { + val result = getNewVariable(LLVMStringType(string.length), pointer = 0, scope = LLVMVariableScope(), prefix = "exceptions.str.") + addStringConstant(result, string) + return result + } + fun addStringConstant(variable: LLVMVariable, value: String) { val type = variable.type as LLVMStringType - globalCode.appendln("$variable = private unnamed_addr constant ${type.fullType()} c\"$value\\00\", align 1") + globalCode.appendln("$variable = private unnamed_addr constant ${type.fullType()} c\"${value.replace("\"", "\\\"")}\\00\", align 1") } fun addGlobalInitialize(target: LLVMVariable, fields: ArrayList, initializers: Map, classType: LLVMType) { @@ -121,6 +131,17 @@ class LLVMBuilder(val arm: Boolean = false) { localCode.appendln(code) } + fun nullCheck(variable: LLVMVariable): LLVMVariable { + val result = getNewVariable(LLVMBooleanType(), pointer = 0) + + val loaded = getNewVariable(variable.type, pointer = variable.pointer - 1) + loadVariable(loaded, variable) + + val code = "$result = icmp eq ${loaded.getType()} null, $loaded" + localCode.appendln(code) + return result + } + fun addComment(comment: String) { localCode.appendln("; " + comment) } @@ -218,6 +239,17 @@ class LLVMBuilder(val arm: Boolean = false) { return empty } + fun addExceptionCall(exceptionName: String) { + val exception = exceptions[exceptionName] + val printResult = getNewVariable(LLVMIntType(), pointer = 0) + localCode.appendln("$printResult = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds (${(exception!!.type as LLVMStringType).fullType()}* $exception, i32 0, i32 0))") + addFunctionCall(LLVMVariable("abort", LLVMVoidType(), scope = LLVMVariableScope()), emptyList()) + } + + fun addFunctionCall(functionName: LLVMVariable, arguments: List) { + localCode.appendln("call ${functionName.type} $functionName(${arguments.joinToString { it -> "${it.type} $it" }})") + } + fun memcpy(castedDst: LLVMVariable, castedSrc: LLVMVariable, size: Int, align: Int = 4, volatile: Boolean = false) { val code = "call void @llvm.memcpy.p0i8.p0i8.i64(i8* $castedDst, i8* $castedSrc, i64 $size, i32 $align, i1 $volatile)" localCode.appendln(code) diff --git a/translator/src/test/kotlin/tests/input/nullable_1.txt b/translator/src/test/kotlin/tests/input/nullable_1.txt index 77d39cc8c32..3bab68cb5b7 100644 --- a/translator/src/test/kotlin/tests/input/nullable_1.txt +++ b/translator/src/test/kotlin/tests/input/nullable_1.txt @@ -1,2 +1,3 @@ -nullable_test_Int(1) == 1 -nullable_test_2_Int(4) == 5 +nullable_1_test_Int(1) == 1 +nullable_1_test_2_Int(4) == 5 +nullable_1_test_2_npe_operator_Int(23) == 578 diff --git a/translator/src/test/kotlin/tests/kotlin/nullable_1.kt b/translator/src/test/kotlin/tests/kotlin/nullable_1.kt index 2ec1b26bbb2..f916d101a78 100644 --- a/translator/src/test/kotlin/tests/kotlin/nullable_1.kt +++ b/translator/src/test/kotlin/tests/kotlin/nullable_1.kt @@ -1,16 +1,21 @@ +class nullable_1_MyAwesomeClass(var i: Int) -class MyAwesomeClass(var i: Int) - -fun nullable_test(i: Int): Int { - var x: MyAwesomeClass? = null - x = MyAwesomeClass(i) +fun nullable_1_test(i: Int): Int { + var x: nullable_1_MyAwesomeClass? = null + x = nullable_1_MyAwesomeClass(i) return x.i } -fun nullable_test_2(i: Int): Int { - var x: MyAwesomeClass? = MyAwesomeClass(i) +fun nullable_1_test_2(i: Int): Int { + var x: nullable_1_MyAwesomeClass? = nullable_1_MyAwesomeClass(i) x = null - x = MyAwesomeClass(i) + x = nullable_1_MyAwesomeClass(i) return x.i + 1 +} + +fun nullable_1_test_2_npe_operator(i: Int): Int { + val x: nullable_1_MyAwesomeClass? = nullable_1_MyAwesomeClass(i) + x!! + return x.i + 555 } \ No newline at end of file