From 4535c94c284364eb77ce3705b56966e8dd64a3f4 Mon Sep 17 00:00:00 2001 From: Alexey Stepanov Date: Fri, 19 Aug 2016 16:36:07 +0300 Subject: [PATCH] translator: add secondary constructors --- .../kotlinnative/translator/BlockCodegen.kt | 53 ++++++---------- .../kotlinnative/translator/ClassCodegen.kt | 9 ++- .../kotlinnative/translator/ObjectCodegen.kt | 4 ++ .../kotlinnative/translator/StructCodegen.kt | 61 +++++++++++++++---- .../translator/llvm/LLVMBuilder.kt | 25 ++++++++ .../llvm/types/LLVMReferenceType.kt | 6 -- .../translator/llvm/types/LLVMType.kt | 8 ++- .../tests/input/secondary_constructor_1.txt | 1 + .../tests/kotlin/secondary_constructor_1.kt | 10 +++ 9 files changed, 119 insertions(+), 58 deletions(-) create mode 100644 translator/src/test/kotlin/tests/input/secondary_constructor_1.txt create mode 100644 translator/src/test/kotlin/tests/kotlin/secondary_constructor_1.kt diff --git a/translator/src/main/kotlin/org/kotlinnative/translator/BlockCodegen.kt b/translator/src/main/kotlin/org/kotlinnative/translator/BlockCodegen.kt index 2aeaf7b6ce5..73cd707e3a8 100644 --- a/translator/src/main/kotlin/org/kotlinnative/translator/BlockCodegen.kt +++ b/translator/src/main/kotlin/org/kotlinnative/translator/BlockCodegen.kt @@ -31,7 +31,7 @@ abstract class BlockCodegen(val state: TranslationState, val variableManager: Va var returnType: LLVMVariable? = null var wasReturnOnTopLevel = false - protected fun evaluateCodeBlock(expr: PsiElement?, startLabel: LLVMLabel? = null, nextIterationLabel: LLVMLabel? = null, breakLabel: LLVMLabel? = null, scopeDepth: Int = 0, isBlock: Boolean = true) { + fun evaluateCodeBlock(expr: PsiElement?, startLabel: LLVMLabel? = null, nextIterationLabel: LLVMLabel? = null, breakLabel: LLVMLabel? = null, scopeDepth: Int = 0, isBlock: Boolean = true) { codeBuilder.markWithLabel(startLabel) if (isBlock) { expressionWalker(expr, breakLabel, scopeDepth) @@ -116,6 +116,14 @@ abstract class BlockCodegen(val state: TranslationState, val variableManager: Va } } + fun evaluateConstructorDelegationReferenceExpression(expr: KtConstructorDelegationReferenceExpression, structCodegen: StructCodegen, constructorArguments: List, scopeDepth: Int): LLVMSingleValue? { + val targetCall = state.bindingContext.get(BindingContext.CALL, expr) + + val names = parseValueArguments(targetCall!!.valueArguments, scopeDepth) + val args = loadArgsIfRequired(names, constructorArguments) + return evaluateConstructorCallExpression(LLVMVariable(structCodegen.fullName + structCodegen.primaryConstructorIndex, structCodegen.type, scope = LLVMVariableScope()), args) + } + private fun evaluateThisExpression(): LLVMSingleValue? { return variableManager["this"] } @@ -247,7 +255,7 @@ abstract class BlockCodegen(val state: TranslationState, val variableManager: Va else -> LLVMVariable("type", standardType, pointer = 0) } - val args = mutableListOf(loadArgumentIfRequired(receiverExpression, typeThisArgument)) + val args = mutableListOf(codeBuilder.loadArgumentIfRequired(receiverExpression, typeThisArgument)) args.addAll(loadArgsIfRequired(names, extensionCodegen.args)) return evaluateFunctionCallExpression(LLVMVariable(extensionCodegen.fullName, extensionCodegen.returnType!!.type, scope = LLVMVariableScope()), args) } @@ -315,7 +323,7 @@ abstract class BlockCodegen(val state: TranslationState, val variableManager: Va val arrayActionType = if (callMaker.callType == Call.CallType.ARRAY_SET_METHOD) "set" else "get" val explicitReceiver = callMaker.explicitReceiver as ExpressionReceiver val receiver = evaluateExpression(explicitReceiver.expression, scope)!! as LLVMVariable - val pureReceiver = downLoadArgument(receiver, 1) + val pureReceiver = codeBuilder.downLoadArgument(receiver, 1) val targetClassName = (receiver.type as LLVMReferenceType).type @@ -397,7 +405,6 @@ abstract class BlockCodegen(val state: TranslationState, val variableManager: Va val names = parseArgList(expr, scopeDepth) var name = expr.firstChild.firstChild.text val external = state.externalFunctions.containsKey(name) - val function = "$name${if (names.size > 0 && !external) "_${names.joinToString(separator = "_", transform = { it.type!!.mangle() })}" else ""}" if (state.functions.containsKey(function) || state.externalFunctions.containsKey(function)) { @@ -408,8 +415,9 @@ abstract class BlockCodegen(val state: TranslationState, val variableManager: Va if (state.classes.containsKey(name) || classScope?.structName == name) { val descriptor = state.classes[name] ?: classScope ?: return null - val args = loadArgsIfRequired(names, descriptor.constructorFields) - return evaluateConstructorCallExpression(LLVMVariable(descriptor.fullName, descriptor.type, scope = LLVMVariableScope()), args) + val detectedConstructor = LLVMType.mangleFunctionArguments(names) + val args = loadArgsIfRequired(names, descriptor.constructorFields[detectedConstructor]!!) + return evaluateConstructorCallExpression(LLVMVariable(descriptor.fullName + detectedConstructor, descriptor.type, scope = LLVMVariableScope()), args) } val localFunction = variableManager[name] @@ -437,7 +445,7 @@ abstract class BlockCodegen(val state: TranslationState, val variableManager: Va val nestedConstructor = classScope?.nestedClasses?.get(expr.calleeExpression!!.text) if (nestedConstructor != null) { - val args = loadArgsIfRequired(names, nestedConstructor.constructorFields) + val args = loadArgsIfRequired(names, nestedConstructor.constructorFields[nestedConstructor.primaryConstructorIndex]!!) return evaluateConstructorCallExpression(LLVMVariable(nestedConstructor.fullName, nestedConstructor.type, scope = LLVMVariableScope()), args) } @@ -445,7 +453,6 @@ abstract class BlockCodegen(val state: TranslationState, val variableManager: Va name = "${containingClass.fullName}.$function" val method = containingClass.methods[name]!! val args = mutableListOf() - val leftName = (expr.context as? KtDotQualifiedExpression)?.receiverExpression?.text if (caller != null) { args.add(caller) @@ -467,7 +474,6 @@ abstract class BlockCodegen(val state: TranslationState, val variableManager: Va if (location.size > 0) { val type = LLVMReferenceType(name, prefix = "class") type.location.addAll(location) - return resolveClassOrObjectLocation(type) } @@ -510,34 +516,9 @@ abstract class BlockCodegen(val state: TranslationState, val variableManager: Va return null } - private fun loadArgumentIfRequired(value: LLVMSingleValue, argument: LLVMVariable): LLVMSingleValue { - var result = value - - while (argument.pointer < result.pointer) { - result = codeBuilder.getNewVariable(argument.type, pointer = result.pointer - 1) - codeBuilder.loadVariable(result, value as LLVMVariable) - } - - when (value.type) { - is LLVMStringType -> if (!(value.type as LLVMStringType).isLoaded) { - val newVariable = codeBuilder.getNewVariable(value.type!!, pointer = result.pointer + 1) - codeBuilder.allocStackVar(newVariable, asValue = true) - codeBuilder.copyVariable(result as LLVMVariable, newVariable) - - result = codeBuilder.getNewVariable(argument.type, pointer = newVariable.pointer - 1) - codeBuilder.loadVariable(result, newVariable) - } - } - - return result - } - - private fun downLoadArgument(value: LLVMSingleValue, pointer: Int): LLVMSingleValue = - loadArgumentIfRequired(value, LLVMVariable("", value.type!!, pointer = pointer)) - private fun loadArgsIfRequired(names: List, args: List) = names.mapIndexed(fun(i: Int, value: LLVMSingleValue): LLVMSingleValue { - return loadArgumentIfRequired(value, args[i]) + return codeBuilder.loadArgumentIfRequired(value, args[i]) }).toList() private fun evaluateConstructorCallExpression(function: LLVMVariable, names: List): LLVMSingleValue? { @@ -968,7 +949,7 @@ abstract class BlockCodegen(val state: TranslationState, val variableManager: Va private fun evaluateIfOperator(element: KtIfExpression, scopeDepth: Int, isExpression: Boolean = true): LLVMVariable? { val conditionResult = evaluateExpression(element.condition, scopeDepth)!! - val conditionNativeResult = downLoadArgument(conditionResult, 0) + val conditionNativeResult = codeBuilder.downLoadArgument(conditionResult, 0) return if (isExpression) executeIfExpression(conditionNativeResult, element.then!!, element.`else`, element, scopeDepth + 1) diff --git a/translator/src/main/kotlin/org/kotlinnative/translator/ClassCodegen.kt b/translator/src/main/kotlin/org/kotlinnative/translator/ClassCodegen.kt index 6252f305727..26f46122baa 100644 --- a/translator/src/main/kotlin/org/kotlinnative/translator/ClassCodegen.kt +++ b/translator/src/main/kotlin/org/kotlinnative/translator/ClassCodegen.kt @@ -6,7 +6,10 @@ import org.jetbrains.kotlin.psi.KtParameter import org.jetbrains.kotlin.resolve.BindingContext import org.kotlinnative.translator.exceptions.TranslationException import org.kotlinnative.translator.llvm.LLVMBuilder +import org.kotlinnative.translator.llvm.LLVMVariable import org.kotlinnative.translator.llvm.types.LLVMReferenceType +import org.kotlinnative.translator.llvm.types.LLVMType +import java.util.* class ClassCodegen(state: TranslationState, variableManager: VariableManager, @@ -49,15 +52,17 @@ class ClassCodegen(state: TranslationState, if (annotation) { return } - + val currentConstructorFields = ArrayList() for (field in parameters) { val item = resolveType(field, state.bindingContext.get(BindingContext.TYPE, field.typeReference)!!) item.offset = fields.size - constructorFields.add(item) + currentConstructorFields.add(item) fields.add(item) fieldsIndex[item.label] = item } + primaryConstructorIndex = LLVMType.mangleFunctionArguments(currentConstructorFields) + constructorFields.put(primaryConstructorIndex!!, currentConstructorFields) } override fun prepareForGenerate() { diff --git a/translator/src/main/kotlin/org/kotlinnative/translator/ObjectCodegen.kt b/translator/src/main/kotlin/org/kotlinnative/translator/ObjectCodegen.kt index 5c21a200a11..6542ae205a4 100644 --- a/translator/src/main/kotlin/org/kotlinnative/translator/ObjectCodegen.kt +++ b/translator/src/main/kotlin/org/kotlinnative/translator/ObjectCodegen.kt @@ -7,6 +7,7 @@ import org.kotlinnative.translator.llvm.LLVMBuilder import org.kotlinnative.translator.llvm.LLVMVariable import org.kotlinnative.translator.llvm.LLVMVariableScope import org.kotlinnative.translator.llvm.types.LLVMReferenceType +import org.kotlinnative.translator.llvm.types.LLVMType class ObjectCodegen(state: TranslationState, variableManager: VariableManager, @@ -25,6 +26,9 @@ class ObjectCodegen(state: TranslationState, type.location.addAll(parentCodegen.type.location) type.location.add(parentCodegen.structName) } + primaryConstructorIndex = LLVMType.mangleFunctionArguments(emptyList()) + constructorFields.put(primaryConstructorIndex!!, arrayListOf()) + generateInnerFields(objectDeclaration.declarations) calculateTypeSize() diff --git a/translator/src/main/kotlin/org/kotlinnative/translator/StructCodegen.kt b/translator/src/main/kotlin/org/kotlinnative/translator/StructCodegen.kt index f992331d029..6a11a4a3ebd 100644 --- a/translator/src/main/kotlin/org/kotlinnative/translator/StructCodegen.kt +++ b/translator/src/main/kotlin/org/kotlinnative/translator/StructCodegen.kt @@ -9,6 +9,7 @@ import org.jetbrains.kotlin.types.KotlinType import org.kotlinnative.translator.llvm.* import org.kotlinnative.translator.llvm.types.LLVMCharType import org.kotlinnative.translator.llvm.types.LLVMReferenceType +import org.kotlinnative.translator.llvm.types.LLVMType import org.kotlinnative.translator.llvm.types.LLVMVoidType import java.util.* @@ -24,7 +25,8 @@ abstract class StructCodegen(val state: TranslationState, val nestedClasses = HashMap() val enumFields = HashMap() - val constructorFields = ArrayList() + val constructorFields = HashMap>() + var primaryConstructorIndex: String? = null val initializedFields = HashMap() abstract val type: LLVMReferenceType @@ -67,6 +69,9 @@ abstract class StructCodegen(val state: TranslationState, open fun generate() { generateEnumFields() generatePrimaryConstructor() + for (secondaryConstructor in classOrObject.getSecondaryConstructors()) { + generateSecondaryConstructor(secondaryConstructor) + } val classVal = LLVMVariable("classvariable.this", type, pointer = if (type.isPrimitive()) 0 else 1) variableManager.addVariable("this", classVal, 0) @@ -110,6 +115,7 @@ abstract class StructCodegen(val state: TranslationState, } } + private fun generateEnumFields() { val enumEntries = classOrObject.declarations.filter { it is KtEnumEntry } @@ -121,7 +127,7 @@ abstract class StructCodegen(val state: TranslationState, val field = codeBuilder.getNewVariable(type, scope = LLVMVariableScope()) val enumField = enumFields[name]!! - codeBuilder.defineGlobalVariable(field, codeBuilder.makeStructInitializer(constructorFields, arguments)) + codeBuilder.defineGlobalVariable(field, codeBuilder.makeStructInitializer(constructorFields[primaryConstructorIndex]!!, arguments)) codeBuilder.defineGlobalVariable(LLVMVariable(enumField.label, enumField.type, enumField.kotlinName, enumField.scope, enumField.pointer - 1), "$field") } } @@ -130,24 +136,56 @@ abstract class StructCodegen(val state: TranslationState, codeBuilder.createClass(fullName, fields) } + private fun generateSecondaryConstructor(secondaryConstructor: KtSecondaryConstructor) { + val thisCall = secondaryConstructor.getDelegationCall().calleeExpression + val descriptor = state.bindingContext.get(BindingContext.CONSTRUCTOR, secondaryConstructor) + + val argFields = ArrayList() + + val classVal = LLVMVariable("classvariable.this", type, pointer = 1) + variableManager.addVariable("this", classVal, 0) + + val secondaryConstructorArguments = descriptor!!.valueParameters.map { + LLVMInstanceOfStandardType(it.name.toString(), it.type, state = state) + } + + argFields.add(classVal) + argFields.addAll(secondaryConstructorArguments) + val currentConstructorIndex = LLVMType.mangleFunctionArguments(secondaryConstructorArguments) + constructorFields.put(currentConstructorIndex, argFields) + codeBuilder.addLLVMCode(LLVMFunctionDescriptor(fullName + currentConstructorIndex, argFields, LLVMVoidType(), arm = state.arm)) + + codeBuilder.addStartExpression() + + for (variable in secondaryConstructorArguments) { + variableManager.addVariable(variable.label, variable, 2) + } + + val blockCodegen = object : BlockCodegen(state, variableManager, codeBuilder) {} + val mainConstructorThis = blockCodegen.evaluateConstructorDelegationReferenceExpression(thisCall!!, this, secondaryConstructorArguments, 1) as LLVMVariable + variableManager.addVariable("this", mainConstructorThis, 0) + + blockCodegen.evaluateCodeBlock(secondaryConstructor.bodyExpression, scopeDepth = 1) + generateReturn(codeBuilder.downLoadArgument(variableManager.get("this")!!, 1) as LLVMVariable) + codeBuilder.addAnyReturn(LLVMVoidType()) + codeBuilder.addEndExpression() + } + private fun generatePrimaryConstructor() { val argFields = ArrayList() - val refType = type.makeClone() as LLVMReferenceType - refType.addParam("sret") - refType.byRef = true val classVal = LLVMVariable("classvariable.this", type, pointer = 1) variableManager.addVariable("this", classVal, 0) argFields.add(classVal) - argFields.addAll(constructorFields) + argFields.addAll(constructorFields[primaryConstructorIndex]!!) - codeBuilder.addLLVMCode(LLVMFunctionDescriptor(fullName, argFields, LLVMVoidType(), arm = state.arm)) + codeBuilder.addLLVMCode(LLVMFunctionDescriptor(fullName + primaryConstructorIndex, argFields, LLVMVoidType(), arm = state.arm)) codeBuilder.addStartExpression() generateLoadArguments(classVal) generateAssignments() - generateReturn() + generateReturn(LLVMVariable("classvariable.this.addr", type, scope = LLVMRegisterScope(), pointer = 1)) genClassInitializers() codeBuilder.addAnyReturn(LLVMVoidType()) codeBuilder.addEndExpression() @@ -157,7 +195,7 @@ abstract class StructCodegen(val state: TranslationState, val thisVariable = LLVMVariable(thisField.label, thisField.type, thisField.label, LLVMRegisterScope(), pointer = 0) codeBuilder.loadArgument(thisVariable, false) - constructorFields.forEach { + constructorFields[primaryConstructorIndex]!!.forEach { if (it.type !is LLVMReferenceType) { val loadVariable = LLVMVariable(it.label, it.type, it.label, LLVMRegisterScope()) codeBuilder.loadArgument(loadVariable) @@ -166,7 +204,7 @@ abstract class StructCodegen(val state: TranslationState, } private fun generateAssignments() { - constructorFields.forEach { + constructorFields[primaryConstructorIndex]!!.forEach { when (it.type) { is LLVMReferenceType -> { val classField = codeBuilder.getNewVariable(it.type, pointer = it.pointer + 1) @@ -200,9 +238,8 @@ abstract class StructCodegen(val state: TranslationState, codeBuilder.addComment("field initilizers ends") } - private fun generateReturn() { + private fun generateReturn(src: LLVMVariable) { val dst = LLVMVariable("classvariable.this", type, scope = LLVMRegisterScope(), pointer = 1) - val src = LLVMVariable("classvariable.this.addr", type, scope = LLVMRegisterScope(), pointer = 1) val castedDst = codeBuilder.bitcast(dst, LLVMVariable("", LLVMCharType(), pointer = 1)) val castedSrc = codeBuilder.bitcast(src, LLVMVariable("", LLVMCharType(), pointer = 1)) 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 a500ac262b6..58113dcfc30 100644 --- a/translator/src/main/kotlin/org/kotlinnative/translator/llvm/LLVMBuilder.kt +++ b/translator/src/main/kotlin/org/kotlinnative/translator/llvm/LLVMBuilder.kt @@ -72,6 +72,31 @@ class LLVMBuilder(val arm: Boolean = false) { else -> throw UnsupportedOperationException() } + fun loadArgumentIfRequired(value: LLVMSingleValue, argument: LLVMVariable): LLVMSingleValue { + var result = value + + while (argument.pointer < result.pointer) { + result = getNewVariable(argument.type, pointer = result.pointer - 1) + loadVariable(result, value as LLVMVariable) + } + + when (value.type) { + is LLVMStringType -> if (!(value.type as LLVMStringType).isLoaded) { + val newVariable = getNewVariable(value.type!!, pointer = result.pointer + 1) + allocStackVar(newVariable, asValue = true) + copyVariable(result as LLVMVariable, newVariable) + + result = getNewVariable(argument.type, pointer = newVariable.pointer - 1) + loadVariable(result, newVariable) + } + } + + return result + } + + fun downLoadArgument(value: LLVMSingleValue, pointer: Int): LLVMSingleValue = + loadArgumentIfRequired(value, LLVMVariable("", value.type!!, pointer = pointer)) + fun clean() { localCode = StringBuilder() globalCode = StringBuilder() diff --git a/translator/src/main/kotlin/org/kotlinnative/translator/llvm/types/LLVMReferenceType.kt b/translator/src/main/kotlin/org/kotlinnative/translator/llvm/types/LLVMReferenceType.kt index dfc6633e6c3..2f8fc51c65f 100644 --- a/translator/src/main/kotlin/org/kotlinnative/translator/llvm/types/LLVMReferenceType.kt +++ b/translator/src/main/kotlin/org/kotlinnative/translator/llvm/types/LLVMReferenceType.kt @@ -15,16 +15,10 @@ class LLVMReferenceType(val type: String, var prefix: String = "", override var override fun toString() = "%$typename" - private val params = ArrayList() - val location = ArrayList() override fun mangle() = "Ref_$type" - fun addParam(param: String) { - params.add(param) - } - override fun operatorEq(firstOp: LLVMSingleValue, secondOp: LLVMSingleValue): LLVMExpression = LLVMExpression(LLVMBooleanType(), "icmp eq ${firstOp.getType()} $firstOp, ${if (secondOp.type is LLVMNullType) "null" else "$secondOp"}") diff --git a/translator/src/main/kotlin/org/kotlinnative/translator/llvm/types/LLVMType.kt b/translator/src/main/kotlin/org/kotlinnative/translator/llvm/types/LLVMType.kt index 935f5c7f107..ef4fc9f13cd 100644 --- a/translator/src/main/kotlin/org/kotlinnative/translator/llvm/types/LLVMType.kt +++ b/translator/src/main/kotlin/org/kotlinnative/translator/llvm/types/LLVMType.kt @@ -26,8 +26,6 @@ abstract class LLVMType() : Cloneable { open fun operatorDec(firstOp: LLVMSingleValue): LLVMExpression = throw UnimplementedException() open fun parseArg(inputArg: String) = inputArg - fun makeClone() = clone() - open fun convertFrom(source: LLVMSingleValue): LLVMExpression = throw UnimplementedException() abstract fun mangle(): String @@ -38,6 +36,12 @@ abstract class LLVMType() : Cloneable { abstract var size: Int abstract val defaultValue: String open fun isPrimitive(): Boolean = false + + companion object { + fun mangleFunctionArguments(names: List) = + "_${names.joinToString(separator = "_", transform = { it.type!!.mangle() })}" + } + } fun parseLLVMType(type: String): LLVMType = when (type) { diff --git a/translator/src/test/kotlin/tests/input/secondary_constructor_1.txt b/translator/src/test/kotlin/tests/input/secondary_constructor_1.txt new file mode 100644 index 00000000000..bdb4be5febb --- /dev/null +++ b/translator/src/test/kotlin/tests/input/secondary_constructor_1.txt @@ -0,0 +1 @@ +secondary_constructor_1_Int(1389) == 1401 diff --git a/translator/src/test/kotlin/tests/kotlin/secondary_constructor_1.kt b/translator/src/test/kotlin/tests/kotlin/secondary_constructor_1.kt new file mode 100644 index 00000000000..574b298e332 --- /dev/null +++ b/translator/src/test/kotlin/tests/kotlin/secondary_constructor_1.kt @@ -0,0 +1,10 @@ +class secondary_constructor_1_class(var field1: Int) { + constructor(field1: Int, field2: Int) : this(field1) { + this.field1 = field1 + field2 + } +} + +fun secondary_constructor_1(x: Int): Int { + val cls = secondary_constructor_1_class(12, x) + return cls.field1 +} \ No newline at end of file