This commit is contained in:
Konstantin Anisimov
2017-02-02 17:11:57 +07:00
2766 changed files with 76538 additions and 1382 deletions
+1
View File
@@ -21,6 +21,7 @@ kotstd/kotstd.iml
*.kt.S
*.kt.exe
*.log
test.output
# Ignore Gradle GUI config
gradle-app.setting
+40 -29
View File
@@ -143,7 +143,7 @@ kotlinNativeInterop {
compilerOpts '-fPIC'
linkerOpts '-fPIC'
linker 'clang++'
linkOutputs ':common:compileHash'
linkOutputs ':common:hostHash'
headers fileTree('../common/src/hash/headers') {
include '**/*.h'
@@ -182,47 +182,58 @@ dependencies {
build.dependsOn 'compilerClasses','cli_bcClasses','bc_frontendClasses'
// These are just a couple of aliases
task stdlib(dependsOn: 'hostStdlib')
task start(dependsOn: 'hostStart')
// These files are built before the 'dist' is complete,
// so we provide custom values for
// -runtime, -properties, -library and -Djava.library.path
task stdlib(type: JavaExec) {
main = 'org.jetbrains.kotlin.cli.bc.K2NativeKt'
classpath = project.configurations.cli_bc
jvmArgs "-ea",
"-Dkonan.home=${project.parent.file('dist')}",
"-Djava.library.path=${project.buildDir}/nativelibs"
args('-output', project(':runtime').file('build/stdlib.kt.bc'),
'-nolink', '-nostdlib',
'-runtime', project(':runtime').file('build/runtime.bc'),
'-properties', project(':backend.native').file('konan.properties'),
targetList.each { target ->
task("${target}Stdlib", type: JavaExec) {
main = 'org.jetbrains.kotlin.cli.bc.K2NativeKt'
classpath = project.configurations.cli_bc
jvmArgs "-ea",
"-Dkonan.home=${project.parent.file('dist')}",
"-Djava.library.path=${project.buildDir}/nativelibs"
args('-output', project(':runtime').file("build/${target}/stdlib.kt.bc"),
'-nolink', '-nostdlib',
'-target', target,
'-runtime', project(':runtime').file("build/${target}/runtime.bc"),
'-properties', project(':backend.native').file('konan.properties'),
project(':runtime').file('src/main/kotlin'),
*project.globalArgs)
inputs.dir(project(':runtime').file('src/main/kotlin'))
outputs.file(project(':runtime').file('build/stdlib.kt.bc'))
inputs.dir(project(':runtime').file('src/main/kotlin'))
outputs.file(project(':runtime').file("build/${target}/stdlib.kt.bc"))
dependsOn ':runtime:build'
dependsOn ":runtime:${target}Runtime"
}
}
task start(type: JavaExec) {
main = 'org.jetbrains.kotlin.cli.bc.K2NativeKt'
classpath = project.configurations.cli_bc
jvmArgs "-ea",
"-Dkonan.home=${project.parent.file('dist')}",
"-Djava.library.path=${project.buildDir}/nativelibs"
args('-output', project(':runtime').file('build/start.kt.bc'),
'-nolink', '-nostdlib',
'-library', project(':runtime').file('build/stdlib.kt.bc'),
'-runtime', project(':runtime').file('build/runtime.bc'),
'-properties', project(':backend.native').file('konan.properties'),
targetList.each { target ->
task("${target}Start", type: JavaExec) {
main = 'org.jetbrains.kotlin.cli.bc.K2NativeKt'
classpath = project.configurations.cli_bc
jvmArgs "-ea",
"-Dkonan.home=${project.parent.file('dist')}",
"-Djava.library.path=${project.buildDir}/nativelibs"
args('-output', project(':runtime').file("build/${target}/start.kt.bc"),
'-nolink', '-nostdlib',
'-target', target,
'-library', project(':runtime').file("build/${target}/stdlib.kt.bc"),
'-runtime', project(':runtime').file("build/${target}/runtime.bc"),
'-properties', project(':backend.native').file('konan.properties'),
project(':runtime').file('src/launcher/kotlin'),
*project.globalArgs)
inputs.dir(project(':runtime').file('src/launcher/kotlin'))
outputs.file(project(':runtime').file('build/start.kt.bc'))
inputs.dir(project(':runtime').file('src/launcher/kotlin'))
outputs.file(project(':runtime').file("build/${target}/start.kt.bc"))
dependsOn ':runtime:build', 'stdlib'
dependsOn ":runtime:${target}Runtime", "${target}Stdlib"
}
}
task run {
@@ -237,7 +248,7 @@ task jars(type: Jar) {
'build/classes/hashInteropStubs',
'build/classes/llvmInteropStubs'
dependsOn 'build', ':runtime:build', 'external_jars'
dependsOn 'build', ':runtime:hostRuntime', 'external_jars'
}
task external_jars(type: Copy) {
@@ -77,11 +77,13 @@ class K2Native : CLICompiler<K2NativeCompilerArguments>() {
put(EXECUTABLE_FILE,
arguments.outputFile ?: "program.kexe")
put(RUNTIME_FILE,
arguments.runtimeFile ?: Distribution.runtime)
put(PROPERTY_FILE,
arguments.propertyFile ?: Distribution.propertyFile)
if (arguments.runtimeFile != null)
put(RUNTIME_FILE, arguments.runtimeFile)
if (arguments.propertyFile != null)
put(PROPERTY_FILE, arguments.propertyFile)
if (arguments.target != null)
put(TARGET, arguments.target)
put(LIST_TARGETS, arguments.listTargets)
put(OPTIMIZATION, arguments.optimization)
put(PRINT_IR, arguments.printIr)
@@ -30,6 +30,13 @@ public class K2NativeCompilerArguments extends CommonCompilerArguments {
@Argument(value = "opt", description = "Enable optimizations during compilation")
public boolean optimization;
@Argument(value = "target", description = "Set hardware target")
@ValueDescription("<target>")
public String target;
@Argument(value = "list_targets", description = "List available hardware targets")
public boolean listTargets;
@Argument(value = "print_ir", description = "Print IR")
public boolean printIr;
@@ -0,0 +1,109 @@
/*
* Copyright 2010-2016 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.
*/
package org.jetbrains.kotlin.backend.common
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrLocalDelegatedProperty
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid
import org.jetbrains.kotlin.resolve.DescriptorUtils
import java.util.*
abstract class AbstractClosureRecorder : IrElementVisitorVoid {
protected abstract fun recordFunctionClosure(functionDescriptor: FunctionDescriptor, closure: Closure)
protected abstract fun recordClassClosure(classDescriptor: ClassDescriptor, closure: Closure)
private class ClosureBuilder(val owner: DeclarationDescriptor) {
val capturedValues = mutableSetOf<ValueDescriptor>()
fun buildClosure() = Closure(capturedValues.toList())
fun addNested(closure: Closure) {
fillInNestedClosure(capturedValues, closure.capturedValues)
}
private fun <T : CallableDescriptor> fillInNestedClosure(destination: MutableSet<T>, nested: List<T>) {
nested.filterTo(destination) {
it.containingDeclaration != owner
}
}
}
private val closuresStack = ArrayDeque<ClosureBuilder>()
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitClass(declaration: IrClass) {
val classDescriptor = declaration.descriptor
val closureBuilder = ClosureBuilder(classDescriptor)
closuresStack.push(closureBuilder)
declaration.acceptChildrenVoid(this)
closuresStack.pop()
val closure = closureBuilder.buildClosure()
if (DescriptorUtils.isLocal(classDescriptor)) {
recordClassClosure(classDescriptor, closure)
}
closuresStack.peek()?.addNested(closure)
}
override fun visitFunction(declaration: IrFunction) {
val functionDescriptor = declaration.descriptor
val closureBuilder = ClosureBuilder(functionDescriptor)
closuresStack.push(closureBuilder)
declaration.acceptChildrenVoid(this)
closuresStack.pop()
val closure = closureBuilder.buildClosure()
if (DescriptorUtils.isLocal(functionDescriptor)) {
recordFunctionClosure(functionDescriptor, closure)
}
closuresStack.peek()?.addNested(closure)
}
override fun visitLocalDelegatedProperty(declaration: IrLocalDelegatedProperty) {
// Getter and setter of local delegated properties are special generated functions and don't have closure.
declaration.delegate.initializer?.acceptVoid(this)
}
override fun visitVariableAccess(expression: IrValueAccessExpression) {
val closureBuilder = closuresStack.peek()
if (closureBuilder != null) {
val variableDescriptor = expression.descriptor
if (variableDescriptor.containingDeclaration != closureBuilder.owner) {
closureBuilder.capturedValues.add(variableDescriptor)
}
}
expression.acceptChildrenVoid(this)
}
}
@@ -37,6 +37,15 @@ abstract class AbstractValueUsageTransformer(val builtIns: KotlinBuiltIns): IrEl
protected open fun IrExpression.useAsArgument(parameter: ParameterDescriptor): IrExpression =
this.useAsValue(parameter)
protected open fun IrExpression.useAsDispatchReceiver(function: CallableDescriptor): IrExpression =
this.useAsArgument(function.dispatchReceiverParameter!!)
protected open fun IrExpression.useAsExtensionReceiver(function: CallableDescriptor): IrExpression =
this.useAsArgument(function.extensionReceiverParameter!!)
protected open fun IrExpression.useAsValueArgument(parameter: ValueParameterDescriptor): IrExpression =
this.useAsArgument(parameter)
protected open fun IrExpression.useForVariable(variable: VariableDescriptor): IrExpression =
this.useAsValue(variable)
@@ -55,12 +64,12 @@ abstract class AbstractValueUsageTransformer(val builtIns: KotlinBuiltIns): IrEl
expression.transformChildrenVoid(this)
with(expression) {
dispatchReceiver = dispatchReceiver?.useAsArgument(descriptor.dispatchReceiverParameter!!)
extensionReceiver = extensionReceiver?.useAsArgument(descriptor.extensionReceiverParameter!!)
dispatchReceiver = dispatchReceiver?.useAsDispatchReceiver(descriptor)
extensionReceiver = extensionReceiver?.useAsExtensionReceiver(descriptor)
for (index in descriptor.valueParameters.indices) {
val argument = getValueArgument(index) ?: continue
val parameter = descriptor.valueParameters[index]
putValueArgument(index, argument.useAsArgument(parameter))
putValueArgument(index, argument.useAsValueArgument(parameter))
}
}
@@ -214,7 +223,7 @@ abstract class AbstractValueUsageTransformer(val builtIns: KotlinBuiltIns): IrEl
declaration.descriptor.valueParameters.forEach { parameter ->
val defaultValue = declaration.getDefault(parameter)
if (defaultValue is IrExpressionBody) {
defaultValue.expression = defaultValue.expression.useAsArgument(parameter)
defaultValue.expression = defaultValue.expression.useAsValueArgument(parameter)
}
}
@@ -0,0 +1,655 @@
/*
* Copyright 2010-2016 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.
*/
package org.jetbrains.kotlin.backend.common.lower
import org.jetbrains.kotlin.backend.common.AbstractClosureRecorder
import org.jetbrains.kotlin.backend.common.BackendContext
import org.jetbrains.kotlin.backend.common.Closure
import org.jetbrains.kotlin.backend.common.DeclarationContainerLoweringPass
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.*
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.util.transformFlat
import org.jetbrains.kotlin.ir.visitors.*
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.descriptorUtil.parents
import org.jetbrains.kotlin.resolve.descriptorUtil.parentsWithSelf
import org.jetbrains.kotlin.types.KotlinType
import java.util.*
class LocalDeclarationsLowering(val context: BackendContext): DeclarationContainerLoweringPass {
override fun lower(irDeclarationContainer: IrDeclarationContainer) {
if (irDeclarationContainer is IrDeclaration &&
irDeclarationContainer.descriptor.parents.any { it is CallableDescriptor }) {
// Lowering of non-local declarations handles all local declarations inside.
// This declaration is local and shouldn't be considered.
return
}
// Continuous numbering across all declarations in the container.
lambdasCount = 0
irDeclarationContainer.declarations.transformFlat { memberDeclaration ->
if (memberDeclaration is IrFunction)
LocalDeclarationsTransformer(memberDeclaration).lowerLocalDeclarations()
else
null
}
}
private var lambdasCount = 0
private abstract class LocalContext {
/**
* @return the expression to get the value for given descriptor, or `null` if [IrGetValue] should be used.
*/
abstract fun irGet(startOffset: Int, endOffset: Int, descriptor: ValueDescriptor): IrExpression?
}
private abstract class LocalContextWithClosureAsParameters : LocalContext() {
abstract val declaration: IrFunction
open val descriptor: FunctionDescriptor
get() = declaration.descriptor
abstract val transformedDescriptor: FunctionDescriptor
val capturedValueToParameter: MutableMap<ValueDescriptor, ValueParameterDescriptor> = HashMap()
override fun irGet(startOffset: Int, endOffset: Int, descriptor: ValueDescriptor): IrExpression? {
val newDescriptor = capturedValueToParameter[descriptor] ?: return null
return IrGetValueImpl(startOffset, endOffset, newDescriptor)
}
}
private class LocalFunctionContext(override val declaration: IrFunction) : LocalContextWithClosureAsParameters() {
lateinit var closure: Closure
override lateinit var transformedDescriptor: FunctionDescriptor
var index: Int = -1
override fun toString(): String =
"LocalFunctionContext for $descriptor"
}
private class LocalClassConstructorContext(override val declaration: IrConstructor) : LocalContextWithClosureAsParameters() {
override val descriptor: ClassConstructorDescriptor
get() = declaration.descriptor
override lateinit var transformedDescriptor: ClassConstructorDescriptor
override fun toString(): String =
"LocalClassConstructorContext for $descriptor"
}
private class LocalClassContext(val declaration: IrClass) : LocalContext() {
val descriptor: ClassDescriptor
get() = declaration.descriptor
lateinit var closure: Closure
val capturedValueToField: MutableMap<ValueDescriptor, PropertyDescriptor> = HashMap()
override fun irGet(startOffset: Int, endOffset: Int, descriptor: ValueDescriptor): IrExpression? {
val fieldDescriptor = capturedValueToField[descriptor] ?: return null
return IrGetFieldImpl(startOffset, endOffset, fieldDescriptor,
receiver = IrGetValueImpl(startOffset, endOffset, this.descriptor.thisAsReceiverParameter)
)
}
override fun toString(): String =
"LocalClassContext for ${descriptor}"
}
private inner class LocalDeclarationsTransformer(val memberFunction: IrFunction) {
val localFunctions: MutableMap<FunctionDescriptor, LocalFunctionContext> = LinkedHashMap()
val localClasses: MutableMap<ClassDescriptor, LocalClassContext> = LinkedHashMap()
val localClassConstructors: MutableMap<ClassConstructorDescriptor, LocalClassConstructorContext> = LinkedHashMap()
val transformedDescriptors = mutableMapOf<DeclarationDescriptor, DeclarationDescriptor>()
val CallableDescriptor.transformed: CallableDescriptor?
get() = transformedDescriptors[this] as CallableDescriptor?
val oldParameterToNew: MutableMap<ParameterDescriptor, ParameterDescriptor> = HashMap()
val newParameterToOld: MutableMap<ParameterDescriptor, ParameterDescriptor> = HashMap()
val newParameterToCaptured: MutableMap<ValueParameterDescriptor, ValueDescriptor> = HashMap()
fun lowerLocalDeclarations(): List<IrDeclaration>? {
collectLocalDeclarations()
if (localFunctions.isEmpty() && localClasses.isEmpty()) return null
collectClosures()
transformDescriptors()
rewriteDeclarations()
val result = collectRewrittenDeclarations()
return result
}
private fun collectRewrittenDeclarations(): ArrayList<IrDeclaration> =
ArrayList<IrDeclaration>(localFunctions.size + localClasses.size + 1).apply {
add(memberFunction)
localFunctions.values.mapTo(this) {
val original = it.declaration
IrFunctionImpl(
original.startOffset, original.endOffset, original.origin,
it.transformedDescriptor,
original.body
)
}
localClasses.values.mapTo(this) {
it.declaration
}
}
private inner class FunctionBodiesRewriter(val localContext: LocalContext?) : IrElementTransformerVoid() {
override fun visitClass(declaration: IrClass): IrStatement {
// Replace local class definition with an empty composite.
return IrCompositeImpl(declaration.startOffset, declaration.endOffset, context.builtIns.unitType)
}
override fun visitFunction(declaration: IrFunction): IrStatement {
if (declaration.descriptor in localFunctions) {
// Replace local function definition with an empty composite.
return IrCompositeImpl(declaration.startOffset, declaration.endOffset, context.builtIns.unitType)
} else {
declaration.transformChildrenVoid(this)
return declaration
}
}
override fun visitConstructor(declaration: IrConstructor): IrStatement {
// Body is transformed separately.
val transformedDescriptor = localClassConstructors[declaration.descriptor]!!.transformedDescriptor
return IrConstructorImpl(declaration.startOffset, declaration.endOffset, declaration.origin,
transformedDescriptor, declaration.body!!)
}
override fun visitGetValue(expression: IrGetValue): IrExpression {
val descriptor = expression.descriptor
localContext?.irGet(expression.startOffset, expression.endOffset, descriptor)?.let {
return it
}
oldParameterToNew[descriptor]?.let {
return IrGetValueImpl(expression.startOffset, expression.endOffset, it)
}
return expression
}
override fun visitCall(expression: IrCall): IrExpression {
expression.transformChildrenVoid(this)
val oldCallee = expression.descriptor.original
val newCallee = oldCallee.transformed ?: return expression
val newCall = createNewCall(expression, newCallee).fillArguments(expression)
return newCall
}
private fun <T : IrMemberAccessExpression> T.fillArguments(oldExpression: IrMemberAccessExpression): T {
mapValueParameters { newValueParameterDescriptor ->
val oldParameter = newParameterToOld[newValueParameterDescriptor]
if (oldParameter != null) {
oldExpression.getValueArgument(oldParameter as ValueParameterDescriptor)
} else {
// The callee expects captured value as argument.
val capturedValueDescriptor =
newParameterToCaptured[newValueParameterDescriptor] ?:
throw AssertionError("Non-mapped parameter $newValueParameterDescriptor")
localContext?.irGet(
oldExpression.startOffset, oldExpression.endOffset,
capturedValueDescriptor
) ?:
// Captured value is directly available for the caller.
IrGetValueImpl(oldExpression.startOffset, oldExpression.endOffset, capturedValueDescriptor)
}
}
dispatchReceiver = oldExpression.dispatchReceiver
extensionReceiver = oldExpression.extensionReceiver
return this
}
override fun visitCallableReference(expression: IrCallableReference): IrExpression {
expression.transformChildrenVoid(this)
val oldCallee = expression.descriptor.original
val newCallee = oldCallee.transformed ?: return expression
val newCallableReference = IrCallableReferenceImpl(
expression.startOffset, expression.endOffset,
expression.type, // TODO functional type for transformed descriptor
newCallee,
remapTypeArguments(expression, newCallee),
expression.origin
).fillArguments(expression)
return newCallableReference
}
override fun visitReturn(expression: IrReturn): IrExpression {
expression.transformChildrenVoid(this)
val oldReturnTarget = expression.returnTarget
val newReturnTarget = oldReturnTarget.transformed ?: return expression
return IrReturnImpl(expression.startOffset, expression.endOffset, newReturnTarget, expression.value)
}
override fun visitDeclarationReference(expression: IrDeclarationReference): IrExpression {
if (expression.descriptor in transformedDescriptors) {
TODO()
}
return super.visitDeclarationReference(expression)
}
override fun visitDeclaration(declaration: IrDeclaration): IrStatement {
if (declaration.descriptor in transformedDescriptors) {
TODO()
}
return super.visitDeclaration(declaration)
}
}
private fun rewriteFunctionBody(irFunction: IrFunction, localContext: LocalContext?) {
irFunction.transformChildrenVoid(FunctionBodiesRewriter(localContext))
}
private object DECLARATION_ORIGIN_FIELD_FOR_CAPTURED_VALUE :
IrDeclarationOriginImpl("FIELD_FOR_CAPTURED_VALUE") {}
private fun rewriteClassMembers(irClass: IrClass, localClassContext: LocalClassContext) {
irClass.transformChildrenVoid(FunctionBodiesRewriter(localClassContext))
val primaryConstructor = irClass.descriptor.unsubstitutedPrimaryConstructor ?:
TODO("local classes without primary constructor")
val primaryConstructorContext = localClassConstructors[primaryConstructor]!!
localClassContext.capturedValueToField.forEach { capturedValue, fieldDescriptor ->
val capturedValueExpression =
primaryConstructorContext.irGet(irClass.startOffset, irClass.endOffset, capturedValue)!!
irClass.declarations.add(
IrFieldImpl(
irClass.startOffset, irClass.endOffset,
DECLARATION_ORIGIN_FIELD_FOR_CAPTURED_VALUE,
fieldDescriptor,
IrExpressionBodyImpl(
irClass.startOffset, irClass.endOffset,
capturedValueExpression
)
)
)
}
}
private fun rewriteDeclarations() {
localFunctions.values.forEach {
rewriteFunctionBody(it.declaration, it)
}
localClassConstructors.values.forEach {
rewriteFunctionBody(it.declaration, it)
}
localClasses.values.forEach {
rewriteClassMembers(it.declaration, it)
}
rewriteFunctionBody(memberFunction, null)
}
private fun createNewCall(oldCall: IrCall, newCallee: CallableDescriptor) =
if (oldCall is IrCallWithShallowCopy)
oldCall.shallowCopy(oldCall.origin, newCallee, oldCall.superQualifier)
else
IrCallImpl(
oldCall.startOffset, oldCall.endOffset,
newCallee,
remapTypeArguments(oldCall, newCallee),
oldCall.origin, oldCall.superQualifier
)
private fun remapTypeArguments(oldExpression: IrMemberAccessExpression, newCallee: CallableDescriptor): Map<TypeParameterDescriptor, KotlinType>? {
val oldCallee = oldExpression.descriptor
return if (oldCallee.typeParameters.isEmpty())
null
else oldCallee.typeParameters.associateBy(
{ newCallee.typeParameters[it.index] },
{ oldExpression.getTypeArgument(it)!! }
)
}
private fun transformDescriptors() {
localFunctions.values.forEach {
createLiftedDescriptor(it)
}
localClasses.values.forEach {
createFieldsForCapturedValues(it)
}
localClassConstructors.values.forEach {
createTransformedConstructorDescriptor(it)
}
}
private fun suggestLocalName(descriptor: DeclarationDescriptor): String {
localFunctions[descriptor]?.let {
if (it.index >= 0)
return "lambda-${it.index}"
}
return descriptor.name.asString()
}
private fun generateNameForLiftedDeclaration(descriptor: DeclarationDescriptor,
newOwner: DeclarationDescriptor): Name =
Name.identifier(
descriptor.parentsWithSelf
.takeWhile { it != newOwner }
.toList().reversed()
.map { suggestLocalName(it) }
.joinToString(separator = "$")
)
private fun createLiftedDescriptor(localFunctionContext: LocalFunctionContext) {
val oldDescriptor = localFunctionContext.descriptor
val memberOwner = memberFunction.descriptor.containingDeclaration
val newDescriptor = SimpleFunctionDescriptorImpl.create(
memberOwner,
oldDescriptor.annotations,
generateNameForLiftedDeclaration(oldDescriptor, memberOwner),
CallableMemberDescriptor.Kind.SYNTHESIZED,
oldDescriptor.source
)
localFunctionContext.transformedDescriptor = newDescriptor
transformedDescriptors[oldDescriptor] = newDescriptor
if (oldDescriptor.dispatchReceiverParameter != null) {
throw AssertionError("local functions must not have dispatch receiver")
}
val newDispatchReceiverParameter = null
// Do not substitute type parameters for now.
val newTypeParameters = oldDescriptor.typeParameters
// TODO: consider using fields to access the closure of enclosing class.
val capturedValues = localFunctionContext.closure.capturedValues
val newValueParameters = createTransformedValueParameters(localFunctionContext, capturedValues)
newDescriptor.initialize(
oldDescriptor.extensionReceiverParameter?.type,
newDispatchReceiverParameter,
newTypeParameters,
newValueParameters,
oldDescriptor.returnType,
Modality.FINAL,
Visibilities.PRIVATE
)
oldDescriptor.extensionReceiverParameter?.let {
recordRemappedParameter(it, newDescriptor.extensionReceiverParameter!!)
}
}
private fun createTransformedValueParameters(localContext: LocalContextWithClosureAsParameters,
capturedValues: List<ValueDescriptor>
): List<ValueParameterDescriptor> {
val oldDescriptor = localContext.descriptor
val newDescriptor = localContext.transformedDescriptor
val closureParametersCount = capturedValues.size
val newValueParametersCount = closureParametersCount + oldDescriptor.valueParameters.size
val newValueParameters = ArrayList<ValueParameterDescriptor>(newValueParametersCount).apply {
capturedValues.mapIndexedTo(this) { i, capturedValueDescriptor ->
createUnsubstitutedCapturedValueParameter(newDescriptor, capturedValueDescriptor, i).apply {
localContext.recordCapturedAsParameter(capturedValueDescriptor, this)
}
}
oldDescriptor.valueParameters.mapIndexedTo(this) { i, oldValueParameterDescriptor ->
createUnsubstitutedParameter(newDescriptor, oldValueParameterDescriptor, closureParametersCount + i).apply {
recordRemappedParameter(oldValueParameterDescriptor, this)
}
}
}
return newValueParameters
}
private fun createTransformedConstructorDescriptor(localFunctionContext: LocalClassConstructorContext) {
val oldDescriptor = localFunctionContext.descriptor
val localClassContext = localClasses[oldDescriptor.containingDeclaration]!!
val newDescriptor = ClassConstructorDescriptorImpl.create(
localClassContext.descriptor,
Annotations.EMPTY, oldDescriptor.isPrimary, oldDescriptor.source)
localFunctionContext.transformedDescriptor = newDescriptor
transformedDescriptors[oldDescriptor] = newDescriptor
// Do not substitute type parameters for now.
val newTypeParameters = oldDescriptor.typeParameters
val capturedValues = localClassContext.closure.capturedValues
val newValueParameters = createTransformedValueParameters(localFunctionContext, capturedValues)
newDescriptor.initialize(
newValueParameters,
Visibilities.PRIVATE,
newTypeParameters
)
oldDescriptor.dispatchReceiverParameter?.let {
recordRemappedParameter(it, newDescriptor.dispatchReceiverParameter!!)
}
oldDescriptor.extensionReceiverParameter?.let {
throw AssertionError("constructors can't have extension receiver")
}
}
private fun createFieldsForCapturedValues(localClassContext: LocalClassContext) {
val classDescriptor = localClassContext.descriptor
localClassContext.closure.capturedValues.forEach { capturedValue ->
val fieldDescriptor = PropertyDescriptorImpl.create(
classDescriptor,
Annotations.EMPTY,
Modality.FINAL,
Visibilities.PRIVATE,
/* isVar = */ false,
suggestNameForCapturedValue(capturedValue),
CallableMemberDescriptor.Kind.SYNTHESIZED,
SourceElement.NO_SOURCE,
/* lateInit = */ false,
/* isConst = */ false,
/* isHeader = */ false,
/* isImpl = */ false,
/* isExternal = */ false,
/* isDelegated = */ false)
fieldDescriptor.initialize(/* getter = */ null, /* setter = */ null)
val extensionReceiverParameter: ReceiverParameterDescriptor? = null
fieldDescriptor.setType(
capturedValue.type,
emptyList<TypeParameterDescriptor>(),
classDescriptor.thisAsReceiverParameter,
extensionReceiverParameter)
localClassContext.capturedValueToField[capturedValue] = fieldDescriptor
}
}
private fun LocalContextWithClosureAsParameters.recordCapturedAsParameter(
oldDescriptor: ValueDescriptor,
newDescriptor: ValueParameterDescriptor
) {
capturedValueToParameter[oldDescriptor] = newDescriptor
newParameterToCaptured[newDescriptor] = oldDescriptor
}
private fun <K, V> MutableMap<K, V>.putAbsentOrSame(key: K, value: V) {
val current = this.getOrPut(key, { value })
if (current != value) {
error("$current != $value")
}
}
private fun recordRemappedParameter(oldDescriptor: ParameterDescriptor, newDescriptor: ParameterDescriptor) {
oldParameterToNew.putAbsentOrSame(oldDescriptor, newDescriptor)
newParameterToOld.putAbsentOrSame(newDescriptor, oldDescriptor)
}
private fun suggestNameForCapturedValue(valueDescriptor: ValueDescriptor): Name =
if (valueDescriptor.name.isSpecial) {
val oldNameStr = valueDescriptor.name.asString()
Name.identifier("$" + oldNameStr.substring(1, oldNameStr.length - 1))
}
else
valueDescriptor.name
private fun createUnsubstitutedCapturedValueParameter(
newParameterOwner: CallableMemberDescriptor,
valueDescriptor: ValueDescriptor,
index: Int
): ValueParameterDescriptor =
ValueParameterDescriptorImpl(
newParameterOwner, null, index,
valueDescriptor.annotations,
suggestNameForCapturedValue(valueDescriptor),
valueDescriptor.type,
false, false, false, null, valueDescriptor.source
)
private fun createUnsubstitutedParameter(
newParameterOwner: CallableMemberDescriptor,
valueParameterDescriptor: ValueParameterDescriptor,
newIndex: Int
): ValueParameterDescriptor =
valueParameterDescriptor.copy(newParameterOwner, valueParameterDescriptor.name, newIndex)
private fun collectClosures() {
memberFunction.acceptChildrenVoid(object : AbstractClosureRecorder() {
override fun recordFunctionClosure(functionDescriptor: FunctionDescriptor, closure: Closure) {
localFunctions[functionDescriptor]?.closure = closure
}
override fun recordClassClosure(classDescriptor: ClassDescriptor, closure: Closure) {
localClasses[classDescriptor]?.closure = closure
}
})
}
private fun collectLocalDeclarations() {
memberFunction.acceptChildrenVoid(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
private fun DeclarationDescriptor.isClassMember() = when (this.containingDeclaration) {
is CallableDescriptor -> false
is ClassDescriptor -> true
else -> TODO(this.toString())
}
override fun visitFunction(declaration: IrFunction) {
declaration.acceptChildrenVoid(this)
val descriptor = declaration.descriptor
if (!descriptor.isClassMember()) {
val localFunctionContext = LocalFunctionContext(declaration)
localFunctions[descriptor] = localFunctionContext
if (descriptor.name.isSpecial) {
localFunctionContext.index = lambdasCount++
}
}
}
override fun visitConstructor(declaration: IrConstructor) {
declaration.acceptChildrenVoid(this)
val descriptor = declaration.descriptor
assert (descriptor.isClassMember())
localClassConstructors[descriptor] = LocalClassConstructorContext(declaration)
}
override fun visitClass(declaration: IrClass) {
declaration.acceptChildrenVoid(this)
val descriptor = declaration.descriptor
if (descriptor.isClassMember()) {
assert (descriptor.isInner)
} else {
val localClassContext = LocalClassContext(declaration)
localClasses[descriptor] = localClassContext
}
}
})
}
}
}
@@ -1,36 +1,88 @@
package org.jetbrains.kotlin.backend.common.lower
import org.jetbrains.kotlin.backend.common.BackendContext
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.incremental.components.LookupLocation
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.NonReportingOverrideStrategy
import org.jetbrains.kotlin.resolve.OverridingUtil
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.resolve.scopes.MemberScopeImpl
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.utils.Printer
class IrLoweringContext(backendContext: BackendContext) : IrGeneratorContext(backendContext.irBuiltIns)
class FunctionIrGenerator(backendContext: BackendContext, functionDescriptor: FunctionDescriptor) : IrGeneratorWithScope {
override val context = IrLoweringContext(backendContext)
override val scope = Scope(functionDescriptor)
}
class FunctionIrBuilder(backendContext: BackendContext, functionDescriptor: FunctionDescriptor) :
IrBuilderWithScope(
IrLoweringContext(backendContext),
Scope(functionDescriptor),
UNDEFINED_OFFSET,
UNDEFINED_OFFSET
)
fun BackendContext.createFunctionIrGenerator(functionDescriptor: FunctionDescriptor) =
FunctionIrGenerator(this, functionDescriptor)
fun BackendContext.createFunctionIrBuilder(functionDescriptor: FunctionDescriptor) =
FunctionIrBuilder(this, functionDescriptor)
class FunctionIrBuilder(context: IrLoweringContext, scope: Scope, startOffset: Int, endOffset: Int) :
IrBuilderWithScope(context, scope, startOffset, endOffset)
fun FunctionIrGenerator.createIrBuilder() = FunctionIrBuilder(context, scope, UNDEFINED_OFFSET, UNDEFINED_OFFSET)
fun <T : IrBuilder> T.at(element: IrElement) = this.at(element.startOffset, element.endOffset)
/**
* Builds [IrBlock] to be used instead of given expression.
*/
inline fun FunctionIrGenerator.irBlock(expression: IrExpression, origin: IrStatementOrigin? = null,
resultType: KotlinType? = expression.type,
body: IrBlockBuilder.() -> Unit) =
inline fun IrGeneratorWithScope.irBlock(expression: IrExpression, origin: IrStatementOrigin? = null,
resultType: KotlinType? = expression.type,
body: IrBlockBuilder.() -> Unit) =
this.irBlock(expression.startOffset, expression.endOffset, origin, resultType, body)
inline fun FunctionIrGenerator.irBlockBody(irElement: IrElement, body: IrBlockBodyBuilder.() -> Unit) =
this.irBlockBody(irElement.startOffset, irElement.endOffset, body)
inline fun IrGeneratorWithScope.irBlockBody(irElement: IrElement, body: IrBlockBodyBuilder.() -> Unit) =
this.irBlockBody(irElement.startOffset, irElement.endOffset, body)
fun computeOverrides(current: ClassDescriptor, functionsFromCurrent: List<CallableMemberDescriptor>): List<DeclarationDescriptor> {
val result = mutableListOf<DeclarationDescriptor>()
val allSuperDescriptors = current.typeConstructor.supertypes
.flatMap { it.memberScope.getContributedDescriptors() }
.filterIsInstance<CallableMemberDescriptor>()
for ((name, group) in allSuperDescriptors.groupBy { it.name }) {
OverridingUtil.generateOverridesInFunctionGroup(
name,
/* membersFromSupertypes = */ group,
/* membersFromCurrent = */ functionsFromCurrent.filter { it.name == name },
current,
object : NonReportingOverrideStrategy() {
override fun addFakeOverride(fakeOverride: CallableMemberDescriptor) {
result.add(fakeOverride)
}
override fun conflict(fromSuper: CallableMemberDescriptor, fromCurrent: CallableMemberDescriptor) {
error("Conflict in scope of $current: $fromSuper vs $fromCurrent")
}
}
)
}
return result
}
class SimpleMemberScope(val members: List<DeclarationDescriptor>) : MemberScopeImpl() {
override fun getContributedClassifier(name: Name, location: LookupLocation): ClassifierDescriptor? = TODO()
override fun getContributedVariables(name: Name, location: LookupLocation): Collection<PropertyDescriptor> = TODO()
override fun getContributedFunctions(name: Name, location: LookupLocation): Collection<SimpleFunctionDescriptor> = TODO()
override fun getContributedDescriptors(kindFilter: DescriptorKindFilter,
nameFilter: (Name) -> Boolean): Collection<DeclarationDescriptor> =
members.filter { kindFilter.accepts(it) && nameFilter(it.name) }
override fun printScopeStructure(p: Printer) = TODO("not implemented")
}
@@ -1,28 +1,47 @@
package org.jetbrains.kotlin.backend.konan
import llvm.*
import org.jetbrains.kotlin.backend.konan.ir.Ir
import org.jetbrains.kotlin.backend.konan.ir.ModuleIndex
import org.jetbrains.kotlin.backend.konan.llvm.Runtime
import org.jetbrains.kotlin.backend.konan.llvm.getFunctionType
import org.jetbrains.kotlin.backend.konan.llvm.StaticData
import org.jetbrains.kotlin.backend.konan.llvm.Llvm
import org.jetbrains.kotlin.backend.konan.KonanBackendContext
import org.jetbrains.kotlin.backend.konan.KonanPhase
import org.jetbrains.kotlin.backend.konan.KonanConfig
import org.jetbrains.kotlin.backend.konan.KonanConfigKeys
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.resolve.BindingContext
import llvm.LLVMDumpModule
import llvm.LLVMModuleRef
import org.jetbrains.kotlin.backend.jvm.descriptors.initialize
import org.jetbrains.kotlin.backend.konan.descriptors.deepPrint
import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName
import org.jetbrains.kotlin.backend.konan.ir.Ir
import org.jetbrains.kotlin.backend.konan.llvm.Llvm
import org.jetbrains.kotlin.backend.konan.llvm.LlvmDeclarations
import org.jetbrains.kotlin.backend.konan.llvm.verifyModule
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.ReceiverParameterDescriptorImpl
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.util.DumpIrTreeVisitor
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver
import java.lang.System.out
internal class SpecialDescriptorsFactory {
private val outerThisDescriptors = mutableMapOf<ClassDescriptor, PropertyDescriptor>()
fun getOuterThisFieldDescriptor(innerClassDescriptor: ClassDescriptor): PropertyDescriptor =
if (!innerClassDescriptor.isInner) throw AssertionError("Class is not inner: $innerClassDescriptor")
else outerThisDescriptors.getOrPut(innerClassDescriptor) {
val outerClassDescriptor = DescriptorUtils.getContainingClass(innerClassDescriptor) ?:
throw AssertionError("No containing class for inner class $innerClassDescriptor")
val receiver = ReceiverParameterDescriptorImpl(innerClassDescriptor, ImplicitClassReceiver(innerClassDescriptor))
PropertyDescriptorImpl.create(innerClassDescriptor, Annotations.EMPTY, Modality.FINAL, Visibilities.PRIVATE,
false, "this$0".synthesizedName, CallableMemberDescriptor.Kind.SYNTHESIZED, SourceElement.NO_SOURCE,
false, false, false, false, false, false).initialize(outerClassDescriptor.defaultType, dispatchReceiverParameter = receiver)
}
}
internal final class Context(val config: KonanConfig) : KonanBackendContext() {
var moduleDescriptor: ModuleDescriptor? = null
val specialDescriptorsFactory = SpecialDescriptorsFactory()
// TODO: make lateinit?
var irModule: IrModuleFragment? = null
set(module: IrModuleFragment?) {
if (field != null) {
@@ -49,6 +68,7 @@ internal final class Context(val config: KonanConfig) : KonanBackendContext() {
}
lateinit var llvm: Llvm
lateinit var llvmDeclarations: LlvmDeclarations
var phase: KonanPhase? = null
var depth: Int = 0
@@ -1,36 +1,42 @@
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.config.CompilerConfiguration
import java.io.File
import java.util.Properties
public class Distribution(val properties: KonanProperties,
val os: String, val target: String) {
public class Distribution(val config: CompilerConfiguration) {
companion object {
val targetManager = TargetManager(config)
val target =
if (!targetManager.crossCompile) "host"
else targetManager.current.name.toLowerCase()
val suffix = targetManager.currentSuffix()
private fun findKonanHome(): String {
val value = System.getProperty("konan.home", "dist")
val path = File(value).absolutePath
return path
}
val konanHome = findKonanHome()
val propertyFile = "$konanHome/konan/konan.properties"
val lib = "$konanHome/lib"
// TODO: Pack all needed things to dist.
val dependencies = "$konanHome/../dependencies/all"
val start = "$lib/start.kt.bc"
val stdlib = "$lib/stdlib.kt.bc"
val runtime = "$lib/runtime.bc"
val launcher = "$lib/launcher.bc"
private fun findKonanHome(): String {
val value = System.getProperty("konan.home", "dist")
val path = File(value).absolutePath
return path
}
val llvmHome = "$dependencies/${properties.propertyString("llvmHome.$os")}"
val sysRoot = "$dependencies/${properties.propertyString("sysRoot.$os")}"
val libGcc = "$dependencies/${properties.propertyString("libGcc.$os")}"
val konanHome = findKonanHome()
val propertyFile = config.get(KonanConfigKeys.PROPERTY_FILE)
?: "$konanHome/konan/konan.properties"
val properties = KonanProperties(propertyFile)
val lib = "$konanHome/lib/$target"
// TODO: Pack all needed things to dist.
val dependencies = "$konanHome/../dependencies/all"
val stdlib = "$lib/stdlib.kt.bc"
val start = "$lib/start.kt.bc"
val launcher = "$lib/launcher.bc"
val runtime = config.get(KonanConfigKeys.RUNTIME_FILE)
?: "$lib/runtime.bc"
val llvmHome = "$dependencies/${properties.propertyString("llvmHome.$suffix")}"
val sysRoot = "$dependencies/${properties.propertyString("sysRoot.$suffix")}"
val libGcc = "$dependencies/${properties.propertyString("libGcc.$suffix")}"
val llvmBin = "$llvmHome/bin"
val llvmLib = "$llvmHome/lib"
@@ -40,9 +46,9 @@ public class Distribution(val properties: KonanProperties,
val llvmLto = "$llvmBin/llvm-lto"
val llvmLink = "$llvmBin/llvm-link"
val libCppAbi = "$llvmLib/libc++abi.a"
val libLTO = when (os) {
"osx" -> "$llvmLib/libLTO.dylib"
"linux" -> "$llvmLib/libLTO.so"
else -> error("Don't know libLTO location for the platform.")
val libLTO = when (TargetManager.host) {
KonanTarget.MACBOOK -> "$llvmLib/libLTO.dylib"
KonanTarget.LINUX -> "$llvmLib/libLTO.so"
else -> error("Don't know libLTO location for this platform.")
}
}
@@ -16,13 +16,15 @@ import java.io.File
class KonanConfig(val project: Project, val configuration: CompilerConfiguration) {
internal val distribution = Distribution(configuration)
internal val libraries: List<String>
get() {
val fromCommandLine = configuration.getList(KonanConfigKeys.LIBRARY_FILES)
if (configuration.get(KonanConfigKeys.NOSTDLIB) ?: false) {
return fromCommandLine
}
return fromCommandLine + Distribution.stdlib
return fromCommandLine + distribution.stdlib
}
private val loadedDescriptors = loadLibMetadata(libraries)
@@ -1,60 +1,64 @@
package org.jetbrains.kotlin.backend.konan;
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.config.CompilerConfigurationKey;
import org.jetbrains.kotlin.serialization.js.ModuleKind;
import org.jetbrains.kotlin.config.CompilerConfigurationKey
import org.jetbrains.kotlin.serialization.js.ModuleKind
class KonanConfigKeys {
companion object {
val LIBRARY_FILES: CompilerConfigurationKey<List<String>>
= CompilerConfigurationKey.create("library file paths");
= CompilerConfigurationKey.create("library file paths")
val BITCODE_FILE: CompilerConfigurationKey<String>
= CompilerConfigurationKey.create("emitted bitcode file path");
= CompilerConfigurationKey.create("emitted bitcode file path")
val EXECUTABLE_FILE: CompilerConfigurationKey<String>
= CompilerConfigurationKey.create("final executable file path");
= CompilerConfigurationKey.create("final executable file path")
val RUNTIME_FILE: CompilerConfigurationKey<String?>
= CompilerConfigurationKey.create("override default runtime file path");
= CompilerConfigurationKey.create("override default runtime file path")
val PROPERTY_FILE: CompilerConfigurationKey<String?>
= CompilerConfigurationKey.create("override default property file path");
= CompilerConfigurationKey.create("override default property file path")
val ABI_VERSION: CompilerConfigurationKey<Int>
= CompilerConfigurationKey.create("current abi version");
= CompilerConfigurationKey.create("current abi version")
val OPTIMIZATION: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("optimized compilation");
= CompilerConfigurationKey.create("optimized compilation")
val NOSTDLIB: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("don't link with stdlib");
= CompilerConfigurationKey.create("don't link with stdlib")
val NOLINK: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("don't link, only produce a bitcode file ");
= CompilerConfigurationKey.create("don't link, only produce a bitcode file ")
val TARGET: CompilerConfigurationKey<String?>
= CompilerConfigurationKey.create("target we compile for")
val LIST_TARGETS: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("list available targets")
val SOURCE_MAP: CompilerConfigurationKey<List<String>>
= CompilerConfigurationKey.create("generate source map");
= CompilerConfigurationKey.create("generate source map")
val META_INFO: CompilerConfigurationKey<List<String>>
= CompilerConfigurationKey.create("generate metadata");
= CompilerConfigurationKey.create("generate metadata")
val MODULE_KIND: CompilerConfigurationKey<ModuleKind>
= CompilerConfigurationKey.create("module kind");
= CompilerConfigurationKey.create("module kind")
val VERIFY_IR: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("verify ir");
= CompilerConfigurationKey.create("verify ir")
val VERIFY_DESCRIPTORS: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("verify descriptors");
= CompilerConfigurationKey.create("verify descriptors")
val VERIFY_BITCODE: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("verify bitcode");
= CompilerConfigurationKey.create("verify bitcode")
val PRINT_IR: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("print ir");
= CompilerConfigurationKey.create("print ir")
val PRINT_DESCRIPTORS: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("print descriptors");
= CompilerConfigurationKey.create("print descriptors")
val PRINT_BITCODE: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("print bitcode");
= CompilerConfigurationKey.create("print bitcode")
val ENABLED_PHASES: CompilerConfigurationKey<List<String>>
= CompilerConfigurationKey.create("enable backend phases");
= CompilerConfigurationKey.create("enable backend phases")
val DISABLED_PHASES: CompilerConfigurationKey<List<String>>
= CompilerConfigurationKey.create("disable backend phases");
= CompilerConfigurationKey.create("disable backend phases")
val VERBOSE_PHASES: CompilerConfigurationKey<List<String>>
= CompilerConfigurationKey.create("verbose backend phases");
= CompilerConfigurationKey.create("verbose backend phases")
val LIST_PHASES: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("list backend phases");
= CompilerConfigurationKey.create("list backend phases")
val TIME_PHASES: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("time backend phases");
= CompilerConfigurationKey.create("time backend phases")
}
}
@@ -4,12 +4,14 @@ import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.backend.konan.llvm.emitLLVM
import org.jetbrains.kotlin.backend.konan.util.profile
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.ir.ModuleIndex
import org.jetbrains.kotlin.cli.common.CLICompiler
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.kotlinSourceRoots
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.psi2ir.Psi2IrConfiguration
@@ -34,11 +36,18 @@ public fun runTopLevelPhases(konanConfig: KonanConfig, environment: KotlinCoreEn
val config = konanConfig.configuration
val targets = TargetManager(config)
if (config.get(KonanConfigKeys.LIST_TARGETS) ?: false) {
targets.list()
}
KonanPhases.config(konanConfig)
if (config.get(KonanConfigKeys.LIST_PHASES) ?: false) {
KonanPhases.list()
}
if (config.kotlinSourceRoots.isEmpty()) return
val collector = config.getNotNull(
CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
@@ -67,6 +76,7 @@ public fun runTopLevelPhases(konanConfig: KonanConfig, environment: KotlinCoreEn
phaser.phase(KonanPhase.BACKEND) {
phaser.phase(KonanPhase.LOWER) {
KonanLower(context).lower()
context.ir.moduleIndexForCodegen = ModuleIndex(context.ir.irModule)
}
phaser.phase(KonanPhase.BITCODE) {
emitLLVM(context)
@@ -20,6 +20,15 @@ internal class KonanLower(val context: Context) {
phaser.phase(KonanPhase.LOWER_INLINE) {
FunctionInlining(context).inline(irFile)
}
phaser.phase(KonanPhase.LOWER_ENUMS) {
EnumClassLowering(context).run(irFile)
}
phaser.phase(KonanPhase.LOWER_INNER_CLASSES) {
InnerClassLowering(context).runOnFilePostfix(irFile)
}
phaser.phase(KonanPhase.LOWER_VARARG) {
VarargInjectionLowering(context).runOnFilePostfix(irFile)
}
phaser.phase(KonanPhase.LOWER_DEFAULT_PARAMETER_EXTENT) {
DefaultParameterStubGenerator(context).runOnFilePostfix(irFile)
DefaultParameterInjector(context).runOnFilePostfix(irFile)
@@ -34,7 +43,7 @@ internal class KonanLower(val context: Context) {
SharedVariablesLowering(context).runOnFilePostfix(irFile)
}
phaser.phase(KonanPhase.LOWER_LOCAL_FUNCTIONS) {
LocalFunctionsLowering(context).runOnFilePostfix(irFile)
LocalDeclarationsLowering(context).runOnFilePostfix(irFile)
}
phaser.phase(KonanPhase.LOWER_CALLABLES) {
CallableReferenceLowering(context).runOnFilePostfix(irFile)
@@ -11,6 +11,7 @@ enum class KonanPhase(val description: String,
/* */ BACKEND("All backend"),
/* ... */ LOWER("IR Lowering"),
/* ... ... */ LOWER_BUILTIN_OPERATORS("BuiltIn Operators Lowering"),
/* ... ... */ LOWER_VARARG("Vararg lowering"),
/* ... ... */ LOWER_DEFAULT_PARAMETER_EXTENT("Default Parameter Extent Lowering"),
/* ... ... */ LOWER_TYPE_OPERATORS("Type operators lowering"),
/* ... ... */ LOWER_SHARED_VARIABLES("Shared Variable Lowering"),
@@ -18,6 +19,8 @@ enum class KonanPhase(val description: String,
/* ... ... */ LOWER_CALLABLES("Callable references Lowering"),
/* ... ... */ LOWER_INLINE("Functions inlining"),
/* ... ... */ AUTOBOX("Autoboxing of primitive types"),
/* ... ... */ LOWER_ENUMS("Enum classes lowering"),
/* ... ... */ LOWER_INNER_CLASSES("Inner classes lowering"),
/* ... */ BITCODE("LLVM BitCode Generation"),
/* ... ... */ RTTI("RTTI Generation"),
/* ... ... */ CODEGEN("Code Generation"),
@@ -1,15 +1,14 @@
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.config.CompilerConfiguration
import java.io.File
import java.util.Properties
public class KonanProperties(config: CompilerConfiguration) {
public class KonanProperties(val propertyFile: String) {
val properties = Properties()
init {
val file = File(config.get(KonanConfigKeys.PROPERTY_FILE))
val file = File(propertyFile)
file.bufferedReader()?.use { reader ->
properties.load(reader)
}
@@ -0,0 +1,106 @@
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.CompilerConfiguration
enum class KonanTarget(var enabled: Boolean = false) {
IPHONE(),
IPHONE_SIM(),
LINUX(),
MACBOOK()
}
class TargetManager(val config: CompilerConfiguration) {
val targets = KonanTarget.values().associate{ it.name.toLowerCase() to it }
val current = determineCurrent()
init {
when (host) {
KonanTarget.LINUX -> KonanTarget.LINUX.enabled = true
KonanTarget.MACBOOK -> {
KonanTarget.MACBOOK.enabled = true
KonanTarget.IPHONE.enabled = true
KonanTarget.IPHONE_SIM.enabled = true
}
}
if (!current.enabled) {
error("Target $current is not available on the current host")
}
}
fun known(name: String): String {
if (targets[name] == null) {
error("Unknown target: $name. Use -list_targets to see the list of available targets")
}
return name!!
}
fun list() {
targets.forEach { key, target ->
if (target.enabled) {
val isDefault = if (target == current) "(default)" else ""
println(String.format("%1$-30s%2$-10s", "$key:", "$isDefault"))
}
}
}
fun determineCurrent(): KonanTarget {
val userRequest = config.get(KonanConfigKeys.TARGET)
return if (userRequest == null || userRequest == "host") {
host
} else {
targets[known(userRequest)]!!
}
}
fun currentSuffix(): String {
if (host == KonanTarget.MACBOOK) {
when (current) {
KonanTarget.MACBOOK -> return("osx")
KonanTarget.IPHONE -> return("osx-ios")
KonanTarget.IPHONE_SIM -> return("osx-ios-sim")
else -> error("Impossible combination of $host and $current")
}
}
if (host == KonanTarget.LINUX) {
when (current) {
KonanTarget.LINUX -> return("linux")
KonanTarget.IPHONE -> return("linux-ios")
KonanTarget.IPHONE_SIM -> return("linux-ios-sim")
else -> error("Impossible combination of $host and $current")
}
}
error("Unknown host target $host)")
}
companion object {
fun host_os(): String {
val javaOsName = System.getProperty("os.name")
return when (javaOsName) {
"Mac OS X" -> "osx"
"Linux" -> "linux"
else -> error("Unknown operating system: ${javaOsName}")
}
}
fun host_arch(): String {
val javaArch = System.getProperty("os.arch")
return when (javaArch) {
"x86_64" -> "x86_64"
"amd64" -> "x86_64"
"arm64" -> "arm64"
else -> error("Unknown hardware platform: ${javaArch}")
}
}
val host: KonanTarget = when (host_os()) {
"osx" -> KonanTarget.MACBOOK
"linux" -> KonanTarget.LINUX
else -> error("Unknown host target: ${host_os()} ${host_arch()}")
}
}
val crossCompile = (host != current)
}
@@ -10,8 +10,8 @@ typealias ExecutableFile = String
// Use "clang -v -save-temps" to write linkCommand() method
// for another implementation of this class.
internal abstract class PlatformFlags(val distrib: Distribution,
val properties: KonanProperties) {
internal abstract class PlatformFlags(val distribution: Distribution) {
val properties = distribution.properties
abstract val llvmLtoFlags: List<String>
abstract val llvmLlcFlags: List<String>
@@ -25,8 +25,8 @@ internal abstract class PlatformFlags(val distrib: Distribution,
}
internal class MacOSPlatform(distrib: Distribution,
properties: KonanProperties) : PlatformFlags(distrib, properties) {
internal open class MacOSPlatform(distribution: Distribution)
: PlatformFlags(distribution) {
override val llvmLtoFlags = properties.propertyList("llvmLtoFlags.osx")
override val llvmLlcFlags = properties.propertyList("llvmLlcFlags.osx")
@@ -34,28 +34,49 @@ internal class MacOSPlatform(distrib: Distribution,
override val linkerOptimizationFlags =
properties.propertyList("linkerOptimizationFlags.osx")
override val linkerKonanFlags = properties.propertyList("linkerKonanFlags.osx")
override val linker = "${distrib.sysRoot}/usr/bin/ld"
override val linker = "${distribution.sysRoot}/usr/bin/ld"
open val arch = properties.propertyString("arch.osx")!!
open val osVersionMin = properties.propertyList("osVersionMin.osx")
open val sysRoot = distribution.sysRoot
open val targetSysRoot = sysRoot
open val llvmLib = distribution.llvmLib
override fun linkCommand(objectFiles: List<String>, executable: String, optimize: Boolean): List<String> {
val sysRoot = distrib.sysRoot
val llvmLib = distrib.llvmLib
return mutableListOf<String>(linker, "-demangle") +
if (optimize) listOf("-object_path_lto", "temporary.o", "-lto_library", distrib.libLTO) else {listOf<String>()} +
listOf( "-dynamic", "-arch", "x86_64", "-macosx_version_min") +
properties.propertyList("macosVersionMin.osx") +
listOf("-syslibroot", "$sysRoot",
if (optimize) listOf("-object_path_lto", "temporary.o", "-lto_library", distribution.libLTO) else {listOf<String>()} +
listOf( "-dynamic", "-arch", arch) +
osVersionMin +
listOf("-syslibroot", "$targetSysRoot",
"-o", executable) +
objectFiles +
if (optimize) linkerOptimizationFlags else {listOf<String>()} +
linkerKonanFlags +
listOf("-lSystem", "$llvmLib/clang/3.8.0/lib/darwin/libclang_rt.osx.a")
listOf("-lSystem")
}
}
internal class LinuxPlatform(distrib: Distribution,
properties: KonanProperties) : PlatformFlags(distrib, properties) {
internal class IPhoneOSfromMacOSPlatform(distribution: Distribution)
: MacOSPlatform(distribution) {
override val arch = properties.propertyString("arch.osx-ios")!!
override val osVersionMin = properties.propertyList("osVersionMin.osx-ios")
override val llvmLlcFlags = properties.propertyList("llvmLlcFlags.osx-ios")
override val targetSysRoot = "${distribution.dependencies}/${properties.propertyString("targetSysRoot.osx-ios")!!}"
}
internal class IPhoneSimulatorFromMacOSPlatform(distribution: Distribution)
: MacOSPlatform(distribution) {
override val arch = properties.propertyString("arch.osx-ios-sim")!!
override val osVersionMin = properties.propertyList("osVersionMin.osx-ios-sim")
override val llvmLlcFlags = properties.propertyList("llvmLlcFlags.osx-ios-sim")
override val targetSysRoot = "${distribution.dependencies}/${properties.propertyString("targetSysRoot.osx-ios-sim")!!}"
}
internal class LinuxPlatform(distribution: Distribution)
: PlatformFlags(distribution) {
override val llvmLtoFlags = properties.propertyList("llvmLtoFlags.linux")
override val llvmLlcFlags = properties.propertyList("llvmLlcFlags.linux")
@@ -63,16 +84,16 @@ internal class LinuxPlatform(distrib: Distribution,
override val linkerOptimizationFlags =
properties.propertyList("linkerOptimizationFlags.linux")
override val linkerKonanFlags = properties.propertyList("linkerKonanFlags.linux")
override val linker = "${distrib.sysRoot}/../bin/ld.gold"
override val linker = "${distribution.sysRoot}/../bin/ld.gold"
val pluginOptimizationFlags =
properties.propertyList("pluginOptimizationFlags.linux")
override fun linkCommand(objectFiles: List<String>, executable: String, optimize: Boolean): List<String> {
val sysRoot = distrib.sysRoot
val llvmLib = distrib.llvmLib
val libGcc = distrib.libGcc
val sysRoot = distribution.sysRoot
val llvmLib = distribution.llvmLib
val libGcc = distribution.libGcc
// TODO: Can we extract more to the konan.properties?
return mutableListOf<String>("$linker",
@@ -97,41 +118,40 @@ internal class LinuxPlatform(distrib: Distribution,
internal class LinkStage(val context: Context) {
private val javaOsName = System.getProperty("os.name")
private val javaArch = System.getProperty("os.arch")
val os = when (javaOsName) {
"Mac OS X" -> "osx"
"Linux" -> "linux"
else -> error("Unknown operating system: ${javaOsName}")
}
val arch = when (javaArch) {
"x86_64" -> "x86_64"
"amd64" -> "x86_64"
else -> error("Unknown hardware platform: ${javaArch}")
}
private val properties = KonanProperties(context.config.configuration)
private val distrib = Distribution(properties, os, arch)
val platform: PlatformFlags = when (os) {
"linux" -> LinuxPlatform(distrib, properties)
"osx" -> MacOSPlatform(distrib, properties)
else -> error("Could not tell the current platform")
}
val config = context.config.configuration
val targetManager = TargetManager(config)
private val distribution =
Distribution(context.config.configuration)
private val properties = distribution.properties
val platform: PlatformFlags = when (TargetManager.host) {
KonanTarget.LINUX -> LinuxPlatform(distribution)
KonanTarget.MACBOOK -> when (targetManager.current) {
KonanTarget.IPHONE_SIM
-> IPhoneSimulatorFromMacOSPlatform(distribution)
KonanTarget.IPHONE
-> IPhoneOSfromMacOSPlatform(distribution)
KonanTarget.MACBOOK
-> MacOSPlatform(distribution)
else -> TODO("Target not implemented yet.")
}
else -> TODO("Target not implemented yet")
}
val suffix = targetManager.currentSuffix()
val optimize = config.get(KonanConfigKeys.OPTIMIZATION) ?: false
val emitted = config.get(KonanConfigKeys.BITCODE_FILE)!!
val nostdlib = config.get(KonanConfigKeys.NOSTDLIB) ?: false
val libraries = context.config.libraries
fun llvmLto(files: List<BitcodeFile>): ObjectFile {
// TODO: Make it a temporary file.
val combined = "combined.o"
val tmpCombined = File.createTempFile("combined", ".o")
tmpCombined.deleteOnExit()
val combined = tmpCombined.absolutePath
val tool = distrib.llvmLto
val tool = distribution.llvmLto
val command = mutableListOf(tool, "-o", combined)
if (optimize) {
command.addAll(platform.llvmLtoFlags)
@@ -143,12 +163,13 @@ internal class LinkStage(val context: Context) {
}
fun llvmLlc(file: BitcodeFile): ObjectFile {
// TODO: Make it a temporary file.
val objectFile = "$file.o"
val tmpObjectFile = File.createTempFile(File(file).name, ".o")
tmpObjectFile.deleteOnExit()
val objectFile = tmpObjectFile.absolutePath
val tool = distrib.llvmLlc
val command = listOf(distrib.llvmLlc, "-o", objectFile, "-filetype=obj") +
properties.propertyList("llvmLlcFlags.$os") + listOf(file)
val tool = distribution.llvmLlc
val command = listOf(distribution.llvmLlc, "-o", objectFile, "-filetype=obj") +
properties.propertyList("llvmLlcFlags.$suffix") + listOf(file)
runTool(*command.toTypedArray())
return objectFile
@@ -186,10 +207,10 @@ internal class LinkStage(val context: Context) {
}
fun linkStage() {
context.log("# Compiler root: ${Distribution.konanHome}")
context.log("# Compiler root: ${distribution.konanHome}")
val bitcodeFiles = listOf<BitcodeFile>(emitted, Distribution.start, Distribution.runtime,
Distribution.launcher) + libraries
val bitcodeFiles = listOf<BitcodeFile>(emitted, distribution.start, distribution.runtime,
distribution.launcher) + libraries
val objectFiles = if (optimize) {
listOf( llvmLto(bitcodeFiles ) )
@@ -5,13 +5,20 @@ import org.jetbrains.kotlin.backend.konan.llvm.functionName
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.isFunctionType
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.PropertyGetterDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.PropertySetterDescriptorImpl
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.OverridingUtil
import org.jetbrains.kotlin.resolve.descriptorUtil.*
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeSubstitution
import org.jetbrains.kotlin.types.TypeSubstitutor
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.typeUtil.isUnit
import org.jetbrains.kotlin.util.OperatorNameConventions
@@ -35,16 +42,16 @@ internal val ClassDescriptor.implementedInterfaces: List<ClassDescriptor>
*
* TODO: this method is actually a part of resolve and probably duplicates another one
*/
internal val FunctionDescriptor.implementation: FunctionDescriptor
get() {
if (this.kind.isReal) {
return this
} else {
val overridden = OverridingUtil.getOverriddenDeclarations(this)
val filtered = OverridingUtil.filterOutOverridden(overridden)
return filtered.first { it.modality != Modality.ABSTRACT } as FunctionDescriptor
}
internal fun FunctionDescriptor.resolveFakeOverride(): FunctionDescriptor {
if (this.kind.isReal) {
return this
} else {
val overridden = OverridingUtil.getOverriddenDeclarations(this)
val filtered = OverridingUtil.filterOutOverridden(overridden)
// TODO: is it correct to take first?
return filtered.first { it.modality != Modality.ABSTRACT } as FunctionDescriptor
}
}
private val intrinsicAnnotation = FqName("konan.internal.Intrinsic")
@@ -128,16 +135,18 @@ internal fun KotlinType.isUnboundCallableReference(): Boolean {
internal val CallableDescriptor.allValueParameters: List<ParameterDescriptor>
get() {
val constructorReceiver = if (this is ConstructorDescriptor) {
this.constructedClass.thisAsReceiverParameter
} else {
null
}
val receivers = mutableListOf<ParameterDescriptor>()
val receivers = listOf(
constructorReceiver,
this.dispatchReceiverParameter,
this.extensionReceiverParameter).filterNotNull()
if (this is ConstructorDescriptor)
receivers.add(this.constructedClass.thisAsReceiverParameter)
val dispatchReceiverParameter = this.dispatchReceiverParameter
if (dispatchReceiverParameter != null)
receivers.add(dispatchReceiverParameter)
val extensionReceiverParameter = this.extensionReceiverParameter
if (extensionReceiverParameter != null)
receivers.add(extensionReceiverParameter)
return receivers + this.valueParameters
}
@@ -152,7 +161,7 @@ internal val FunctionDescriptor.isFunctionInvoke: Boolean
internal fun ClassDescriptor.isUnit() = this.defaultType.isUnit()
internal val ClassDescriptor.сontributedMethods: List<FunctionDescriptor>
internal val ClassDescriptor.contributedMethods: List<FunctionDescriptor>
get() {
val contributedDescriptors = unsubstitutedMemberScope.getContributedDescriptors()
// (includes declarations from supers)
@@ -171,6 +180,7 @@ internal val ClassDescriptor.сontributedMethods: List<FunctionDescriptor>
}
fun ClassDescriptor.isAbstract() = this.modality == Modality.SEALED || this.modality == Modality.ABSTRACT
|| this.kind == ClassKind.ENUM_CLASS
// TODO: optimize
val ClassDescriptor.vtableEntries: List<FunctionDescriptor>
@@ -183,18 +193,15 @@ val ClassDescriptor.vtableEntries: List<FunctionDescriptor>
this.getSuperClassOrAny().vtableEntries
}
val methods = this.сontributedMethods
val methods = this.contributedMethods
val inheritedVtableSlots = superVtableEntries.map { superMethod ->
methods.single { OverridingUtil.overrides(it, superMethod) }
methods.singleOrNull { OverridingUtil.overrides(it, superMethod) } ?: superMethod
}
return inheritedVtableSlots + (methods - inheritedVtableSlots).filter { it.isOverridable }
}
val ClassDescriptor.vtableSize: Int
get() = if (this.isAbstract()) 0 else this.vtableEntries.size
fun ClassDescriptor.vtableIndex(function: FunctionDescriptor): Int {
this.vtableEntries.forEachIndexed { index, functionDescriptor ->
if (functionDescriptor == function.original) return index
@@ -204,8 +211,11 @@ fun ClassDescriptor.vtableIndex(function: FunctionDescriptor): Int {
val ClassDescriptor.methodTableEntries: List<FunctionDescriptor>
get() {
assert (!this.isAbstract())
return this.сontributedMethods.filter { it.isOverridableOrOverrides }
assert(!this.isAbstract())
return this.contributedMethods.filter {
// We check that either method is open, or one of declarations it overrides
// is open.
it.isOverridable || DescriptorUtils.getAllOverriddenDeclarations(it).any { it.isOverridable }
}
// TODO: probably method table should contain all accessible methods to improve binary compatibility
}
@@ -37,5 +37,7 @@ val PropertyDescriptor.backingField: PropertyDescriptor?
}
fun DeclarationDescriptor.deepPrint() {
this!!.accept(DeepPrintVisitor(PrintVisitor()), 0)
this.accept(DeepPrintVisitor(PrintVisitor()), 0)
}
internal val String.synthesizedName get() = Name.identifier("\$" + this)
@@ -1,6 +1,8 @@
package org.jetbrains.kotlin.backend.konan.ir
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.lower.DefaultParameterDescription
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
@@ -8,5 +10,9 @@ import org.jetbrains.kotlin.descriptors.PropertyDescriptor
internal class Ir(val context: Context, val irModule: IrModuleFragment) {
val propertiesWithBackingFields = mutableSetOf<PropertyDescriptor>()
val moduleIndex = ModuleIndex(irModule)
val defaultParameterDescriptions = mutableMapOf<FunctionDescriptor, DefaultParameterDescription>()
val originalModuleIndex = ModuleIndex(irModule)
lateinit var moduleIndexForCodegen: ModuleIndex
}
@@ -1,5 +1,6 @@
package org.jetbrains.kotlin.backend.konan.ir
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrClass
@@ -8,15 +9,13 @@ import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
class ModuleIndex(val module: IrModuleFragment) {
/**
* Contains all classes declared in [module]
*/
val classes: Map<ClassId, IrClass>
val classes: Map<ClassDescriptor, IrClass>
/**
* Contains all functions declared in [module]
@@ -24,7 +23,7 @@ class ModuleIndex(val module: IrModuleFragment) {
val functions = mutableMapOf<FunctionDescriptor, IrFunction>()
init {
val map = mutableMapOf<ClassId, IrClass>()
val map = mutableMapOf<ClassDescriptor, IrClass>()
module.acceptVoid(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
@@ -34,10 +33,7 @@ class ModuleIndex(val module: IrModuleFragment) {
override fun visitClass(declaration: IrClass) {
super.visitClass(declaration)
val classId = declaration.descriptor.classId
if (classId != null) {
map[classId] = declaration
}
map[declaration.descriptor] = declaration
}
override fun visitFunction(declaration: IrFunction) {
@@ -0,0 +1,160 @@
package org.jetbrains.kotlin.backend.konan.llvm
import llvm.LLVMTypeRef
import org.jetbrains.kotlin.backend.konan.descriptors.allValueParameters
import org.jetbrains.kotlin.backend.konan.descriptors.isUnit
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.constants.StringValue
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.findOriginalTopMostOverriddenDescriptors
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
// This file describes the ABI for Kotlin descriptors of exported declarations.
// TODO: revise the naming scheme to ensure it produces unique names.
// TODO: do not serialize descriptors of non-exported declarations.
/**
* Defines whether the declaration is exported, i.e. visible from other modules.
*
* Exported declarations must have predictable and stable ABI
* that doesn't depend on any internal transformations (e.g. IR lowering),
* and so should be computable from the descriptor itself without checking a backend state.
*/
internal tailrec fun DeclarationDescriptor.isExported(): Boolean {
if (this.annotations.findAnnotation(symbolNameAnnotation) != null) {
// Treat any `@SymbolName` declaration as exported.
return true
}
if (this.annotations.findAnnotation(exportForCppRuntimeAnnotation) != null) {
// Treat any `@ExportForCppRuntime` declaration as exported.
return true
}
if (this.annotations.hasAnnotation(exportForCompilerAnnotation)){
return true
}
if (this is ConstructorDescriptor && constructedClass.kind.isSingleton) {
// Currently code generator can access the constructor of the singleton,
// so ignore visibility of the constructor itself.
return constructedClass.isExported()
}
if (this is DeclarationDescriptorWithVisibility && !this.visibility.isPublicAPI) {
// If the declaration is explicitly marked as non-public,
// then it must not be accessible from other modules.
return false
}
if (this is DeclarationDescriptorNonRoot) {
// If the declaration is not root, then check its container too.
return containingDeclaration.isExported()
}
return true
}
private val symbolNameAnnotation = FqName("konan.SymbolName")
private val exportForCppRuntimeAnnotation = FqName("konan.internal.ExportForCppRuntime")
private val exportForCompilerAnnotation = FqName("konan.internal.ExportForCompiler")
private fun typeToHashString(type: KotlinType): String {
if (TypeUtils.isTypeParameter(type)) return "GENERIC"
var hashString = TypeUtils.getClassDescriptor(type)!!.fqNameSafe.asString()
if (!type.arguments.isEmpty()) {
hashString += "<${type.arguments.map {
typeToHashString(it.type)
}.joinToString(",")}>"
}
if (type.isMarkedNullable) hashString += "?"
return hashString
}
private val FunctionDescriptor.signature: String
get() {
val extensionReceiverPart = this.extensionReceiverParameter?.let { "${it.type}." } ?: ""
val actualDescriptor = this.findOriginalTopMostOverriddenDescriptors().firstOrNull() ?: this
val argsPart = actualDescriptor.valueParameters.map {
typeToHashString(it.type)
}.joinToString(";")
// TODO: add return type
// (it is not simple because return type can be changed when overriding)
return "$extensionReceiverPart($argsPart)"
}
// TODO: rename to indicate that it has signature included
internal val FunctionDescriptor.functionName: String
get() = with(this.original) { // basic support for generics
"$name$signature"
}
internal val FunctionDescriptor.symbolName: String
get() {
if (!this.isExported()) {
throw AssertionError(this.toString())
}
this.annotations.findAnnotation(symbolNameAnnotation)?.let {
if (this.isExternal) {
return getStringValue(it)!!
} else {
// ignore; TODO: report compile error
}
}
this.annotations.findAnnotation(exportForCppRuntimeAnnotation)?.let {
val name = getStringValue(it) ?: this.name.asString()
return name // no wrapping currently required
}
val containingDeclarationPart = containingDeclaration.fqNameSafe.let {
if (it.isRoot) "" else "$it."
}
return "kfun:$containingDeclarationPart$functionName"
}
private fun getStringValue(annotation: AnnotationDescriptor): String? {
annotation.allValueArguments.values.ifNotEmpty {
val stringValue = this.single() as StringValue
return stringValue.value
}
return null
}
// TODO: bring here dependencies of this method?
internal fun RuntimeAware.getLlvmFunctionType(function: FunctionDescriptor): LLVMTypeRef {
val original = function.original
val returnType = if (original is ConstructorDescriptor) voidType else getLLVMReturnType(original.returnType!!)
val paramTypes = ArrayList(original.allValueParameters.map { getLLVMType(it.type) })
if (isObjectType(returnType)) paramTypes.add(kObjHeaderPtrPtr)
return functionType(returnType, isVarArg = false, paramTypes = *paramTypes.toTypedArray())
}
internal val ClassDescriptor.typeInfoSymbolName: String
get() {
assert (this.isExported())
return "ktype:" + this.fqNameSafe.toString()
}
internal val theUnitInstanceName = "kobj:kotlin.Unit"
internal val ClassDescriptor.objectInstanceFieldSymbolName: String
get() {
assert (this.isExported())
assert (this.kind.isSingleton)
assert (!this.isUnit())
return "kobjref:$fqNameSafe"
}
@@ -17,14 +17,27 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
// TODO: remove, to make CodeGenerator descriptor-agnostic.
var constructedClass: ClassDescriptor? = null
val vars = VariableManager(this)
var returnSlot: LLVMValueRef? = null
var slotsPhi: LLVMValueRef? = null
var slotCount = 0
var functionDescriptor: FunctionDescriptor? = null
private var returnSlot: LLVMValueRef? = null
private var slotsPhi: LLVMValueRef? = null
private var slotCount = 0
private var localAllocs = 0
private var arenaSlot: LLVMValueRef? = null
private val intPtrType = LLVMIntPtrType(llvmTargetData)!!
private val immOneIntPtrType = LLVMConstInt(intPtrType, 1, 1)!!
fun prologue(descriptor: FunctionDescriptor) {
prologue(llvmFunction(descriptor),
val llvmFunction = llvmFunction(descriptor)
prologue(llvmFunction,
LLVMGetReturnType(getLlvmFunctionType(descriptor))!!)
if (!descriptor.isExported()) {
LLVMSetLinkage(llvmFunction, LLVMLinkage.LLVMInternalLinkage)
// (Cannot do this before the function body is created).
}
if (descriptor is ConstructorDescriptor) {
constructedClass = descriptor.constructedClass
}
@@ -47,16 +60,21 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
cleanupLandingpad = LLVMAppendBasicBlock(function, "cleanup_landingpad")!!
positionAtEnd(entryBb!!)
slotsPhi = phi(kObjHeaderPtrPtr)
slotCount = 0
// First slot can be assigned to keep pointer to frame local arena.
slotCount = 1
localAllocs = 0
// Is removed by DCE trivially, if not needed.
arenaSlot = intToPtr(
or(ptrToInt(slotsPhi, intPtrType), immOneIntPtrType), kObjHeaderPtrPtr)
}
fun epilogue() {
appendingTo(prologueBb!!) {
val slots = if (slotCount > 0)
val slots = if (needSlots)
LLVMBuildArrayAlloca(builder, kObjHeaderPtr, Int32(slotCount).llvm, "")!!
else
kNullObjHeaderPtrPtr
if (slotCount > 0) {
if (needSlots) {
// Zero-init slots.
val slotsMem = bitcast(kInt8Ptr, slots)
val pointerSize = LLVMABISizeOfType(llvmTargetData, kObjHeaderPtr).toInt()
@@ -81,7 +99,7 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
val returnPhi = phi(returnType!!)
addPhiIncoming(returnPhi, *returns.toList().toTypedArray())
if (returnSlot != null) {
updateLocalRef(returnPhi, returnSlot!!)
updateReturnRef(returnPhi, returnSlot!!)
}
releaseVars()
LLVMBuildRet(builder, returnPhi)
@@ -104,9 +122,14 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
slotsPhi = null
}
fun releaseVars() {
if (slotCount > 0) {
call(context.llvm.releaseLocalRefsFunction,
private val needSlots: Boolean
get() {
return slotCount > 1 || localAllocs > 0
}
private fun releaseVars() {
if (needSlots) {
call(context.llvm.leaveFrameFunction,
listOf(slotsPhi!!, Int32(slotCount).llvm))
}
}
@@ -125,6 +148,8 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
fun div (arg0: LLVMValueRef, arg1: LLVMValueRef, name: String = ""): LLVMValueRef = LLVMBuildSDiv(builder, arg0, arg1, name)!!
fun srem (arg0: LLVMValueRef, arg1: LLVMValueRef, name: String = ""): LLVMValueRef = LLVMBuildSRem(builder, arg0, arg1, name)!!
fun or (arg0: LLVMValueRef, arg1: LLVMValueRef, name: String = ""): LLVMValueRef = LLVMBuildOr (builder, arg0, arg1, name)!!
/* integers comparisons */
fun icmpEq(arg0: LLVMValueRef, arg1: LLVMValueRef, name: String = ""): LLVMValueRef = LLVMBuildICmp(builder, LLVMIntPredicate.LLVMIntEQ, arg0, arg1, name)!!
fun icmpGt(arg0: LLVMValueRef, arg1: LLVMValueRef, name: String = ""): LLVMValueRef = LLVMBuildICmp(builder, LLVMIntPredicate.LLVMIntSGT, arg0, arg1, name)!!
@@ -140,7 +165,8 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
fun bitcast(type: LLVMTypeRef?, value: LLVMValueRef, name: String = "") = LLVMBuildBitCast(builder, value, type, name)!!
fun intToPtr(imm: LLVMValueRef?, DestTy: LLVMTypeRef, Name: String = "") = LLVMBuildIntToPtr(builder, imm, DestTy, Name)!!
fun intToPtr(value: LLVMValueRef?, DestTy: LLVMTypeRef, Name: String = "") = LLVMBuildIntToPtr(builder, value, DestTy, Name)!!
fun ptrToInt(value: LLVMValueRef?, DestTy: LLVMTypeRef, Name: String = "") = LLVMBuildPtrToInt(builder, value, DestTy, Name)!!
fun alloca(type: LLVMTypeRef?, name: String = ""): LLVMValueRef {
if (isObjectType(type!!)) {
@@ -150,6 +176,16 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
return LLVMBuildAlloca(builder, type, name)!!
}
}
fun allocInstance(typeInfo: LLVMValueRef, lifetime: Lifetime) : LLVMValueRef {
return call(context.llvm.allocInstanceFunction, listOf(typeInfo), lifetime)
}
fun allocArray(
typeInfo: LLVMValueRef, count: LLVMValueRef, lifetime: Lifetime) : LLVMValueRef {
return call(context.llvm.allocArrayFunction, listOf(typeInfo, count), lifetime)
}
fun load(value: LLVMValueRef, name: String = ""): LLVMValueRef {
val result = LLVMBuildLoad(builder, value, name)!!
// Use loadSlot() API for that.
@@ -191,6 +227,10 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
}
}
fun updateReturnRef(value: LLVMValueRef, address: LLVMValueRef) {
call(context.llvm.updateReturnRefFunction, listOf(address, value))
}
// Only use ignoreOld, when sure that memory is freshly inited and have no value.
fun updateLocalRef(value: LLVMValueRef, address: LLVMValueRef, ignoreOld: Boolean = false) {
call(if (ignoreOld) context.llvm.setLocalRefFunction else context.llvm.updateLocalRefFunction,
@@ -206,19 +246,44 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
//-------------------------------------------------------------------------//
fun callAtFunctionScope(llvmFunction: LLVMValueRef, args: List<LLVMValueRef>, name: String = "") =
call(llvmFunction, args, this::cleanupLandingpad, name)
fun callAtFunctionScope(llvmFunction: LLVMValueRef, args: List<LLVMValueRef>,
lifetime: Lifetime) =
call(llvmFunction, args, lifetime, this::cleanupLandingpad)
fun call(llvmFunction: LLVMValueRef, args: List<LLVMValueRef>,
lazyLandingpad: () -> LLVMBasicBlockRef? = { null }, name: String = ""): LLVMValueRef {
resultLifetime: Lifetime = Lifetime.IRRELEVANT,
lazyLandingpad: () -> LLVMBasicBlockRef? = { null }): LLVMValueRef {
var callArgs = if (isObjectReturn(llvmFunction.type)) {
// If function returns an object - create slot for the returned value or give local arena.
// This allows appropriate rootset accounting by just looking at the stack slots,
// along with ability to allocate in appropriate arena.
val resultSlot = when (resultLifetime.slotType) {
SlotType.ARENA -> {
localAllocs++
arenaSlot!!
}
SlotType.RETURN -> returnSlot!!
// TODO: for RETURN_IF_ARENA choose between created slot and arenaSlot
// dynamically.
SlotType.ANONYMOUS, SlotType.RETURN_IF_ARENA -> vars.createAnonymousSlot()
else -> throw Error("Incorrect slot type")
}
args + resultSlot
} else {
args
}
return callRaw(llvmFunction, callArgs, lazyLandingpad)
}
private fun callRaw(llvmFunction: LLVMValueRef, args: List<LLVMValueRef>,
lazyLandingpad: () -> LLVMBasicBlockRef?): LLVMValueRef {
memScoped {
val rargs = allocArrayOf(args)[0].ptr
if (LLVMIsAFunction(llvmFunction) != null /* the function declaration */ &&
LLVMAttribute.LLVMNoUnwindAttribute in LLVMGetFunctionAttrSet(llvmFunction)) {
return LLVMBuildCall(builder, llvmFunction, rargs, args.size, name)!!
return LLVMBuildCall(builder, llvmFunction, rargs, args.size, "")!!
} else {
val landingpad = lazyLandingpad()
@@ -232,7 +297,7 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
}
val success = basicBlock()
val result = LLVMBuildInvoke(builder, llvmFunction, rargs, args.size, success, landingpad, name)!!
val result = LLVMBuildInvoke(builder, llvmFunction, rargs, args.size, success, landingpad, "")!!
positionAtEnd(success)
return result
}
@@ -264,7 +329,6 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
//-------------------------------------------------------------------------//
/* to class descriptor */
fun classType(descriptor: ClassDescriptor): LLVMTypeRef = LLVMGetTypeByName(context.llvmModule, descriptor.symbolName)!!
fun typeInfoValue(descriptor: ClassDescriptor): LLVMValueRef = descriptor.llvmTypeInfoPtr
/**
@@ -278,9 +342,6 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
}
fun countParams(fn: FunctionDescriptor) = LLVMCountParams(fn.llvmFunction)
fun indexInClass(p:PropertyDescriptor):Int = (p.containingDeclaration as ClassDescriptor).fields.indexOf(p)
fun basicBlock(name: String = "label_"): LLVMBasicBlockRef {
val currentBlock = this.currentBlock
val result = LLVMInsertBasicBlock(currentBlock, name)!!
@@ -3,32 +3,61 @@ package org.jetbrains.kotlin.backend.konan.llvm
import kotlinx.cinterop.*
import llvm.*
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.Distribution
import org.jetbrains.kotlin.backend.konan.hash.GlobalHash
import org.jetbrains.kotlin.backend.konan.KonanConfigKeys
import org.jetbrains.kotlin.backend.konan.descriptors.backingField
import org.jetbrains.kotlin.backend.konan.descriptors.vtableSize
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
internal enum class SlotType {
// Frame local arena slot can be used.
ARENA,
// Return slot can be used.
RETURN,
// Return slot, if it is an arena, can be used.
RETURN_IF_ARENA,
// Anonymous slot.
ANONYMOUS,
// Unknown slot type.
UNKNOWN
}
// Lifetimes class of reference, computed by escape analysis.
internal enum class Lifetime(val slotType: SlotType) {
// If reference is frame-local (only obtained from some call and never leaves).
LOCAL(SlotType.ARENA),
// If reference is only returned.
RETURN_VALUE(SlotType.RETURN),
// If reference is set as field of references of class RETURN_VALUE or INDIRECT_RETURN_VALUE.
INDIRECT_RETURN_VALUE(SlotType.RETURN_IF_ARENA),
// If reference is stored to the field of an incoming parameters.
PARAMETER_FIELD(SlotType.ANONYMOUS),
// If reference refers to the global (either global object or global variable).
GLOBAL(SlotType.ANONYMOUS),
// If reference used to throw.
THROW(SlotType.ANONYMOUS),
// If reference used as an argument of outgoing function. Class can be improved by escape analysis
// of called function.
ARGUMENT(SlotType.ANONYMOUS),
// If reference class is unknown.
UNKNOWN(SlotType.UNKNOWN),
// If reference class is irrelevant.
IRRELEVANT(SlotType.UNKNOWN)
}
/**
* Provides utility methods to the implementer.
*/
internal interface ContextUtils {
internal interface ContextUtils : RuntimeAware {
val context: Context
val runtime: Runtime
override val runtime: Runtime
get() = context.llvm.runtime
/**
@@ -42,53 +71,7 @@ internal interface ContextUtils {
val staticData: StaticData
get() = context.llvm.staticData
/**
* All fields of the class instance.
* The order respects the class hierarchy, i.e. a class [fields] contains superclass [fields] as a prefix.
*/
val ClassDescriptor.fields: List<PropertyDescriptor>
get() {
val superClass = this.getSuperClassNotAny() // TODO: what if Any has fields?
val superFields = if (superClass != null) superClass.fields else emptyList()
return superFields + this.declaredFields
}
/**
* Fields declared in the class.
*/
val ClassDescriptor.declaredFields: List<PropertyDescriptor>
get() {
// TODO: Here's what is going on here:
// The existence of a backing field for a property is only described in the IR,
// but not in the property descriptor.
// That works, while we process the IR, but not for deserialized descriptors.
//
// So to have something in deserialized descriptors,
// while we still see the IR, we mark the property with an annotation.
//
// We could apply the annotation during IR rewite, but we still are not
// that far in the rewriting infrastructure. So we postpone
// the annotation until the serializer.
//
// In this function we check the presence of the backing filed
// two ways: first we check IR, then we check the annotation.
val irClass = context.ir.moduleIndex.classes[this.classId]
if (irClass != null) {
val irProperties = irClass.declarations
return irProperties.mapNotNull {
(it as? IrProperty)?.backingField?.descriptor
}
} else {
val properties = this.unsubstitutedMemberScope.
getContributedDescriptors().
filterIsInstance<PropertyDescriptor>()
return properties.mapNotNull{ it.backingField }
}
}
fun isExternal(descriptor: DeclarationDescriptor) = descriptor.module != context.ir.irModule.descriptor
/**
* LLVM function generated from the Kotlin function.
@@ -97,13 +80,15 @@ internal interface ContextUtils {
val FunctionDescriptor.llvmFunction: LLVMValueRef
get() {
assert (this.kind.isReal)
val globalName = this.symbolName
val module = context.llvmModule
if (this is TypeAliasConstructorDescriptor) {
return this.underlyingConstructorDescriptor.llvmFunction
}
val functionType = getLlvmFunctionType(this)
return LLVMGetNamedFunction(module, globalName) ?:
LLVMAddFunction(module, globalName, functionType)!!
return if (isExternal(this)) {
context.llvm.externalFunction(this.symbolName, getLlvmFunctionType(this))
} else {
context.llvmDeclarations.forFunction(this).llvmFunction
}
}
/**
@@ -115,17 +100,14 @@ internal interface ContextUtils {
return constValue(result)
}
/**
* Pointer to struct { TypeInfo, vtable }.
*/
val ClassDescriptor.typeInfoWithVtable: ConstPointer
get() {
val type = structType(runtime.typeInfoType, LLVMArrayType(int8TypePtr, this.vtableSize)!!)
return constPointer(externalGlobal(this.typeInfoSymbolName, type))
}
val ClassDescriptor.typeInfoPtr: ConstPointer
get() = typeInfoWithVtable.getElementPtr(0)
get() {
return if (isExternal(this)) {
constPointer(externalGlobal(this.typeInfoSymbolName, runtime.typeInfoType))
} else {
context.llvmDeclarations.forClass(this).typeInfo
}
}
/**
* Pointer to type info for given class.
@@ -223,13 +205,14 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
}
}
private fun externalFunction(name: String, type: LLVMTypeRef): LLVMValueRef {
val found = LLVMGetNamedFunction(context.llvmModule, name)
internal fun externalFunction(name: String, type: LLVMTypeRef): LLVMValueRef {
val found = LLVMGetNamedFunction(llvmModule, name)
if (found != null) {
assert (getFunctionType(found) == type)
assert (LLVMGetLinkage(found) == LLVMLinkage.LLVMExternalLinkage)
return found
} else {
return LLVMAddFunction(context.llvmModule, name, type)!!
return LLVMAddFunction(llvmModule, name, type)!!
}
}
@@ -241,7 +224,7 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
val staticData = StaticData(context)
val runtimeFile = context.config.configuration.get(KonanConfigKeys.RUNTIME_FILE)!!
val runtimeFile = context.config.distribution.runtime
val runtime = Runtime(runtimeFile) // TODO: dispose
init {
@@ -254,13 +237,14 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
var globalInitIndex:Int = 0
val allocInstanceFunction = importRtFunction("AllocInstance")
val initInstanceFunction = importRtFunction("InitInstance")
val allocArrayFunction = importRtFunction("AllocArrayInstance")
val initInstanceFunction = importRtFunction("InitInstance")
val updateReturnRefFunction = importRtFunction("UpdateReturnRef")
val setLocalRefFunction = importRtFunction("SetLocalRef")
val setGlobalRefFunction = importRtFunction("SetGlobalRef")
val updateLocalRefFunction = importRtFunction("UpdateLocalRef")
val updateGlobalRefFunction = importRtFunction("UpdateGlobalRef")
val releaseLocalRefsFunction = importRtFunction("ReleaseLocalRefs")
val leaveFrameFunction = importRtFunction("LeaveFrame")
val setArrayFunction = importRtFunction("Kotlin_Array_set")
val copyImplArrayFunction = importRtFunction("Kotlin_Array_copyImpl")
val lookupFieldOffset = importRtFunction("LookupFieldOffset")
@@ -6,7 +6,7 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.isUnit
internal fun ContextUtils.getLLVMType(type: KotlinType): LLVMTypeRef {
internal fun RuntimeAware.getLLVMType(type: KotlinType): LLVMTypeRef {
return when {
// Nullable types must be represented as objects for boxing.
type.isMarkedNullable -> this.kObjHeaderPtr
@@ -23,7 +23,7 @@ internal fun ContextUtils.getLLVMType(type: KotlinType): LLVMTypeRef {
}!!
}
internal fun ContextUtils.getLLVMReturnType(type: KotlinType): LLVMTypeRef {
internal fun RuntimeAware.getLLVMReturnType(type: KotlinType): LLVMTypeRef {
return when {
type.isUnit() -> LLVMVoidType()!!
// TODO: stdlib have methods taking Nothing, such as kotlin.collections.EmptySet.contains().
@@ -0,0 +1,38 @@
package org.jetbrains.kotlin.backend.konan.llvm
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid
// Analysis we're implementing here is as following.
// We build graph with the following nodes:
// * allocation set, keeping tuple of [local, ctor call, owner function], AS
// * local store set, keeping pair [local, stored], LSS
// * field store set, keeping tuple [local, object to store, stored field id], FSS
// * global store set, [local, stored global address], GSS
// Function we're trying to compute is the following:
// for each element of AS, could it be referred by someone, whose value is
// alive on return from function, where element was allocated.
// Each element in RS is associated with few elements in AS, which it could refer to.
//
internal class EscapeAnalyzerVisitor(
val lifetimes: MutableMap<IrMemberAccessExpression, Lifetime>) : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitModuleFragment(module: IrModuleFragment) {
module.acceptChildrenVoid(this)
}
}
internal fun computeLifetimes(irModule: IrModuleFragment,
lifetimes: MutableMap<IrMemberAccessExpression, Lifetime>) {
assert(lifetimes.size == 0)
irModule.acceptVoid(EscapeAnalyzerVisitor(lifetimes))
}
@@ -20,7 +20,6 @@ import org.jetbrains.kotlin.ir.visitors.acceptVoid
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
import org.jetbrains.kotlin.types.KotlinType
@@ -33,9 +32,16 @@ import org.jetbrains.kotlin.utils.addToStdlib.singletonList
internal fun emitLLVM(context: Context) {
val irModule = context.irModule!!
// Note that we don't set module target explicitly.
// It is determined by the target of runtime.bc
// (see Llvm class in ContextUtils)
// Which in turn is determined by the clang flags
// used to compile runtime.bc.
val llvmModule = LLVMModuleCreateWithName("out")!! // TODO: dispose
context.llvmModule = llvmModule
context.llvmDeclarations = createLlvmDeclarations(context)
val phaser = PhaseManager(context)
phaser.phase(KonanPhase.RTTI) {
@@ -78,11 +84,6 @@ internal class RTTIGeneratorVisitor(context: Context) : IrElementVisitorVoid {
override fun visitClass(declaration: IrClass) {
super.visitClass(declaration)
if (declaration.descriptor.kind == ClassKind.ANNOTATION_CLASS) {
// do not generate any RTTI for annotation classes as a workaround for link errors
return
}
if (declaration.descriptor.isIntrinsic) {
// do not generate any code for intrinsic classes as they require special handling
return
@@ -118,7 +119,7 @@ internal class MetadatorVisitor(val context: Context) : IrElementVisitorVoid {
/**
* Defines how to generate context-dependent operations.
*/
interface CodeContext {
internal interface CodeContext {
/**
* Generates `return` [value] operation.
@@ -131,7 +132,7 @@ interface CodeContext {
fun genContinue(destination: IrContinue)
fun genCall(function: LLVMValueRef, args: List<LLVMValueRef>): LLVMValueRef
fun genCall(function: LLVMValueRef, args: List<LLVMValueRef>, resultLifetime: Lifetime): LLVMValueRef
fun genThrow(exception: LLVMValueRef)
@@ -167,6 +168,7 @@ interface CodeContext {
internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid {
val codegen = CodeGenerator(context)
val resultLifetimes = mutableMapOf<IrMemberAccessExpression, Lifetime>()
//-------------------------------------------------------------------------//
@@ -188,7 +190,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
override fun genContinue(destination: IrContinue) = unsupported()
override fun genCall(function: LLVMValueRef, args: List<LLVMValueRef>) = unsupported(function)
override fun genCall(function: LLVMValueRef, args: List<LLVMValueRef>, resultLifetime: Lifetime) = unsupported(function)
override fun genThrow(exception: LLVMValueRef) = unsupported()
@@ -235,6 +237,8 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
override fun visitModuleFragment(module: IrModuleFragment) {
context.log("visitModule : ${ir2string(module)}")
computeLifetimes(module, resultLifetimes)
module.acceptChildrenVoid(this)
appendLlvmUsed(context.llvm.usedFunctions)
appendStaticInitializers(context.llvm.staticInitializers)
@@ -246,7 +250,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
val kNodeInitType = LLVMGetTypeByName(context.llvmModule, "struct.InitNode")!!
//-------------------------------------------------------------------------//
fun createInitBody(initName: String) {
fun createInitBody(initName: String): LLVMValueRef {
val initFunction = LLVMAddFunction(context.llvmModule, initName, kVoidFuncType)!! // create LLVM function
codegen.prologue(initFunction, voidType)
using(FunctionScope(initFunction)) {
@@ -254,33 +258,33 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
val irField = it as IrField
val descriptor = irField.descriptor
val initialization = evaluateExpression(irField.initializer!!.expression)
val globalPtr = LLVMGetNamedGlobal(context.llvmModule, descriptor.symbolName)
codegen.storeAnyGlobal(initialization, globalPtr!!)
val globalPtr = context.llvmDeclarations.forStaticField(descriptor).storage
codegen.storeAnyGlobal(initialization, globalPtr)
}
codegen.ret(null)
}
codegen.epilogue()
return initFunction
}
//-------------------------------------------------------------------------//
// Creates static struct InitNode $nodeName = {$initName, NULL};
fun createInitNode(initName: String, nodeName: String) {
fun createInitNode(initFunction: LLVMValueRef, nodeName: String): LLVMValueRef {
memScoped {
val initFunction = LLVMGetNamedFunction(context.llvmModule, initName)!! // Get initialization function.
val nextInitNode = LLVMConstNull(pointerType(kNodeInitType)) // Set InitNode.next = NULL.
val argList = allocArrayOf(initFunction, nextInitNode)[0].ptr // Allocate array of args.
val initNode = LLVMConstNamedStruct(kNodeInitType, argList, 2)!! // Create static object of class InitNode.
context.llvm.staticData.placeGlobal(nodeName, constPointer(initNode)).llvmGlobal // Put the object in global var with name "nodeName".
return context.llvm.staticData.placeGlobal(nodeName, constPointer(initNode)).llvmGlobal // Put the object in global var with name "nodeName".
}
}
//-------------------------------------------------------------------------//
fun createInitCtor(ctorName: String, nodeName: String) {
fun createInitCtor(ctorName: String, initNodePtr: LLVMValueRef) {
val ctorFunction = LLVMAddFunction(context.llvmModule, ctorName, kVoidFuncType)!! // Create constructor function.
codegen.prologue(ctorFunction, voidType)
val initNodePtr = LLVMGetNamedGlobal(context.llvmModule, nodeName)!! // Get LLVM function initializing globals of current file.
codegen.call(context.llvm.appendToInitalizersTail, initNodePtr.singletonList()) // Add node to the tail of initializers list.
codegen.ret(null)
codegen.epilogue()
@@ -305,9 +309,9 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
val nodeName = "${fileName}_node_${context.llvm.globalInitIndex}"
val ctorName = "${fileName}_ctor_${context.llvm.globalInitIndex++}"
createInitBody(initName)
createInitNode(initName, nodeName)
createInitCtor(ctorName, nodeName)
val initFunction = createInitBody(initName)
val initNode = createInitNode(initFunction, nodeName)
createInitCtor(ctorName, initNode)
}
//-------------------------------------------------------------------------//
@@ -380,15 +384,13 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
if (constructorDescriptor.isPrimary) {
if (DescriptorUtils.isObject(classDescriptor)) {
if (classDescriptor.isUnit()) {
context.llvm.staticData.createUnitInstance(classDescriptor)
} else {
if (!classDescriptor.isUnit()) {
val objectPtr = objectPtrByName(classDescriptor)
LLVMSetInitializer(objectPtr, codegen.kNullObjHeaderPtr)
}
}
val irOfCurrentClass = context.ir.moduleIndex.classes[classDescriptor.classId]
val irOfCurrentClass = context.ir.moduleIndexForCodegen.classes[classDescriptor]
irOfCurrentClass!!.acceptChildrenVoid(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
@@ -527,14 +529,14 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
}
}
override fun genCall(function: LLVMValueRef, args: List<LLVMValueRef>) =
codegen.callAtFunctionScope(function, args)
override fun genCall(function: LLVMValueRef, args: List<LLVMValueRef>, resultLifetime: Lifetime) =
codegen.callAtFunctionScope(function, args, resultLifetime)
override fun genThrow(exception: LLVMValueRef) {
val objHeaderPtr = codegen.bitcast(codegen.kObjHeaderPtr, exception)
val args = listOf(objHeaderPtr)
this.genCall(context.llvm.throwExceptionFunction, args)
this.genCall(context.llvm.throwExceptionFunction, args, Lifetime.IRRELEVANT)
codegen.unreachable()
}
@@ -587,6 +589,12 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
//-------------------------------------------------------------------------//
override fun visitTypeAlias(declaration: IrTypeAlias) {
// Nothing to do.
}
//-------------------------------------------------------------------------//
override fun visitProperty(declaration: IrProperty) {
declaration.acceptChildrenVoid(this)
}
@@ -597,13 +605,18 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
context.log("visitField : ${ir2string(expression)}")
val descriptor = expression.descriptor
if (descriptor.containingDeclaration is PackageFragmentDescriptor) {
val globalProperty = LLVMAddGlobal(context.llvmModule, codegen.getLLVMType(descriptor.type), descriptor.symbolName)
val type = codegen.getLLVMType(descriptor.type)
val globalProperty = context.llvmDeclarations.forStaticField(descriptor).storage
if (expression.initializer!!.expression is IrConst<*>) {
LLVMSetInitializer(globalProperty, evaluateExpression(expression.initializer!!.expression))
} else {
LLVMSetInitializer(globalProperty, codegen.kNullObjHeaderPtr)
LLVMSetInitializer(globalProperty, LLVMConstNull(type))
context.llvm.fileInitializers.add(expression)
}
LLVMSetLinkage(globalProperty, LLVMLinkage.LLVMInternalLinkage)
// (Cannot do this before the global is initialized).
return
}
}
@@ -679,11 +692,10 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
codegen.positionAtEnd(bbInit)
val typeInfo = codegen.typeInfoValue(value.descriptor)
val allocHint = Int32(1).llvm
val initFunction = value.descriptor.constructors.first { it.valueParameters.size == 0 }
val ctor = codegen.llvmFunction(initFunction)
val args = listOf(objectPtr, typeInfo, allocHint, ctor)
val newValue = call(context.llvm.initInstanceFunction, args)
val args = listOf(objectPtr, typeInfo, ctor)
val newValue = call(context.llvm.initInstanceFunction, args, Lifetime.GLOBAL)
val bbInitResult = codegen.currentBlock
codegen.br(bbExit)
@@ -790,9 +802,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
codegen.plus(sum, size!!)
}
val typeInfo = codegen.typeInfoValue(value.type)!!
val arrayCreationArgs = listOf(typeInfo, kImmInt32One, finalLength)
val array = call(context.llvm.allocArrayFunction, arrayCreationArgs)
val array = codegen.allocArray(codegen.typeInfoValue(value.type)!!, finalLength, Lifetime.GLOBAL)
elements.fold(kImmZero) { sum, (exp, size, isArray) ->
if (!isArray) {
call(context.llvm.setArrayFunction, listOf(array, sum, exp))
@@ -820,6 +830,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
val kStringLength = KonanPlatform.builtIns.string.getter2Descriptor(kNameLength)
val kStringBuilderToString = kStringBuilder.signature2Descriptor(kNameToString)
//TODO: make it lowering pass.
private fun evaluateStringConcatenation(value: IrStringConcatenation): LLVMValueRef {
data class Element(val string: LLVMValueRef, val llvmLenght: LLVMValueRef?, val length: Int)
@@ -830,8 +841,10 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
return@map Element(codegen.staticData.kotlinStringLiteral(it as IrConst<String>).llvm, null, string.length)
} else {
val toStringDescriptor = getToString(it.type)
val string = if (KotlinBuiltIns.isString(it.type)) evaluationResult
else evaluateSimpleFunctionCall(toStringDescriptor, listOf(evaluationResult))
val string =
if (KotlinBuiltIns.isString(it.type)) evaluationResult
else evaluateSimpleFunctionCall(
toStringDescriptor, listOf(evaluationResult), Lifetime.LOCAL)
val length = call(codegen.llvmFunction(kStringLength!!), listOf(string))
return@map Element(string, length, -1)
}
@@ -845,8 +858,8 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
val constructor = kStringBuilder!!.constructors
.firstOrNull { it -> it.valueParameters.size == 1 && KotlinBuiltIns.isInt(it.valueParameters[0].type) }!!
val stringBuilderObj = call(context.llvm.allocInstanceFunction,
listOf(codegen.typeInfoValue(kStringBuilder.defaultType)!!, kImmOne))
val stringBuilderObj = codegen.allocInstance(codegen.typeInfoValue(kStringBuilder), Lifetime.LOCAL)
call(codegen.llvmFunction(constructor), listOf(stringBuilderObj, totalLength))
stringsWithLengths.fold(stringBuilderObj) { sum, (string, _, _) ->
@@ -854,7 +867,8 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
return@fold sum
}
return evaluateSimpleFunctionCall(kStringBuilderToString!!, listOf(stringBuilderObj))
return evaluateSimpleFunctionCall(kStringBuilderToString!!, listOf(stringBuilderObj),
Lifetime.GLOBAL /* TODO: fix */)
}
//-------------------------------------------------------------------------//
@@ -946,8 +960,8 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
}
// The call inside [CatchingScope] must be configured to dispatch exception to the scope's handler.
override fun genCall(function: LLVMValueRef, args: List<LLVMValueRef>): LLVMValueRef {
val res = codegen.call(function, args, this::landingpad)
override fun genCall(function: LLVMValueRef, args: List<LLVMValueRef>, lifetime: Lifetime): LLVMValueRef {
val res = codegen.call(function, args, lifetime, this::landingpad)
return res
}
@@ -1144,9 +1158,13 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
if (!isNothing) // If "when" has "exit".
bbExit = codegen.basicBlock() // Create basic block to process "exit".
val resultPhi = if (isUnit || isNothing) null else
val hasNoValue = !isUnconditional(expression.branches.last())
// (It is possible if IrWhen is used as statement).
val llvmType = codegen.getLLVMType(expression.type)
val resultPhi = if (isUnit || isNothing || hasNoValue) null else
codegen.appendingTo(bbExit!!) {
codegen.phi(codegen.getLLVMType(expression.type))
codegen.phi(llvmType)
}
expression.branches.forEach { // Iterate through "when" branches (clauses).
@@ -1160,6 +1178,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
// FIXME: remove the hacks.
isUnit -> codegen.theUnitInstanceRef.llvm
isNothing -> codegen.kNothingFakeValue
hasNoValue -> LLVMGetUndef(llvmType)!!
else -> resultPhi!!
}
}
@@ -1279,7 +1298,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
val srcArg = evaluateExpression(value.argument) // Evaluate src expression.
val srcObjInfoPtr = codegen.bitcast(codegen.kObjHeaderPtr, srcArg) // Cast src to ObjInfoPtr.
val args = listOf(srcObjInfoPtr, dstTypeInfo) // Create arg list.
call(context.llvm.checkInstanceFunction, args) // Check if dst is subclass of src.
call(context.llvm.checkInstanceFunction, args) // Check if dst is subclass of src.
return srcArg
}
@@ -1317,6 +1336,8 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
private fun genInstanceOf(obj: LLVMValueRef, type: KotlinType): LLVMValueRef {
val dstDescriptor = TypeUtils.getClassDescriptor(type) // Get class descriptor for dst type.
// Reified parameters are not yet supported.
assert(dstDescriptor != null)
val dstTypeInfo = codegen.typeInfoValue(dstDescriptor!!) // Get TypeInfo for dst type.
val srcObjInfoPtr = codegen.bitcast(codegen.kObjHeaderPtr, obj) // Cast src to ObjInfoPtr.
val args = listOf(srcObjInfoPtr, dstTypeInfo) // Create arg list.
@@ -1343,7 +1364,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
}
else {
assert (value.receiver == null)
val ptr = LLVMGetNamedGlobal(context.llvmModule, value.descriptor.symbolName)!!
val ptr = context.llvmDeclarations.forStaticField(value.descriptor).storage
return codegen.loadSlot(ptr, value.descriptor.isVar())
}
}
@@ -1352,13 +1373,12 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
private fun objectPtrByName(descriptor: ClassDescriptor): LLVMValueRef {
assert (!descriptor.isUnit())
val objName = descriptor.fqNameSafe.asString()
var objectPtr = LLVMGetNamedGlobal(context.llvmModule, objName)
if (objectPtr == null) {
return if (codegen.isExternal(descriptor)) {
val llvmType = codegen.getLLVMType(descriptor.defaultType)
objectPtr = LLVMAddGlobal(context.llvmModule, llvmType, objName)
codegen.externalGlobal(descriptor.objectInstanceFieldSymbolName, llvmType)
} else {
context.llvmDeclarations.forSingleton(descriptor).instanceFieldRef
}
return objectPtr!!
}
//-------------------------------------------------------------------------//
@@ -1372,8 +1392,8 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
}
else {
assert (value.receiver == null)
val globalValue = LLVMGetNamedGlobal(context.llvmModule, value.descriptor.symbolName)
codegen.storeAnyGlobal(valueToAssign, globalValue!!)
val globalValue = context.llvmDeclarations.forStaticField(value.descriptor).storage
codegen.storeAnyGlobal(valueToAssign, globalValue)
}
assert (value.type.isUnit())
@@ -1415,13 +1435,14 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
*/
private fun fieldPtrOfClass(thisPtr: LLVMValueRef, value: PropertyDescriptor): LLVMValueRef {
val objHeaderPtr = codegen.bitcast(codegen.kObjHeaderPtr, thisPtr)
val typePtr = pointerType(codegen.classType(value.containingDeclaration as ClassDescriptor))
val fieldInfo = context.llvmDeclarations.forField(value)
val typePtr = pointerType(fieldInfo.classBodyType)
memScoped {
val args = allocArrayOf(kImmOne)
val objectPtr = LLVMBuildGEP(codegen.builder, objHeaderPtr, args[0].ptr, 1, "")
val objectPtr = LLVMBuildGEP(codegen.builder, thisPtr, args[0].ptr, 1, "")
val typedObjPtr = codegen.bitcast(typePtr, objectPtr!!)
val fieldPtr = LLVMBuildStructGEP(codegen.builder, typedObjPtr, codegen.indexInClass(value), "")
val fieldPtr = LLVMBuildStructGEP(codegen.builder, typedObjPtr, fieldInfo.index, "")
return fieldPtr!!
}
}
@@ -1619,7 +1640,8 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
value is IrDelegatingConstructorCall ->
return delegatingConstructorCall(value.descriptor, args)
value.descriptor is FunctionDescriptor -> return evaluateFunctionCall(value as IrCall, args)
value.descriptor is FunctionDescriptor -> return evaluateFunctionCall(
value as IrCall, args, resultLifetime(value))
else -> {
TODO("${ir2string(value)}")
}
@@ -1654,11 +1676,12 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
//-------------------------------------------------------------------------//
private fun evaluateFunctionCall(callee: IrCall, args: List<LLVMValueRef>): LLVMValueRef {
private fun evaluateFunctionCall(callee: IrCall, args: List<LLVMValueRef>,
resultLifetime: Lifetime): LLVMValueRef {
val descriptor:FunctionDescriptor = callee.descriptor as FunctionDescriptor
if (descriptor.isFunctionInvoke) {
return evaluateFunctionInvoke(descriptor, args)
return evaluateFunctionInvoke(descriptor, args, resultLifetime)
}
if (descriptor.isIntrinsic) {
@@ -1667,8 +1690,9 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
when (descriptor) {
is IrBuiltinOperatorDescriptorBase -> return evaluateOperatorCall (callee, args)
is ClassConstructorDescriptor -> return evaluateConstructorCall (callee, args)
else -> return evaluateSimpleFunctionCall(descriptor, args)
is ConstructorDescriptor -> return evaluateConstructorCall (callee, args)
else -> return evaluateSimpleFunctionCall(
descriptor, args, resultLifetime, callee.superQualifier)
}
}
@@ -1683,7 +1707,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
}
private fun evaluateFunctionInvoke(descriptor: FunctionDescriptor,
args: List<LLVMValueRef>): LLVMValueRef {
args: List<LLVMValueRef>, resultLifetime: Lifetime): LLVMValueRef {
// Note: the whole function code below is written in the assumption that
// `invoke` method receiver is passed as first argument.
@@ -1698,22 +1722,24 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
// Get `functionImpl.unboundRef`:
val unboundRef = evaluateSimpleFunctionCall(functionImplUnboundRefGetter,
listOf(functionImpl))
listOf(functionImpl), Lifetime.IRRELEVANT /* unboundRef isn't managed reference */)
// Cast `functionImpl.unboundRef` to pointer to function:
val entryPtr = codegen.bitcast(pointerType(unboundRefType), unboundRef, "entry")
return call(descriptor, entryPtr, args)
return call(descriptor, entryPtr, args, resultLifetime)
}
//-------------------------------------------------------------------------//
private fun evaluateSimpleFunctionCall(descriptor: FunctionDescriptor, args: List<LLVMValueRef>): LLVMValueRef {
private fun evaluateSimpleFunctionCall(
descriptor: FunctionDescriptor, args: List<LLVMValueRef>,
resultLifetime: Lifetime, superClass: ClassDescriptor? = null): LLVMValueRef {
//context.log("evaluateSimpleFunctionCall : $tmpVariableName = ${ir2string(value)}")
if (descriptor.isOverridable)
return callVirtual(descriptor, args)
if (descriptor.isOverridable && superClass == null)
return callVirtual(descriptor, args, resultLifetime)
else
return callDirect(descriptor, args)
return callDirect(descriptor, args, resultLifetime)
}
//-------------------------------------------------------------------------//
@@ -1724,28 +1750,28 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
if (descriptor.dispatchReceiverParameter != null)
args.add(evaluateExpression(value.dispatchReceiver!!)) //add this ptr
args.add(evaluateExpression(value.getValueArgument(0)!!))
return evaluateSimpleFunctionCall(descriptor, args)
return evaluateSimpleFunctionCall(
descriptor, args, Lifetime.IRRELEVANT, value.superQualifier)
}
//-------------------------------------------------------------------------//
private fun resultLifetime(callee: IrMemberAccessExpression): Lifetime {
return resultLifetimes.getOrElse(callee) { Lifetime.GLOBAL }
}
private fun evaluateConstructorCall(callee: IrCall, args: List<LLVMValueRef>): LLVMValueRef {
context.log("evaluateConstructorCall : ${ir2string(callee)}")
memScoped {
val containingClass = (callee.descriptor as ClassConstructorDescriptor).containingDeclaration
val typeInfo = codegen.typeInfoValue(containingClass)
val allocHint = Int32(1).llvm
val thisValue = if (containingClass.isArray) {
val constructedClass = (callee.descriptor as ConstructorDescriptor).constructedClass
val thisValue = if (constructedClass.isArray) {
assert(args.size >= 1 && args[0].type == int32Type)
val allocArrayInstanceArgs = listOf(typeInfo, allocHint, args[0])
call(context.llvm.allocArrayFunction, allocArrayInstanceArgs)
codegen.allocArray(codegen.typeInfoValue(constructedClass), args[0],
resultLifetime(callee))
} else {
call(context.llvm.allocInstanceFunction, listOf(typeInfo, allocHint))
codegen.allocInstance(codegen.typeInfoValue(constructedClass), resultLifetime(callee))
}
val constructorParams: MutableList<LLVMValueRef> = mutableListOf()
constructorParams += thisValue
constructorParams += args
evaluateSimpleFunctionCall(callee.descriptor as FunctionDescriptor, constructorParams)
evaluateSimpleFunctionCall(callee.descriptor as FunctionDescriptor,
listOf(thisValue) + args, Lifetime.IRRELEVANT /* constructor doesn't return anything */)
return thisValue
}
}
@@ -1758,8 +1784,8 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
return when (name) {
"konan.internal.areEqualByValue" -> {
val arg0 = args[0]!!
val arg1 = args[1]!!
val arg0 = args[0]
val arg1 = args[1]
assert (arg0.type == arg1.type)
when (LLVMGetTypeKind(arg0.type)) {
@@ -1787,12 +1813,12 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
val descriptor = callee.descriptor
val ib = context.irModule!!.irBuiltins
when (descriptor) {
ib.eqeqeq -> return codegen.icmpEq(args[0]!!, args[1]!!)
ib.gt0 -> return codegen.icmpGt(args[0]!!, kImmZero)
ib.gteq0 -> return codegen.icmpGe(args[0]!!, kImmZero)
ib.lt0 -> return codegen.icmpLt(args[0]!!, kImmZero)
ib.lteq0 -> return codegen.icmpLe(args[0]!!, kImmZero)
ib.booleanNot -> return codegen.icmpNe(args[0]!!, kTrue)
ib.eqeqeq -> return codegen.icmpEq(args[0], args[1])
ib.gt0 -> return codegen.icmpGt(args[0], kImmZero)
ib.gteq0 -> return codegen.icmpGe(args[0], kImmZero)
ib.lt0 -> return codegen.icmpLt(args[0], kImmZero)
ib.lteq0 -> return codegen.icmpLe(args[0], kImmZero)
ib.booleanNot -> return codegen.icmpNe(args[0], kTrue)
else -> {
TODO(descriptor.name.toString())
}
@@ -1824,7 +1850,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
if (resultPhi != null && !isNothing)
codegen.assignPhis(resultPhi to brResult)
if (bbExit != null && !isNothing)
codegen.br(bbExit!!)
codegen.br(bbExit)
if (bbNext != null) // Switch generation to next or exit.
codegen.positionAtEnd(bbNext)
else if (bbExit != null)
@@ -1840,15 +1866,17 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
//-------------------------------------------------------------------------//
fun callDirect(descriptor: FunctionDescriptor, args: List<LLVMValueRef>): LLVMValueRef {
val realDescriptor = DescriptorUtils.unwrapFakeOverride(descriptor)
fun callDirect(descriptor: FunctionDescriptor, args: List<LLVMValueRef>,
resultLifetime: Lifetime): LLVMValueRef {
val realDescriptor = descriptor.resolveFakeOverride().original
val llvmFunction = codegen.functionLlvmValue(realDescriptor)
return call(descriptor, llvmFunction, args)
return call(descriptor, llvmFunction, args, resultLifetime)
}
//-------------------------------------------------------------------------//
fun callVirtual(descriptor: FunctionDescriptor, args: List<LLVMValueRef>): LLVMValueRef {
fun callVirtual(descriptor: FunctionDescriptor, args: List<LLVMValueRef>,
resultLifetime: Lifetime): LLVMValueRef {
val typeInfoPtrPtr = LLVMBuildStructGEP(codegen.builder, args[0], 0 /* type_info */, "")!!
val typeInfoPtr = codegen.load(typeInfoPtrPtr)
assert (typeInfoPtr.type == codegen.kTypeInfoPtr)
@@ -1864,18 +1892,17 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
val slot = codegen.gep(vtable, Int32(index).llvm)
codegen.load(slot)
} else {
// Otherwise, call via hashtable.
// Otherwise, call by hash.
// TODO: optimize by storing interface number in lower bits of 'this' pointer
// when passing object as an interface. This way we can use those bits as index
// for an additional per-interface vtable.
val methodHash = codegen.functionHash(descriptor) // Calculate hash of the method to be invoked
val lookupArgs = listOf(typeInfoPtr, methodHash) // Prepare args for lookup
call(context.llvm.lookupOpenMethodFunction,
lookupArgs)
call(context.llvm.lookupOpenMethodFunction, lookupArgs)
}
val functionPtrType = pointerType(codegen.getLlvmFunctionType(descriptor)) // Construct type of the method to be invoked
val function = codegen.bitcast(functionPtrType, llvmMethod) // Cast method address to the type
return call(descriptor, function, args) // Invoke the method
return call(descriptor, function, args, resultLifetime) // Invoke the method
}
//-------------------------------------------------------------------------//
@@ -1884,8 +1911,9 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
// instead of a plain list.
// In such case it would be possible to check that all args are available and in the correct order.
// However, it currently requires some refactoring to be performed.
private fun call(descriptor: FunctionDescriptor, function: LLVMValueRef, args: List<LLVMValueRef>): LLVMValueRef {
val result = call(function, args)
private fun call(descriptor: FunctionDescriptor, function: LLVMValueRef, args: List<LLVMValueRef>,
resultLifetime: Lifetime): LLVMValueRef {
val result = call(function, args, resultLifetime)
if (descriptor.returnType?.isNothing() == true) {
codegen.unreachable()
}
@@ -1897,15 +1925,9 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
return result
}
private fun call(function: LLVMValueRef, args: List<LLVMValueRef>): LLVMValueRef {
if (codegen.isObjectReturn(function.type)) {
// If function returns an object - create slot for the returned value.
// This allows appropriate rootset accounting by just looking on stack slots.
val resultSlot = codegen.vars.createAnonymousSlot()
return currentCodeContext.genCall(function, args + resultSlot)
} else {
return currentCodeContext.genCall(function, args)
}
private fun call(function: LLVMValueRef, args: List<LLVMValueRef>,
resultLifetime: Lifetime = Lifetime.IRRELEVANT): LLVMValueRef {
return currentCodeContext.genCall(function, args, resultLifetime)
}
//-------------------------------------------------------------------------//
@@ -1923,7 +1945,8 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
codegen.bitcast(thisPtrArgType, thisPtr)
}
return callDirect(descriptor, listOf(thisPtrArg) + args)
return callDirect(descriptor, listOf(thisPtrArg) + args,
Lifetime.IRRELEVANT /* no value returned */)
}
//-------------------------------------------------------------------------//
@@ -0,0 +1,335 @@
package org.jetbrains.kotlin.backend.konan.llvm
import kotlinx.cinterop.*
import llvm.*
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.descriptors.*
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
internal fun createLlvmDeclarations(context: Context): LlvmDeclarations {
val generator = DeclarationsGeneratorVisitor(context)
context.ir.irModule.acceptChildrenVoid(generator)
return with(generator) {
LlvmDeclarations(
functions, classes, fields, staticFields, theUnitInstanceRef
)
}
}
internal class LlvmDeclarations(
private val functions: Map<FunctionDescriptor, FunctionLlvmDeclarations>,
private val classes: Map<ClassDescriptor, ClassLlvmDeclarations>,
private val fields: Map<PropertyDescriptor, FieldLlvmDeclarations>,
private val staticFields: Map<PropertyDescriptor, StaticFieldLlvmDeclarations>,
private val theUnitInstanceRef: ConstPointer?
) {
fun forFunction(descriptor: FunctionDescriptor) = functions[descriptor] ?:
error(descriptor.toString())
fun forClass(descriptor: ClassDescriptor) = classes[descriptor] ?:
error(descriptor.toString())
fun forField(descriptor: PropertyDescriptor) = fields[descriptor] ?:
error(descriptor.toString())
fun forStaticField(descriptor: PropertyDescriptor) = staticFields[descriptor] ?:
error(descriptor.toString())
fun forSingleton(descriptor: ClassDescriptor) = forClass(descriptor).singletonDeclarations ?:
error(descriptor.toString())
fun getUnitInstanceRef() = theUnitInstanceRef ?: error("")
}
internal class ClassLlvmDeclarations(
val bodyType: LLVMTypeRef,
val fields: List<PropertyDescriptor>, // TODO: it is not an LLVM declaration.
val typeInfoGlobal: StaticData.Global,
val typeInfo: ConstPointer,
val singletonDeclarations: SingletonLlvmDeclarations?)
internal class SingletonLlvmDeclarations(val instanceFieldRef: LLVMValueRef)
internal class FunctionLlvmDeclarations(val llvmFunction: LLVMValueRef)
internal class FieldLlvmDeclarations(val index: Int, val classBodyType: LLVMTypeRef)
internal class StaticFieldLlvmDeclarations(val storage: LLVMValueRef)
// TODO: rework getFields and getDeclaredFields.
/**
* All fields of the class instance.
* The order respects the class hierarchy, i.e. a class [fields] contains superclass [fields] as a prefix.
*/
private fun getFields(context: Context, classDescriptor: ClassDescriptor): List<PropertyDescriptor> {
val superClass = classDescriptor.getSuperClassNotAny() // TODO: what if Any has fields?
val superFields = if (superClass != null) getFields(context, superClass) else emptyList()
return superFields + getDeclaredFields(context, classDescriptor)
}
/**
* Fields declared in the class.
*/
private fun getDeclaredFields(context: Context, classDescriptor: ClassDescriptor): List<PropertyDescriptor> {
// TODO: Here's what is going on here:
// The existence of a backing field for a property is only described in the IR,
// but not in the property descriptor.
// That works, while we process the IR, but not for deserialized descriptors.
//
// So to have something in deserialized descriptors,
// while we still see the IR, we mark the property with an annotation.
//
// We could apply the annotation during IR rewite, but we still are not
// that far in the rewriting infrastructure. So we postpone
// the annotation until the serializer.
//
// In this function we check the presence of the backing filed
// two ways: first we check IR, then we check the annotation.
val irClass = context.ir.moduleIndexForCodegen.classes[classDescriptor]
if (irClass != null) {
val declarations = irClass.declarations
return declarations.mapNotNull {
when (it) {
is IrProperty -> it.backingField?.descriptor
is IrField -> it.descriptor
else -> null
}
}
} else {
val properties = classDescriptor.unsubstitutedMemberScope.
getContributedDescriptors().
filterIsInstance<PropertyDescriptor>()
return properties.mapNotNull { it.backingField }
}
}
private fun ContextUtils.createClassBodyType(name: String, fields: List<PropertyDescriptor>): LLVMTypeRef {
val fieldTypes = fields.map { getLLVMType(it.type) }.toTypedArray()
val classType = LLVMStructCreateNamed(LLVMGetModuleContext(context.llvmModule), name)!!
memScoped {
val fieldTypesNativeArrayPtr = allocArrayOf(*fieldTypes)[0].ptr
LLVMStructSetBody(classType, fieldTypesNativeArrayPtr, fieldTypes.size, 0)
}
return classType
}
private class DeclarationsGeneratorVisitor(override val context: Context) :
IrElementVisitorVoid, ContextUtils {
val functions = mutableMapOf<FunctionDescriptor, FunctionLlvmDeclarations>()
val classes = mutableMapOf<ClassDescriptor, ClassLlvmDeclarations>()
val fields = mutableMapOf<PropertyDescriptor, FieldLlvmDeclarations>()
val staticFields = mutableMapOf<PropertyDescriptor, StaticFieldLlvmDeclarations>()
var theUnitInstanceRef: ConstPointer? = null
private class Namer(val prefix: String) {
private val names = mutableMapOf<DeclarationDescriptor, Name>()
private val counts = mutableMapOf<FqName, Int>()
fun getName(parent: FqName, descriptor: DeclarationDescriptor): Name {
return names.getOrPut(descriptor) {
val count = counts.getOrDefault(parent, 0) + 1
counts[parent] = count
Name.identifier(prefix + count)
}
}
}
val objectNamer = Namer("object-")
private fun getLocalName(parent: FqName, descriptor: DeclarationDescriptor): Name {
if (DescriptorUtils.isAnonymousObject(descriptor)) {
return objectNamer.getName(parent, descriptor)
}
return descriptor.name
}
private fun getFqName(descriptor: DeclarationDescriptor): FqName {
if (descriptor is PackageFragmentDescriptor) {
return descriptor.fqName
}
val containingDeclaration = descriptor.containingDeclaration
val parent = if (containingDeclaration != null) {
getFqName(containingDeclaration)
} else {
FqName.ROOT
}
val localName = getLocalName(parent, descriptor)
return parent.child(localName)
}
/**
* Produces the name to be used for non-exported LLVM declarations corresponding to [descriptor].
*
* Note: since these declarations are going to be private, the name is only required not to clash with any
* exported declarations.
*/
private fun qualifyInternalName(descriptor: DeclarationDescriptor): String {
return getFqName(descriptor).asString() + "#internal"
}
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitClass(declaration: IrClass) {
if (declaration.descriptor.isIntrinsic) {
// do not generate any declarations for intrinsic classes as they require special handling
} else {
this.classes[declaration.descriptor] = createClassDeclarations(declaration)
}
super.visitClass(declaration)
}
private fun createClassDeclarations(declaration: IrClass): ClassLlvmDeclarations {
val descriptor = declaration.descriptor
val internalName = qualifyInternalName(descriptor)
val fields = getFields(context, descriptor)
val bodyType = createClassBodyType("kclassbody:$internalName", fields)
val typeInfoPtr: ConstPointer
val typeInfoGlobal: StaticData.Global
val typeInfoSymbolName = if (descriptor.isExported()) {
descriptor.typeInfoSymbolName
} else {
"ktype:$internalName"
}
if (!descriptor.isAbstract()) {
// Create the special global consisting of TypeInfo and vtable.
val typeInfoGlobalName = "ktypeglobal:$internalName"
val typeInfoWithVtableType = structType(
runtime.typeInfoType,
LLVMArrayType(int8TypePtr, descriptor.vtableEntries.size)!!
)
typeInfoGlobal = staticData.createGlobal(typeInfoWithVtableType, typeInfoGlobalName, isExported = false)
val llvmTypeInfoPtr = LLVMAddAlias(context.llvmModule,
kTypeInfoPtr,
typeInfoGlobal.pointer.getElementPtr(0).llvm,
typeInfoSymbolName)!!
if (!descriptor.isExported()) {
LLVMSetLinkage(llvmTypeInfoPtr, LLVMLinkage.LLVMInternalLinkage)
}
typeInfoPtr = constPointer(llvmTypeInfoPtr)
} else {
typeInfoGlobal = staticData.createGlobal(runtime.typeInfoType,
typeInfoSymbolName,
isExported = descriptor.isExported())
typeInfoPtr = typeInfoGlobal.pointer
}
val singletonDeclarations = if (descriptor.kind.isSingleton) {
createSingletonDeclarations(descriptor, typeInfoPtr, bodyType)
} else {
null
}
return ClassLlvmDeclarations(bodyType, fields, typeInfoGlobal, typeInfoPtr, singletonDeclarations)
}
private fun createSingletonDeclarations(
descriptor: ClassDescriptor,
typeInfoPtr: ConstPointer,
bodyType: LLVMTypeRef
): SingletonLlvmDeclarations? {
if (descriptor.isUnit()) {
this.theUnitInstanceRef = staticData.createUnitInstance(descriptor, bodyType, typeInfoPtr)
return null
}
val symbolName = if (descriptor.isExported()) {
descriptor.objectInstanceFieldSymbolName
} else {
"kobjref:" + qualifyInternalName(descriptor)
}
val instanceFieldRef = LLVMAddGlobal(
context.llvmModule,
getLLVMType(descriptor.defaultType),
symbolName
)!!
return SingletonLlvmDeclarations(instanceFieldRef)
}
override fun visitField(declaration: IrField) {
super.visitField(declaration)
val descriptor = declaration.descriptor
val dispatchReceiverParameter = descriptor.dispatchReceiverParameter
if (dispatchReceiverParameter != null) {
val containingClass = dispatchReceiverParameter.containingDeclaration
val classDeclarations = this.classes[containingClass] ?: error(containingClass.toString())
val allFields = classDeclarations.fields
this.fields[descriptor] = FieldLlvmDeclarations(
allFields.indexOf(descriptor),
classDeclarations.bodyType
)
} else {
// Fields are module-private, so we use internal name:
val name = "kvar:" + qualifyInternalName(descriptor)
val storage = LLVMAddGlobal(context.llvmModule, getLLVMType(descriptor.type), name)!!
this.staticFields[descriptor] = StaticFieldLlvmDeclarations(storage)
}
}
override fun visitFunction(declaration: IrFunction) {
super.visitFunction(declaration)
val descriptor = declaration.descriptor
val llvmFunctionType = getLlvmFunctionType(descriptor)
val llvmFunction = if (descriptor.isExternal) {
context.llvm.externalFunction(descriptor.symbolName, llvmFunctionType)
} else {
val symbolName = if (descriptor.isExported()) {
descriptor.symbolName
} else {
"kfun:" + qualifyInternalName(descriptor)
}
LLVMAddFunction(context.llvmModule, symbolName, llvmFunctionType)!!
}
this.functions[descriptor] = FunctionLlvmDeclarations(llvmFunction)
}
}
@@ -111,25 +111,27 @@ internal val ContextUtils.kTheAnyTypeInfo: LLVMValueRef
get() = KonanPlatform.builtIns.any.llvmTypeInfoPtr
internal val ContextUtils.kTheArrayTypeInfo: LLVMValueRef
get() = KonanPlatform.builtIns.array.llvmTypeInfoPtr
internal val ContextUtils.kTypeInfo: LLVMTypeRef
get() = LLVMGetTypeByName(context.llvmModule, "struct.TypeInfo")!!
internal val ContextUtils.kObjHeader: LLVMTypeRef
get() = LLVMGetTypeByName(context.llvmModule, "struct.ObjHeader")!!
internal val ContextUtils.kObjHeaderPtr: LLVMTypeRef
internal val RuntimeAware.kTypeInfo: LLVMTypeRef
get() = runtime.typeInfoType
internal val RuntimeAware.kObjHeader: LLVMTypeRef
get() = runtime.objHeaderType
internal val RuntimeAware.kContainerHeader: LLVMTypeRef
get() = runtime.containerHeaderType
internal val RuntimeAware.kObjHeaderPtr: LLVMTypeRef
get() = pointerType(kObjHeader)
internal val ContextUtils.kObjHeaderPtrPtr: LLVMTypeRef
internal val RuntimeAware.kObjHeaderPtrPtr: LLVMTypeRef
get() = pointerType(kObjHeaderPtr)
internal val ContextUtils.kArrayHeader: LLVMTypeRef
get() = LLVMGetTypeByName(context.llvmModule, "struct.ArrayHeader")!!
internal val ContextUtils.kArrayHeaderPtr: LLVMTypeRef
internal val RuntimeAware.kArrayHeader: LLVMTypeRef
get() = runtime.arrayHeaderType
internal val RuntimeAware.kArrayHeaderPtr: LLVMTypeRef
get() = pointerType(kArrayHeader)
internal val ContextUtils.kTypeInfoPtr: LLVMTypeRef
internal val RuntimeAware.kTypeInfoPtr: LLVMTypeRef
get() = pointerType(kTypeInfo)
internal val kInt1 = LLVMInt1Type()!!
internal val kBoolean = kInt1
internal val kInt8Ptr = pointerType(int8Type)
internal val kInt8PtrPtr = pointerType(kInt8Ptr)
internal val kNullInt8Ptr = LLVMConstNull(kInt8Ptr)
internal val kNullInt8Ptr = LLVMConstNull(kInt8Ptr)!!
internal val kImmInt32One = Int32(1).llvm
internal val kImmInt64One = Int64(1).llvm
internal val ContextUtils.kNullObjHeaderPtr: LLVMValueRef
@@ -149,18 +151,6 @@ internal fun structType(types: List<LLVMTypeRef>): LLVMTypeRef = memScoped {
LLVMStructType(allocArrayOf(types)[0].ptr, types.size, 0)!!
}
internal fun ContextUtils.getLlvmFunctionType(function: FunctionDescriptor): LLVMTypeRef {
val original = function.original
val returnType = if (original is ConstructorDescriptor) voidType else getLLVMReturnType(original.returnType!!)
val paramTypes = ArrayList(original.allValueParameters.map { getLLVMType(it.type) })
if (isObjectType(returnType)) paramTypes.add(kObjHeaderPtrPtr)
memScoped {
val paramTypesPtr = allocArrayOf(paramTypes)[0].ptr
return LLVMFunctionType(returnType, paramTypesPtr, paramTypes.size, 0)!!
}
}
internal fun ContextUtils.numParameters(functionType: LLVMTypeRef) : Int {
// Note that type is usually function pointer, so we have to dereference it.
return LLVMCountParamTypes(LLVMGetElementType(functionType))!!
@@ -176,7 +166,7 @@ internal fun ContextUtils.isObjectRef(value: LLVMValueRef): Boolean {
return isObjectType(value.type)
}
internal fun ContextUtils.isObjectType(type: LLVMTypeRef): Boolean {
internal fun RuntimeAware.isObjectType(type: LLVMTypeRef): Boolean {
return type == kObjHeaderPtr || type == kArrayHeaderPtr
}
@@ -198,6 +188,7 @@ internal fun ContextUtils.externalGlobal(name: String, type: LLVMTypeRef): LLVMV
val found = LLVMGetNamedGlobal(context.llvmModule, name)
if (found != null) {
assert (getGlobalType(found) == type)
assert (LLVMGetInitializer(found) == null)
return found
} else {
return LLVMAddGlobal(context.llvmModule, type, name)!!
@@ -225,3 +216,9 @@ internal operator fun LLVMAttributeSet.contains(attribute: LLVMAttribute): Boole
return (this and attribute.value) != 0
}
fun getStructElements(type: LLVMTypeRef): List<LLVMTypeRef> {
val count = LLVMCountStructElementTypes(type)
return (0 until count).map {
LLVMStructGetTypeAtIndex(type, it)!!
}
}
@@ -1,99 +0,0 @@
package org.jetbrains.kotlin.backend.konan.llvm
import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.ClassKind.*
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.OverridingUtil
import org.jetbrains.kotlin.resolve.constants.StringValue
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
import org.jetbrains.kotlin.resolve.findOriginalTopMostOverriddenDescriptors
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
private val symbolNameAnnotation = FqName("konan.SymbolName")
private val exportForCppRuntimeAnnotation = FqName("konan.internal.ExportForCppRuntime")
fun typeToHashString(type: KotlinType): String {
if (TypeUtils.isTypeParameter(type)) return "GENERIC"
var hashString = TypeUtils.getClassDescriptor(type)!!.fqNameSafe.asString()
if (!type.arguments.isEmpty()) {
hashString += "<${type.arguments.map {
typeToHashString(it.type)
}.joinToString(",")}>"
}
if (type.isMarkedNullable) hashString += "?"
return hashString
}
private val FunctionDescriptor.signature: String
get() {
val extensionReceiverPart = this.extensionReceiverParameter?.let { "${it.type}." } ?: ""
val actualDescriptor = this.findOriginalTopMostOverriddenDescriptors().firstOrNull() ?: this
val argsPart = actualDescriptor.valueParameters.map {
typeToHashString(it.type)
}.joinToString(";")
// TODO: add return type
// (it is not simple because return type can be changed when overriding)
return "$extensionReceiverPart($argsPart)"
}
// TODO: rename to indicate that it has signature included
internal val FunctionDescriptor.functionName: String
get() = with(this.original) { // basic support for generics
"$name$signature"
}
internal val FunctionDescriptor.symbolName: String
get() {
this.annotations.findAnnotation(symbolNameAnnotation)?.let {
if (this.isExternal) {
return getStringValue(it)!!
} else {
// ignore; TODO: report compile error
}
}
this.annotations.findAnnotation(exportForCppRuntimeAnnotation)?.let {
val name = getStringValue(it) ?: this.name.asString()
return name // no wrapping currently required
}
val containingDeclarationPart = containingDeclaration.fqNameSafe.let {
if (it.isRoot) "" else "$it."
}
return "kfun:$containingDeclarationPart$functionName"
}
private fun getStringValue(annotation: AnnotationDescriptor): String? {
annotation.allValueArguments.values.ifNotEmpty {
val stringValue = this.single() as StringValue
return stringValue.value
}
return null
}
internal val ClassDescriptor.symbolName: String
get() = when (this.kind) {
CLASS -> "kclass:"
INTERFACE -> "kinf:"
OBJECT -> "kclass:"
else -> TODO("fixme: " + this.kind)
} + fqNameSafe
internal val ClassDescriptor.typeInfoSymbolName: String
get() = "ktype:" + this.fqNameSafe.toString()
internal val PropertyDescriptor.symbolName:String
get() = "kvar:${this.containingDeclaration.name.asString()}.${this.name.asString()}"
@@ -1,7 +1,5 @@
package org.jetbrains.kotlin.backend.konan.llvm
import kotlinx.cinterop.*
import llvm.*
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.descriptors.*
@@ -52,18 +50,6 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
Int32(fieldsCount)
)
// TODO: probably it should be moved out of this class and shared.
private fun createStructFor(className: FqName, fields: List<PropertyDescriptor>): LLVMTypeRef? {
val classType = LLVMStructCreateNamed(LLVMGetModuleContext(context.llvmModule), "kclass:" + className)
val fieldTypes = fields.map { getLLVMType(it.returnType!!) }.toTypedArray()
memScoped {
val fieldTypesNativeArrayPtr = allocArrayOf(*fieldTypes)[0].ptr
LLVMStructSetBody(classType, fieldTypesNativeArrayPtr, fieldTypes.size, 0)
}
return classType
}
private fun exportTypeInfoIfRequired(classDesc: ClassDescriptor, typeInfoGlobal: LLVMValueRef?) {
val annot = classDesc.annotations.findAnnotation(FqName("konan.ExportTypeInfo"))
if (annot != null) {
@@ -97,11 +83,13 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
val className = classDesc.fqNameSafe
val classType = createStructFor(className, classDesc.fields)
val llvmDeclarations = context.llvmDeclarations.forClass(classDesc)
val bodyType = llvmDeclarations.bodyType
val name = className.globalHash
val size = getInstanceSize(classType, className)
val size = getInstanceSize(bodyType, className)
val superTypeOrAny = classDesc.getSuperClassOrAny()
val superType = if (KotlinBuiltIns.isAny(classDesc)) NullPointer(runtime.typeInfoType)
@@ -111,51 +99,39 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
val interfacesPtr = staticData.placeGlobalConstArray("kintf:$className",
pointerType(runtime.typeInfoType), interfaces)
val refFieldIndices = classDesc.fields.mapIndexedNotNull { index, field ->
val type = field.returnType!!
if (isObjectType(getLLVMType(type))) {
index
// TODO: reuse offsets obtained for 'fields' below
val objOffsets = getStructElements(bodyType).mapIndexedNotNull { index, type ->
if (isObjectType(type)) {
LLVMOffsetOfElement(llvmTargetData, bodyType, index)
} else {
null
}
}
// TODO: reuse offsets obtained for 'fields' below
val objOffsets = refFieldIndices.map { LLVMOffsetOfElement(llvmTargetData, classType, it) }
val objOffsetsPtr = staticData.placeGlobalConstArray("krefs:$className", int32Type,
objOffsets.map { Int32(it.toInt()) })
val fields = classDesc.fields.mapIndexed { index, field ->
val fields = llvmDeclarations.fields.mapIndexed { index, field ->
// Note: using FQ name because a class may have multiple fields with the same name due to property overriding
val nameSignature = field.fqNameSafe.localHash // FIXME: add signature
val fieldOffset = LLVMOffsetOfElement(llvmTargetData, classType, index)
val fieldOffset = LLVMOffsetOfElement(llvmTargetData, bodyType, index)
FieldTableRecord(nameSignature, fieldOffset.toInt())
}.sortedBy { it.nameSignature.value }
val fieldsPtr = staticData.placeGlobalConstArray("kfields:$className",
runtime.fieldTableRecordType, fields)
val vtableEntries: List<ConstValue>
val methods: List<ConstValue>
if (!classDesc.isAbstract()) {
// TODO: compile-time resolution limits binary compatibility
vtableEntries = classDesc.vtableEntries.map { it.implementation.entryPointAddress }
methods = classDesc.methodTableEntries.map {
val methods = if (!classDesc.isAbstract()) {
classDesc.methodTableEntries.map {
val nameSignature = it.functionName.localHash
// TODO: compile-time resolution limits binary compatibility
val methodEntryPoint = it.implementation.entryPointAddress
val methodEntryPoint = it.resolveFakeOverride().original.entryPointAddress
MethodTableRecord(nameSignature, methodEntryPoint)
}.sortedBy { it.nameSignature.value }
} else {
vtableEntries = emptyList()
methods = emptyList()
emptyList()
}
assert (vtableEntries.size == classDesc.vtableSize)
val vtable = ConstArray(int8TypePtr, vtableEntries)
val methodsPtr = staticData.placeGlobalConstArray("kmethods:$className",
runtime.methodTableRecordType, methods)
@@ -166,9 +142,19 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
methodsPtr, methods.size,
fieldsPtr, if (classDesc.isInterface) -1 else fields.size)
val typeInfoGlobal = classDesc.typeInfoWithVtable.llvm // TODO: it is a hack
LLVMSetInitializer(typeInfoGlobal, Struct(typeInfo, vtable).llvm)
LLVMSetGlobalConstant(typeInfoGlobal, 1)
val typeInfoGlobal = llvmDeclarations.typeInfoGlobal
val typeInfoGlobalValue = if (classDesc.isAbstract()) {
typeInfo
} else {
// TODO: compile-time resolution limits binary compatibility
val vtableEntries = classDesc.vtableEntries.map { it.resolveFakeOverride().original.entryPointAddress }
val vtable = ConstArray(int8TypePtr, vtableEntries)
Struct(typeInfo, vtable)
}
typeInfoGlobal.setInitializer(typeInfoGlobalValue)
typeInfoGlobal.setConstant(true)
exportTypeInfoIfRequired(classDesc, classDesc.llvmTypeInfoPtr)
}
@@ -3,6 +3,10 @@ package org.jetbrains.kotlin.backend.konan.llvm
import kotlinx.cinterop.*
import llvm.*
interface RuntimeAware {
val runtime: Runtime
}
class Runtime(private val bitcodeFile: String) {
val llvmModule: LLVMModuleRef
@@ -11,6 +15,7 @@ class Runtime(private val bitcodeFile: String) {
val bufRef = alloc<LLVMMemoryBufferRefVar>()
val errorRef = allocPointerTo<CInt8Var>()
val res = LLVMCreateMemoryBufferWithContentsOfFile(bitcodeFile, bufRef.ptr, errorRef.ptr)
if (res != 0) {
throw Error(errorRef.value?.asCString()?.toString())
@@ -15,34 +15,34 @@ internal class StaticData(override val context: Context): ContextUtils {
class Global private constructor(val staticData: StaticData, val llvmGlobal: LLVMValueRef) {
companion object {
private fun createLlvmGlobal(module: LLVMModuleRef, type: LLVMTypeRef, name: String): LLVMValueRef {
val isUnnamed = (name == "") // LLVM will select the unique index and represent the global as `@idx`.
private fun createLlvmGlobal(module: LLVMModuleRef,
type: LLVMTypeRef,
name: String,
isExported: Boolean
): LLVMValueRef {
if (!isUnnamed) {
LLVMGetNamedGlobal(module, name)?.let { existingGlobal ->
if (getGlobalType(existingGlobal) != type || LLVMGetInitializer(existingGlobal) != null) {
throw IllegalArgumentException("Global '$name' already exists")
} else {
return existingGlobal
}
}
if (isExported && LLVMGetNamedGlobal(module, name) != null) {
throw IllegalArgumentException("Global '$name' already exists")
}
val llvmGlobal = LLVMAddGlobal(module, type, name)!!
if (isUnnamed) {
// Currently using unnamed globals may result in link errors due to symbol redefinition;
// apply the workaround:
LLVMSetLinkage(llvmGlobal, LLVMLinkage.LLVMPrivateLinkage)
if (!isExported) {
LLVMSetLinkage(llvmGlobal, LLVMLinkage.LLVMInternalLinkage)
}
return llvmGlobal
}
fun create(staticData: StaticData, type: LLVMTypeRef, name: String): Global {
fun create(staticData: StaticData, type: LLVMTypeRef, name: String, isExported: Boolean): Global {
val module = staticData.context.llvmModule
val llvmGlobal = createLlvmGlobal(module!!, type, name)
val isUnnamed = (name == "") // LLVM will select the unique index and represent the global as `@idx`.
if (isUnnamed && isExported) {
throw IllegalArgumentException("unnamed global can't be exported")
}
val llvmGlobal = createLlvmGlobal(module!!, type, name, isExported)
return Global(staticData, llvmGlobal)
}
}
@@ -110,15 +110,15 @@ internal class StaticData(override val context: Context): ContextUtils {
*
* It is external until explicitly initialized with [Global.setInitializer].
*/
fun createGlobal(type: LLVMTypeRef, name: String): Global {
return Global.create(this, type, name)
fun createGlobal(type: LLVMTypeRef, name: String, isExported: Boolean = false): Global {
return Global.create(this, type, name, isExported)
}
/**
* Creates [Global] with given name and value.
*/
fun placeGlobal(name: String, initializer: ConstValue): Global {
val global = createGlobal(initializer.llvmType, name)
fun placeGlobal(name: String, initializer: ConstValue, isExported: Boolean = false): Global {
val global = createGlobal(initializer.llvmType, name, isExported)
global.setInitializer(initializer)
return global
}
@@ -117,16 +117,23 @@ internal fun StaticData.createArrayList(elementType: TypeProjection, array: Cons
return createKotlinObject(type, body)
}
private val theUnitInstanceName = "kobj:kotlin.Unit"
internal fun StaticData.createUnitInstance(descriptor: ClassDescriptor): ConstPointer {
internal fun StaticData.createUnitInstance(descriptor: ClassDescriptor,
bodyType: LLVMTypeRef,
typeInfo: ConstPointer
): ConstPointer {
assert (descriptor.isUnit())
assert (descriptor.fields.isEmpty())
val typeInfo = descriptor.defaultType.typeInfoPtr!!
assert (getStructElements(bodyType).isEmpty())
val objHeader = objHeader(typeInfo)
val global = this.placeGlobal(theUnitInstanceName, objHeader)
val global = this.placeGlobal(theUnitInstanceName, objHeader, isExported = true)
return global.pointer
}
internal val ContextUtils.theUnitInstanceRef: ConstPointer
get() = constPointer(externalGlobal(theUnitInstanceName, context.llvm.runtime.objHeaderType))
get() {
val unitDescriptor = context.builtIns.unit
return if (isExternal(unitDescriptor)) {
constPointer(externalGlobal(theUnitInstanceName, context.llvm.runtime.objHeaderType))
} else {
context.llvmDeclarations.getUnitInstanceRef()
}
}
@@ -2,6 +2,7 @@ package org.jetbrains.kotlin.backend.konan.llvm
import llvm.*
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.*
@@ -19,20 +20,25 @@ internal class VariableManager(val codegen: CodeGenerator) {
override fun toString() = (if (refSlot) "refslot" else "slot") + " for ${address}"
}
class ValueRecord(val value: LLVMValueRef, val descriptor: ValueDescriptor) : Record {
class ValueRecord(val value: LLVMValueRef, val name: Name) : Record {
override fun load() : LLVMValueRef = value
override fun store(value: LLVMValueRef) = throw Error("writing to immutable: ${descriptor}")
override fun address() : LLVMValueRef = throw Error("no address for: ${descriptor}")
override fun toString() = "value of ${value} from ${descriptor}"
override fun store(value: LLVMValueRef) = throw Error("writing to immutable: ${name}")
override fun address() : LLVMValueRef = throw Error("no address for: ${name}")
override fun toString() = "value of ${value} from ${name}"
}
data class ScopedVariable private constructor(val descriptor: ValueDescriptor, val context: CodeContext)
{
constructor(scoped: Pair<ValueDescriptor, CodeContext>) : this(scoped.first, scoped.second)
}
val variables: ArrayList<Record> = arrayListOf()
val descriptors: HashMap<Pair<ValueDescriptor, CodeContext>, Int> = hashMapOf()
val contextVariablesToIndex: HashMap<ScopedVariable, Int> = hashMapOf()
// Clears inner state of variable manager.
fun clear() {
variables.clear()
descriptors.clear()
contextVariablesToIndex.clear()
}
fun createVariable(scoped: Pair<VariableDescriptor, CodeContext>, value: LLVMValueRef? = null) : Int {
@@ -51,14 +57,15 @@ internal class VariableManager(val codegen: CodeGenerator) {
fun createMutable(scoped: Pair<VariableDescriptor, CodeContext>,
isVar: Boolean, value: LLVMValueRef? = null) : Int {
val descriptor = scoped.first
assert(descriptors.get(scoped) == null)
val scopedVariable = ScopedVariable(scoped)
assert(!contextVariablesToIndex.contains(scopedVariable))
val index = variables.size
val type = codegen.getLLVMType(descriptor.type)
val slot = codegen.alloca(type, descriptor.name.asString())
val slot = codegen.alloca(type, scopedVariable.descriptor.name.asString())
if (value != null)
codegen.storeAnyLocal(value, slot)
variables.add(SlotRecord(slot, codegen.isObjectType(type), isVar))
descriptors[scoped] = index
contextVariablesToIndex[scopedVariable] = index
return index
}
@@ -83,16 +90,18 @@ internal class VariableManager(val codegen: CodeGenerator) {
}
fun createImmutable(scoped: Pair<ValueDescriptor, CodeContext>, value: LLVMValueRef) : Int {
val descriptor = scoped.first
assert(descriptors.get(scoped) == null)
val scopedVariable = ScopedVariable(scoped)
if(contextVariablesToIndex.containsKey(scopedVariable))
throw Error("${scopedVariable.descriptor} is already defined")
assert(!contextVariablesToIndex.containsKey(scopedVariable))
val index = variables.size
variables.add(ValueRecord(value, descriptor))
descriptors[scoped] = index
variables.add(ValueRecord(value, scopedVariable.descriptor.name))
contextVariablesToIndex[scopedVariable] = index
return index
}
fun indexOf(scoped: Pair<ValueDescriptor, CodeContext>) : Int {
return descriptors.getOrElse(scoped) { -1 }
return contextVariablesToIndex.getOrElse(ScopedVariable(scoped)) { -1 }
}
fun addressOf(index: Int): LLVMValueRef {
@@ -6,8 +6,9 @@ import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.descriptors.getKonanInternalClass
import org.jetbrains.kotlin.backend.konan.descriptors.getKonanInternalFunctions
import org.jetbrains.kotlin.backend.konan.descriptors.unboundCallableReferenceTypeOrNull
import org.jetbrains.kotlin.descriptors.ParameterDescriptor
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
@@ -128,8 +129,17 @@ private class AutoboxingTransformer(val context: Context) : AbstractValueUsageTr
return this.adaptIfNecessary(actualType, type)
}
override fun IrExpression.useAsArgument(parameter: ParameterDescriptor): IrExpression {
return this.useAsValue(parameter.original)
override fun IrExpression.useAsDispatchReceiver(function: CallableDescriptor): IrExpression {
return this.useAsArgument(function.original.dispatchReceiverParameter!!)
}
override fun IrExpression.useAsExtensionReceiver(function: CallableDescriptor): IrExpression {
return this.useAsArgument(function.original.extensionReceiverParameter!!)
}
override fun IrExpression.useAsValueArgument(parameter: ValueParameterDescriptor): IrExpression {
val function = parameter.containingDeclaration
return this.useAsArgument(function.original.valueParameters[parameter.index])
}
override fun IrExpression.useForField(field: PropertyDescriptor): IrExpression {
@@ -1,7 +1,7 @@
package org.jetbrains.kotlin.backend.konan.lower
import org.jetbrains.kotlin.backend.common.DeclarationContainerLoweringPass
import org.jetbrains.kotlin.backend.common.lower.createFunctionIrGenerator
import org.jetbrains.kotlin.backend.common.lower.createFunctionIrBuilder
import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.descriptors.*
import org.jetbrains.kotlin.backend.konan.ir.*
@@ -136,8 +136,8 @@ private class CallableReferencesUnbinder(val lower: CallableReferenceLowering,
// Create new function descriptor:
val newDescriptor = createSimpleFunctionImplTargetDescriptor(descriptor, unboundParams)
val generator = lower.context.createFunctionIrGenerator(newDescriptor)
val blockBody = generator.irBlockBody(startOffset, endOffset) {
val builder = lower.context.createFunctionIrBuilder(newDescriptor)
val blockBody = builder.irBlockBody(startOffset, endOffset) {
val boundArgsGet = irCall(simpleFunctionImplBoundArgsGetter).apply {
dispatchReceiver = irGet(newDescriptor.valueParameters[0])
}
@@ -2,14 +2,15 @@ package org.jetbrains.kotlin.backend.konan.lower
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.backend.common.DeclarationContainerLoweringPass
import org.jetbrains.kotlin.backend.common.lower.createFunctionIrGenerator
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.createFunctionIrBuilder
import org.jetbrains.kotlin.backend.common.lower.irBlockBody
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.KonanPlatform
import org.jetbrains.kotlin.backend.konan.ir.ir2string
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
import org.jetbrains.kotlin.descriptors.impl.ClassConstructorDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
import org.jetbrains.kotlin.ir.IrElement
@@ -18,10 +19,11 @@ import org.jetbrains.kotlin.ir.declarations.IrDeclarationContainer
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOriginImpl
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
import org.jetbrains.kotlin.ir.util.transformFlat
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
@@ -49,6 +51,11 @@ class DefaultParameterStubGenerator internal constructor(val context: Context):
private fun lower(irFunction: IrFunction): List<IrFunction> {
val bodies = mutableListOf<IrExpressionBody>()
val functionDescriptor = irFunction.descriptor
if (hasNotDefaultParameters(functionDescriptor))
return irFunction.singletonList()
irFunction.acceptChildrenVoid(object:IrElementVisitorVoid{
override fun visitExpressionBody(body: IrExpressionBody) {
bodies.add(body)
@@ -59,21 +66,29 @@ class DefaultParameterStubGenerator internal constructor(val context: Context):
}
})
val functionDescriptor = irFunction.descriptor
log("detected ${functionDescriptor.name.asString()} has got #${bodies.size} default expressions")
functionDescriptor.overriddenDescriptors.forEach { context.log("DEFAULT-REPLACER: $it") }
if (bodies.isNotEmpty()) {
val (descriptor, mask, extension, dispatch) = functionDescriptor.generateDefaultsDescriptor()
val generator = context.createFunctionIrGenerator(descriptor)
val builder = generator.createIrBuilder()
builder.apply {
startOffset = irFunction.startOffset
endOffset = irFunction.endOffset
}
val body = generator.irBlockBody {
val description = functionDescriptor.getOrCreateDefaultsDescription(context)
val builder = context.createFunctionIrBuilder(description.function)
val body = builder.irBlockBody(irFunction) {
val params = mutableListOf<VariableDescriptor>()
val variables = mutableMapOf<VariableDescriptor, VariableDescriptor>()
val newExtensionReceiver =
if (description.function.extensionReceiverParameter == null) {
null
} else {
IrGetValueImpl(
startOffset = irFunction.startOffset,
endOffset = irFunction.endOffset,
descriptor = description.function.extensionReceiverParameter!!,
origin = null
)
}
for (valueParameter in functionDescriptor.valueParameters) {
val parameterDescriptor = descriptor.valueParameters[valueParameter.index]
val parameterDescriptor = description.function.valueParameters[valueParameter.index]
if (valueParameter.hasDefaultValue()) {
val variable = scope.createTemporaryVariable(
irExpression = nullConst(valueParameter.type)!!,
@@ -82,13 +97,15 @@ class DefaultParameterStubGenerator internal constructor(val context: Context):
params.add(variableDescriptor)
+variable
val condition = irNotEquals(irCall(intAnd).apply {
dispatchReceiver = irGet(mask)
dispatchReceiver = irGet(description.mask)
putValueArgument(0, irInt(1 shl valueParameter.index))
}, irInt(0))
val exprBody = getDefaultParameterExpressionBody(irFunction, valueParameter)
/* Use previously calculated values in next expression. */
exprBody.expression.transformChildrenVoid(object:IrElementTransformerVoid() {
override fun visitGetValue(expression: IrGetValue): IrExpression {
if (expression.descriptor == functionDescriptor.extensionReceiverParameter)
return newExtensionReceiver!!
if (!variables.containsKey(expression.descriptor))
return expression
return irGet(variables[expression.descriptor] as VariableDescriptor)
@@ -97,36 +114,49 @@ class DefaultParameterStubGenerator internal constructor(val context: Context):
/* Mapping calculated values with its origin variables. */
variables.put(valueParameter, variableDescriptor)
+irIfThenElse(
type = KonanPlatform.builtIns.unitType,
type = KonanPlatform.builtIns.unitType,
condition = condition,
thenPart = irSetVar(variableDescriptor, exprBody.expression),
elsePart = irSetVar(variableDescriptor, irGet(parameterDescriptor)))
thenPart = irSetVar(variableDescriptor, exprBody.expression),
elsePart = irSetVar(variableDescriptor, irGet(parameterDescriptor)))
} else {
params.add(parameterDescriptor)
}
}
+ irReturn(irCall(functionDescriptor).apply {
if (functionDescriptor.dispatchReceiverParameter != null) {
dispatchReceiver = irGet(dispatch!!)
}
if (functionDescriptor.extensionReceiverParameter != null) {
extensionReceiver = irGet(extension!!)
}
params.forEachIndexed { i, variable ->
putValueArgument(i, irGet(variable))
}
})
if (functionDescriptor !is ClassConstructorDescriptor) {
+irReturn(irCall(functionDescriptor).apply {
if (functionDescriptor.dispatchReceiverParameter != null) {
dispatchReceiver = irThis()
}
if (functionDescriptor.extensionReceiverParameter != null) {
extensionReceiver = newExtensionReceiver
}
params.forEachIndexed { i, variable ->
putValueArgument(i, irGet(variable))
}
})
} else {
+ irReturn(IrDelegatingConstructorCallImpl(
startOffset = irFunction.startOffset,
endOffset = irFunction.endOffset,
descriptor = functionDescriptor
).apply {
params.forEachIndexed { i, variable ->
putValueArgument(i, irGet(variable))
}
})
}
}
// TODO: replace irFunction with new one without expression bodies.
return listOf(irFunction, IrFunctionImpl(
irFunction.startOffset ,
irFunction.startOffset,
irFunction.endOffset,
DECLARATION_ORIGIN_FUNCTION_FOR_DEFAULT_PARAMETER,
descriptor, body))
description.function, body))
}
return irFunction.singletonList()
}
private fun log(msg:String) = context.log("DEFAULT-REPLACER: $msg")
}
private fun getDefaultParameterExpressionBody(irFunction: IrFunction, valueParameter: ValueParameterDescriptor):IrExpressionBody {
@@ -149,81 +179,143 @@ private fun nullConst(type: KotlinType): IrExpression? {
class DefaultParameterInjector internal constructor(val context: Context): BodyLoweringPass {
override fun lower(irBody: IrBody) {
irBody.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall): IrExpression {
super.visitDelegatingConstructorCall(expression)
val descriptor = expression.descriptor
if (hasNotDefaultParameters(descriptor))
return expression
val argumentsCount = argumentCount(expression)
if (argumentsCount == descriptor.valueParameters.size)
return expression
val (desc, params) = parametersForCall(expression)
return IrDelegatingConstructorCallImpl(
startOffset = irBody.startOffset,
endOffset = irBody.endOffset,
descriptor = desc.function as ClassConstructorDescriptor)
.apply {
params.forEach {
log("call::params@${it.first.index}/${it.first.name.asString()}: ${ir2string(it.second)}")
putValueArgument(it.first.index, it.second)
}
}
}
override fun visitCall(expression: IrCall): IrExpression {
super.visitCall(expression)
val descriptor = expression.descriptor
if (descriptor.valueParameters.none{it.hasDefaultValue()})
val functionDescriptor = expression.descriptor as FunctionDescriptor
if (hasNotDefaultParameters(functionDescriptor))
return expression
val argumentsCount = argumentCount(expression)
if (argumentsCount == functionDescriptor.valueParameters.size)
return expression
val (desc, params) = parametersForCall(expression)
return IrCallImpl(
startOffset = irBody.startOffset,
endOffset = irBody.endOffset,
type = desc.function.returnType!!,
descriptor = desc.function,
typeArguments = null)
.apply {
params.forEach {
log("call::params@${it.first.index}/${it.first.name.asString()}: ${ir2string(it.second)}")
putValueArgument(it.first.index, it.second)
}
expression.extensionReceiver?.apply{
extensionReceiver = expression.extensionReceiver
}
expression.dispatchReceiver?.apply {
dispatchReceiver = expression.dispatchReceiver
}
log("call::extension@: ${ir2string(expression.extensionReceiver)}")
log("call::dispatch@: ${ir2string(expression.dispatchReceiver)}")
}
}
private fun parametersForCall(expression: IrMemberAccessExpression): Pair<DefaultParameterDescription, List<Pair<ValueParameterDescriptor, IrExpression?>>> {
var maskValue = 0
val rawDescriptor = expression.descriptor as FunctionDescriptor
val descriptor = rawDescriptor.overriddenDescriptors.firstOrNull()?:rawDescriptor
val desc = descriptor.getOrCreateDefaultsDescription(context)
descriptor.valueParameters.forEach {
log("descriptor::${descriptor.name.asString()}#${it.index}: ${it.name.asString()}")
}
val params = descriptor.valueParameters.mapIndexed { i, _ ->
val valueArgument = expression.getValueArgument(i)
if (valueArgument == null) maskValue = maskValue or (1 shl i)
val valueParameterDescriptor = desc.function.valueParameters[i]
val pair = valueParameterDescriptor to (valueArgument ?: nullConst(valueParameterDescriptor.type))
return@mapIndexed pair
} + (desc.mask to IrConstImpl<Int>(
startOffset = irBody.startOffset,
endOffset = irBody.endOffset,
type = KonanPlatform.builtIns.intType,
kind = IrConstKind.Int,
value = maskValue))
params.forEach {
log("descriptor::${desc.function.name.asString()}#${it.first.index}: ${it.first.name.asString()}")
}
return Pair(desc, params)
}
private fun argumentCount(expression: IrMemberAccessExpression): Int {
var argumentsCount = 0
expression.acceptChildrenVoid(object:IrElementVisitorVoid{
expression.acceptChildrenVoid(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
argumentsCount++
}
})
if (descriptor.dispatchReceiverParameter != null || descriptor.extensionReceiverParameter != null)
val descriptor = expression.descriptor
if (descriptor !is ConstructorDescriptor && (descriptor.dispatchReceiverParameter != null || descriptor.extensionReceiverParameter != null))
argumentsCount--
if (argumentsCount == descriptor.valueParameters.size)
return expression
var maskValue = 0
val functionDescriptor = descriptor as FunctionDescriptor
val (defaultFunctiondescriptor, mask, extension, dispatch) = functionDescriptor.generateDefaultsDescriptor()
val params = descriptor.valueParameters.mapIndexed { i, it ->
if (expression.getValueArgument(i) == null) maskValue = maskValue or (1 shl i)
val valueParameterDescriptor = defaultFunctiondescriptor.valueParameters[i]
return@mapIndexed valueParameterDescriptor to (expression.getValueArgument(i) ?: nullConst(valueParameterDescriptor.type))
} + (mask to IrConstImpl<Int>(
startOffset = irBody.startOffset,
endOffset = irBody.endOffset,
type = KonanPlatform.builtIns.intType,
kind = IrConstKind.Int,
value = maskValue))
return IrCallImpl(
startOffset = irBody.startOffset,
endOffset = irBody.endOffset,
type = descriptor.returnType!!,
descriptor = defaultFunctiondescriptor,
typeArguments = null)
.apply {
params.forEach {
putValueArgument(it.first.index, it.second)
}
extension?.apply {
putValueArgument(extension.index, expression.extensionReceiver)
}
dispatch?.apply {
putValueArgument(dispatch.index, expression.dispatchReceiver)
}
}
return argumentsCount
}
})
}
private fun log(msg: String) = context.log("DEFAULT-INJECTOR: $msg")
}
private fun hasNotDefaultParameters(descriptor: CallableDescriptor) = descriptor.valueParameters.none { it.hasDefaultValue() }
val intDesctiptor = DescriptorUtils.getClassDescriptorForType(KonanPlatform.builtIns.intType)
val intAnd = DescriptorUtils.getFunctionByName(intDesctiptor.unsubstitutedMemberScope, Name.identifier("and"))
data class DefaultParameterDescriptor(val function: FunctionDescriptor, val mask:ValueParameterDescriptor,
val extensionReceiver:ValueParameterDescriptor?, val dispatchReceiver: ValueParameterDescriptor?)
data class DefaultParameterDescription(val function: FunctionDescriptor, val mask:ValueParameterDescriptor,
val hasExtensionReceiver:Boolean, val hasDispatchReceiver: Boolean)
fun FunctionDescriptor.generateDefaultsDescriptor():DefaultParameterDescriptor {
val name = Name.identifier("${this.name.asString()}\$default")
val descriptor = SimpleFunctionDescriptorImpl.create(this.containingDeclaration, Annotations.EMPTY,
name, CallableMemberDescriptor.Kind.SYNTHESIZED, SourceElement.NO_SOURCE)
val maskVariable = valueParameter(descriptor, valueParameters.size, "__\$mask\$__", KonanPlatform.builtIns.intType)
var extensionReceiver:ValueParameterDescriptor? = null
var index = valueParameters.size + 1
extensionReceiverParameter?.let {
extensionReceiver = valueParameter(descriptor, index++, "__\$ext_receiver\$__", extensionReceiverParameter!!.type)
private fun FunctionDescriptor.getOrCreateDefaultsDescription(context: Context): DefaultParameterDescription {
return context.ir.defaultParameterDescriptions.getOrPut(this) {
this.generateDefaultsDescription(context)
}
var dispatchReceiver:ValueParameterDescriptor? = null
dispatchReceiverParameter?.let {
dispatchReceiver = valueParameter(descriptor, index++, "__\$dispatch_receiver\$__", dispatchReceiverParameter!!.type)
}
private fun FunctionDescriptor.generateDefaultsDescription(context: Context): DefaultParameterDescription {
val descriptor = when (this){
is ConstructorDescriptor -> DefaultParameterClassConstructorDescriptor(
containingDeclaration = this.containingDeclaration as ClassDescriptor,
annotations = annotations,
original = null,
isPrimary = false,
source = SourceElement.NO_SOURCE,
kind = CallableMemberDescriptor.Kind.SYNTHESIZED)
is FunctionDescriptor -> SimpleFunctionDescriptorImpl.create(
/* containingDeclaration = */ this.containingDeclaration,
/* annotations = */ Annotations.EMPTY,
/* name = */ name,
/* kind = */ CallableMemberDescriptor.Kind.SYNTHESIZED,
/* source = */ SourceElement.NO_SOURCE)
else -> TODO("FIXME!!!")
}
val parameterList = mutableListOf(*valueParameters.map{
if (it.hasDefaultValue()) ValueParameterDescriptorImpl(
val parameterList = mutableListOf<ValueParameterDescriptor>()
val maskVariable = valueParameter(descriptor, valueParameters.size , "__\$mask\$__", KonanPlatform.builtIns.intType)
parameterList += valueParameters.map{
ValueParameterDescriptorImpl(
containingDeclaration = it.containingDeclaration,
original = it.original,
index = it.index,
@@ -235,21 +327,22 @@ fun FunctionDescriptor.generateDefaultsDescriptor():DefaultParameterDescriptor {
isNoinline = it.isNoinline,
varargElementType = it.varargElementType,
source = it.source)
else it
}.toTypedArray())
}
parameterList.add(maskVariable)
if (extensionReceiver != null) parameterList.add(extensionReceiver)
if (dispatchReceiver != null) parameterList.add(dispatchReceiver)
descriptor.initialize(
/* receiverParameterType = */ null,
/* dispatchReceiverParameterType = */ null,
/* receiverParameterType = */ extensionReceiverParameter?.type,
/* dispatchReceiverParameter = */ dispatchReceiverParameter,
/* typeParameters = */ typeParameters,
/* unsubstitutedValueParameters = */ parameterList,
/* unsubstitutedReturnType = */ returnType,
/* modality = */ this.modality,
/* modality = */ Modality.FINAL,
/* visibility = */ this.visibility)
return DefaultParameterDescriptor(descriptor, maskVariable, extensionReceiver, dispatchReceiver)
return DefaultParameterDescription(
function = descriptor,
mask = maskVariable,
hasDispatchReceiver = (extensionReceiverParameter != null),
hasExtensionReceiver = (dispatchReceiverParameter != null))
}
private fun valueParameter(descriptor: FunctionDescriptor, index: Int, name: String, type: KotlinType):ValueParameterDescriptor {
@@ -266,4 +359,21 @@ private fun valueParameter(descriptor: FunctionDescriptor, index: Int, name: Str
varargElementType = null,
source = SourceElement.NO_SOURCE
)
}
/**
* This descriptor overrides name property, for class constructor : instead of <init> -> <init>$defeult symbol.
*/
class DefaultParameterClassConstructorDescriptor(
containingDeclaration: ClassDescriptor,
original: ConstructorDescriptor?,
annotations: Annotations,
isPrimary: Boolean,
kind: CallableMemberDescriptor.Kind,
source: SourceElement
) : ClassConstructorDescriptorImpl(containingDeclaration, original, annotations, isPrimary, kind, source) {
override fun getName(): Name {
return Name.identifier("${super.getName().asString()}\$default")
}
}
@@ -0,0 +1,600 @@
package org.jetbrains.kotlin.backend.konan.lower
import org.jetbrains.kotlin.backend.common.ClassLoweringPass
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.runOnFilePostfix
import org.jetbrains.kotlin.backend.jvm.descriptors.createValueParameter
import org.jetbrains.kotlin.backend.jvm.descriptors.initialize
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.KonanPlatform
import org.jetbrains.kotlin.backend.konan.descriptors.getKonanInternalFunctions
import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.ClassConstructorDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.ReceiverParameterDescriptorImpl
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.util.dump
import org.jetbrains.kotlin.ir.util.transform
import org.jetbrains.kotlin.ir.util.transformFlat
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.descriptorUtil.classValueType
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.utils.addToStdlib.singletonList
import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptyList
internal data class LoweredSyntheticFunction(val functionDescriptor: FunctionDescriptor, val containingClass: ClassDescriptor)
internal data class LoweredEnumEntry(val implObject: ClassDescriptor, val valuesProperty: PropertyDescriptor,
val itemGetter: FunctionDescriptor, val entriesMap: Map<Name, Int>)
internal class EnumUsageLowering(val context: Context,
val loweredFunctions: Map<FunctionDescriptor, LoweredSyntheticFunction>,
val loweredEnumEntries: Map<ClassDescriptor, LoweredEnumEntry>)
: IrElementTransformerVoid(), FileLoweringPass {
override fun lower(irFile: IrFile) {
visitFile(irFile)
}
override fun visitGetEnumValue(expression: IrGetEnumValue): IrExpression {
val enumClassDescriptor = expression.descriptor.containingDeclaration as ClassDescriptor
enumClassDescriptor.companionObjectDescriptor
return loadEnumEntry(expression.startOffset, expression.endOffset, enumClassDescriptor, expression.descriptor.name)
}
override fun visitGetObjectValue(expression: IrGetObjectValue): IrExpression {
if(expression.descriptor.kind != ClassKind.ENUM_ENTRY)
return super.visitGetObjectValue(expression)
val enumClassDescriptor = expression.descriptor.containingDeclaration as ClassDescriptor
return loadEnumEntry(expression.startOffset, expression.endOffset, enumClassDescriptor, expression.descriptor.name)
}
private fun loadEnumEntry(startOffset: Int, endOffset: Int, enumClassDescriptor: ClassDescriptor, name: Name): IrExpression {
val loweredEnumEntry = loweredEnumEntries[enumClassDescriptor]!!
val implObject = loweredEnumEntry.implObject
val implObjectGetter = IrGetObjectValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, implObject.classValueType!!, implObject)
val valuesGetter = IrGetFieldImpl(startOffset, endOffset, loweredEnumEntry.valuesProperty).apply {
receiver = implObjectGetter
}
val ordinal = loweredEnumEntry.entriesMap[name]!!
return IrCallImpl(startOffset, endOffset, loweredEnumEntry.itemGetter).apply {
dispatchReceiver = valuesGetter
putValueArgument(0, IrConstImpl.int(startOffset, endOffset, enumClassDescriptor.module.builtIns.intType, ordinal))
}
}
override fun visitCall(expression: IrCall): IrExpression {
val loweredFunction = loweredFunctions[expression.descriptor] ?: return super.visitCall(expression)
return IrCallImpl(expression.startOffset, expression.endOffset, loweredFunction.functionDescriptor).apply {
val containingClass = loweredFunction.containingClass
dispatchReceiver = IrGetObjectValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, containingClass.classValueType!!, containingClass)
expression.descriptor.valueParameters.forEach { p -> putValueArgument(p, expression.getValueArgument(p)) }
}
}
}
internal class EnumClassLowering(val context: Context) : ClassLoweringPass {
val loweredFunctions = mutableMapOf<FunctionDescriptor, LoweredSyntheticFunction>()
val loweredEnumEntries = mutableMapOf<ClassDescriptor, LoweredEnumEntry>()
fun run(irFile: IrFile) {
runOnFilePostfix(irFile)
EnumUsageLowering(context, loweredFunctions, loweredEnumEntries).lower(irFile)
}
override fun lower(irClass: IrClass) {
val descriptor = irClass.descriptor
if (descriptor.kind != ClassKind.ENUM_CLASS)
return
EnumClassTransformer(irClass).run()
}
private interface EnumConstructorCallTransformer {
fun transform(enumConstructorCall: IrEnumConstructorCall): IrExpression
fun transform(delegatingConstructorCall: IrDelegatingConstructorCall): IrExpression
}
private inner class EnumClassTransformer(val irClass: IrClass) {
private val enumEntryOrdinals = mutableMapOf<ClassDescriptor, Int>()
private val loweredEnumConstructors = mutableMapOf<ClassConstructorDescriptor, ClassConstructorDescriptor>()
private val defaultEnumEntryConstructors = mutableMapOf<ClassConstructorDescriptor, ClassConstructorDescriptor>()
private val loweredEnumConstructorParameters = mutableMapOf<ValueParameterDescriptor, ValueParameterDescriptor>()
private lateinit var valuesFieldDescriptor: PropertyDescriptor
private lateinit var valuesFunctionDescriptor: FunctionDescriptor
private lateinit var valueOfFunctionDescriptor: FunctionDescriptor
fun run() {
assignOrdinalsToEnumEntries()
lowerEnumConstructors(irClass)
lowerEnumEntriesClasses()
val defaultClass = createDefaultClassForEnumEntries()
lowerEnumClassBody()
if (defaultClass != null)
irClass.declarations.add(defaultClass)
createImplObject()
}
private fun assignOrdinalsToEnumEntries() {
var ordinal = 0
irClass.declarations.forEach {
if (it is IrEnumEntry) {
enumEntryOrdinals.put(it.descriptor, ordinal)
ordinal++
}
}
}
private fun lowerEnumEntriesClasses() {
irClass.declarations.transformFlat { declaration ->
if (declaration is IrEnumEntry) {
declaration.singletonList() + lowerEnumEntryClass(declaration.correspondingClass).singletonOrEmptyList()
} else null
}
}
private fun lowerEnumEntryClass(enumEntryClass: IrClass?): IrClass? {
if (enumEntryClass == null) return null
lowerEnumConstructors(enumEntryClass)
return enumEntryClass
}
private fun createDefaultClassForEnumEntries(): IrClass? {
if (!irClass.declarations.any({ it is IrEnumEntry && it.correspondingClass == null })) return null
val descriptor = irClass.descriptor
val defaultClassDescriptor = ClassDescriptorImpl(descriptor, "DEFAULT".synthesizedName, Modality.FINAL,
ClassKind.CLASS, descriptor.defaultType.singletonList(), SourceElement.NO_SOURCE, false)
val defaultClass = IrClassImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, IrDeclarationOrigin.DEFINED, defaultClassDescriptor)
val constructors = mutableSetOf<ClassConstructorDescriptor>()
descriptor.constructors.forEach {
val loweredEnumConstructor = loweredEnumConstructors[it]!!
val (constructorDescriptor, constructor) = createSimpleDelegatingConstructor(defaultClassDescriptor, loweredEnumConstructor)
constructors.add(constructorDescriptor)
defaultClass.declarations.add(constructor)
defaultEnumEntryConstructors.put(loweredEnumConstructor, constructorDescriptor)
}
val memberScope = MemberScope.Empty
defaultClassDescriptor.initialize(memberScope, constructors, null)
return defaultClass
}
private fun createImplObject() {
val descriptor = irClass.descriptor
val implObjectDescriptor = ClassDescriptorImpl(descriptor, "OBJECT".synthesizedName, Modality.FINAL,
ClassKind.OBJECT, KonanPlatform.builtIns.anyType.singletonList(), SourceElement.NO_SOURCE, false)
val implObject = IrClassImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, IrDeclarationOrigin.DEFINED, implObjectDescriptor)
val enumEntries = mutableListOf<IrEnumEntry>()
var i = 0
while (i < irClass.declarations.size) {
val declaration = irClass.declarations[i]
var delete = false
when (declaration) {
is IrEnumEntry -> {
enumEntries.add(declaration)
delete = true
}
is IrFunction -> {
var copiedDescriptor = tryCopySyntheticBodyDeclaration(implObjectDescriptor, declaration, IrSyntheticBodyKind.ENUM_VALUES)
if(copiedDescriptor != null)
{
valuesFunctionDescriptor = copiedDescriptor
delete = true
}
copiedDescriptor = tryCopySyntheticBodyDeclaration(implObjectDescriptor, declaration, IrSyntheticBodyKind.ENUM_VALUEOF)
if(copiedDescriptor != null)
{
valueOfFunctionDescriptor = copiedDescriptor
delete = true
}
}
}
if (delete)
irClass.declarations.removeAt(i)
else
++i
}
val memberScope = MemberScope.Empty
val constructorOfAny = irClass.descriptor.module.builtIns.any.constructors.first()
val (constructorDescriptor, constructor) = createSimpleDelegatingConstructor(implObjectDescriptor, constructorOfAny)
implObjectDescriptor.initialize(memberScope, setOf(constructorDescriptor), constructorDescriptor)
implObject.declarations.add(constructor)
implObject.declarations.add(createSyntheticValuesFieldDeclaration(implObjectDescriptor, enumEntries))
implObject.declarations.add(createSyntheticValuesMethodDeclaration(implObjectDescriptor))
implObject.declarations.add(createSyntheticValueOfMethodDeclaration(implObjectDescriptor))
irClass.declarations.add(implObject)
}
private fun tryCopySyntheticBodyDeclaration(implObjectDescriptor: ClassDescriptor,
declaration: IrFunction,
kind: IrSyntheticBodyKind): FunctionDescriptor? {
if (!declaration.body.let { it is IrSyntheticBody && it.kind == kind })
return null
val newDescriptor = declaration.descriptor
.newCopyBuilder()
.setOwner(implObjectDescriptor)
.setName(declaration.descriptor.name.identifier.synthesizedName)
.setDispatchReceiverParameter(implObjectDescriptor.thisAsReceiverParameter)
.build()!!
loweredFunctions.put(declaration.descriptor, LoweredSyntheticFunction(newDescriptor, implObjectDescriptor))
return newDescriptor
}
private fun createSimpleDelegatingConstructor(classDescriptor: ClassDescriptor,
superConstructorDescriptor: ClassConstructorDescriptor)
: Pair<ClassConstructorDescriptor, IrConstructor> {
val constructorDescriptor = ClassConstructorDescriptorImpl.createSynthesized(
classDescriptor,
Annotations.EMPTY,
superConstructorDescriptor.isPrimary,
SourceElement.NO_SOURCE)
val valueParameters = superConstructorDescriptor.valueParameters.map {
it.copy(constructorDescriptor, it.name, it.index)
}
constructorDescriptor.initialize(valueParameters, superConstructorDescriptor.visibility)
constructorDescriptor.returnType = superConstructorDescriptor.returnType
val body = IrBlockBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET,
listOf(
IrDelegatingConstructorCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, superConstructorDescriptor).apply {
valueParameters.forEachIndexed { idx, parameter ->
putValueArgument(idx, IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, parameter))
}
},
IrInstanceInitializerCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, classDescriptor)
)
)
val constructor = IrConstructorImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, IrDeclarationOrigin.DEFINED, constructorDescriptor, body)
return Pair(constructorDescriptor, constructor)
}
private fun createSyntheticValuesFieldDeclaration(implObjectDescriptor: ClassDescriptor,
enumEntries: List<IrEnumEntry>): IrFieldImpl {
val valuesArrayType = context.builtIns.getArrayType(Variance.INVARIANT, irClass.descriptor.defaultType)
valuesFieldDescriptor = createSyntheticValuesFieldDescriptor(implObjectDescriptor, valuesArrayType)
val getter = genericArrayType.unsubstitutedMemberScope.getContributedFunctions(Name.identifier("get"), NoLookupLocation.FROM_BACKEND).single()
val typeParameterT = genericArrayType.declaredTypeParameters[0]
val enumClassType = irClass.descriptor.defaultType
val typeSubstitutor = TypeSubstitutor.create(mapOf(typeParameterT.typeConstructor to TypeProjectionImpl(enumClassType)))
val substitutedValueOf = getter.substitute(typeSubstitutor)!!
val entriesMap = enumEntries.associateBy({ it -> it.descriptor.name }, { it -> enumEntryOrdinals[it.descriptor]!! }).toMap()
val loweredEnumEntry = LoweredEnumEntry(implObjectDescriptor, valuesFieldDescriptor, substitutedValueOf, entriesMap)
loweredEnumEntries.put(irClass.descriptor, loweredEnumEntry)
val irValuesInitializer = createArrayOfExpression(irClass.descriptor.defaultType, enumEntries.map { it.initializerExpression })
val irField = IrFieldImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, IrDeclarationOrigin.DEFINED,
valuesFieldDescriptor,
IrExpressionBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, irValuesInitializer))
return irField
}
private fun createSyntheticValuesFieldDescriptor(implObjectDescriptor: ClassDescriptor, valuesArrayType: SimpleType): PropertyDescriptorImpl {
val receiver = ReceiverParameterDescriptorImpl(implObjectDescriptor, ImplicitClassReceiver(implObjectDescriptor))
return PropertyDescriptorImpl.create(implObjectDescriptor, Annotations.EMPTY, Modality.FINAL, Visibilities.PRIVATE,
false, "VALUES".synthesizedName, CallableMemberDescriptor.Kind.SYNTHESIZED, irClass.descriptor.source,
false, false, false, false, false, false).initialize(valuesArrayType, dispatchReceiverParameter = receiver)
}
private val kotlinPackage = context.irModule!!.descriptor.getPackage(FqName("kotlin"))
private val genericArrayOfFun = kotlinPackage.memberScope.getContributedFunctions(Name.identifier("arrayOf"), NoLookupLocation.FROM_BACKEND).first()
private val genericValueOfFun = context.builtIns.getKonanInternalFunctions("valueOfForEnum").single()
private val genericValuesFun = context.builtIns.getKonanInternalFunctions("valuesForEnum").single()
private val genericArrayType = kotlinPackage.memberScope.getContributedClassifier(Name.identifier("Array"), NoLookupLocation.FROM_BACKEND) as ClassDescriptor
private fun createArrayOfExpression(arrayElementType: KotlinType, arrayElements: List<IrExpression>): IrExpression {
val typeParameter0 = genericArrayOfFun.typeParameters[0]
val typeSubstitutor = TypeSubstitutor.create(mapOf(typeParameter0.typeConstructor to TypeProjectionImpl(arrayElementType)))
val substitutedArrayOfFun = genericArrayOfFun.substitute(typeSubstitutor)!!
val typeArguments = mapOf(typeParameter0 to arrayElementType)
val valueParameter0 = substitutedArrayOfFun.valueParameters[0]
val arg0VarargType = valueParameter0.type
val arg0VarargElementType = valueParameter0.varargElementType!!
val arg0 = IrVarargImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, arg0VarargType, arg0VarargElementType, arrayElements)
return IrCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, substitutedArrayOfFun, typeArguments).apply {
putValueArgument(0, arg0)
}
}
private fun createSyntheticValuesMethodDeclaration(companionObjectDescriptor: ClassDescriptor): IrFunction {
val typeParameterT = genericValuesFun.typeParameters[0]
val enumClassType = irClass.descriptor.defaultType
val typeSubstitutor = TypeSubstitutor.create(mapOf(typeParameterT.typeConstructor to TypeProjectionImpl(enumClassType)))
val substitutedValueOf = genericValuesFun.substitute(typeSubstitutor)!!
val irValuesCall = IrCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, substitutedValueOf, mapOf(typeParameterT to enumClassType))
.apply {
val receiver = IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, companionObjectDescriptor.thisAsReceiverParameter)
putValueArgument(0, IrGetFieldImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, valuesFieldDescriptor, receiver))
}
val body = IrBlockBodyImpl(
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
listOf(IrReturnImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, valuesFunctionDescriptor, irValuesCall))
)
return IrFunctionImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, IrDeclarationOrigin.DEFINED, valuesFunctionDescriptor, body)
}
private fun createSyntheticValueOfMethodDeclaration(companionObjectDescriptor: ClassDescriptor): IrFunction {
val typeParameterT = genericValueOfFun.typeParameters[0]
val enumClassType = irClass.descriptor.defaultType
val typeSubstitutor = TypeSubstitutor.create(mapOf(typeParameterT.typeConstructor to TypeProjectionImpl(enumClassType)))
val substitutedValueOf = genericValueOfFun.substitute(typeSubstitutor)!!
val irValueOfCall = IrCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, substitutedValueOf, mapOf(typeParameterT to enumClassType))
.apply {
putValueArgument(0, IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, valueOfFunctionDescriptor.valueParameters[0]))
val receiver = IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, companionObjectDescriptor.thisAsReceiverParameter)
putValueArgument(1, IrGetFieldImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, valuesFieldDescriptor, receiver))
}
val body = IrBlockBodyImpl(
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
listOf(IrReturnImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, valueOfFunctionDescriptor, irValueOfCall))
)
return IrFunctionImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, IrDeclarationOrigin.DEFINED, valueOfFunctionDescriptor, body)
}
private fun lowerEnumConstructors(irClass: IrClass) {
irClass.declarations.transform { declaration ->
if (declaration is IrConstructor)
transformEnumConstructor(declaration)
else
declaration
}
}
private fun transformEnumConstructor(enumConstructor: IrConstructor): IrConstructor {
val constructorDescriptor = enumConstructor.descriptor
val loweredConstructorDescriptor = lowerEnumConstructor(constructorDescriptor)
val loweredEnumConstructor = IrConstructorImpl(
enumConstructor.startOffset, enumConstructor.endOffset, enumConstructor.origin,
loweredConstructorDescriptor,
enumConstructor.body!! // will be transformed later
)
return loweredEnumConstructor
}
private fun lowerEnumConstructor(constructorDescriptor: ClassConstructorDescriptor): ClassConstructorDescriptor {
val loweredConstructorDescriptor = ClassConstructorDescriptorImpl.createSynthesized(
constructorDescriptor.containingDeclaration,
constructorDescriptor.annotations,
constructorDescriptor.isPrimary,
constructorDescriptor.source
)
val valueParameters =
listOf(
loweredConstructorDescriptor.createValueParameter(0, "name", context.builtIns.stringType),
loweredConstructorDescriptor.createValueParameter(1, "ordinal", context.builtIns.intType)
) +
constructorDescriptor.valueParameters.map {
lowerConstructorValueParameter(loweredConstructorDescriptor, it)
}
loweredConstructorDescriptor.initialize(valueParameters, Visibilities.PROTECTED)
loweredConstructorDescriptor.returnType = constructorDescriptor.returnType
loweredEnumConstructors[constructorDescriptor] = loweredConstructorDescriptor
return loweredConstructorDescriptor
}
private fun lowerConstructorValueParameter(
loweredConstructorDescriptor: ClassConstructorDescriptor,
valueParameterDescriptor: ValueParameterDescriptor
): ValueParameterDescriptor {
val loweredValueParameterDescriptor = valueParameterDescriptor.copy(
loweredConstructorDescriptor,
valueParameterDescriptor.name,
valueParameterDescriptor.index + 2
)
loweredEnumConstructorParameters[valueParameterDescriptor] = loweredValueParameterDescriptor
return loweredValueParameterDescriptor
}
private fun lowerEnumClassBody() {
irClass.transformChildrenVoid(EnumClassBodyTransformer())
}
private inner class InEnumClassConstructor(val enumClassConstructor: ClassConstructorDescriptor) :
EnumConstructorCallTransformer {
override fun transform(enumConstructorCall: IrEnumConstructorCall): IrExpression {
val startOffset = enumConstructorCall.startOffset
val endOffset = enumConstructorCall.endOffset
val origin = enumConstructorCall.origin
val result = IrDelegatingConstructorCallImpl(startOffset, endOffset, enumConstructorCall.descriptor)
assert(result.descriptor.valueParameters.size == 2) {
"Enum(String, Int) constructor call expected:\n${result.dump()}"
}
val nameParameter = enumClassConstructor.valueParameters.getOrElse(0) {
throw AssertionError("No 'name' parameter in enum constructor: $enumClassConstructor")
}
val ordinalParameter = enumClassConstructor.valueParameters.getOrElse(1) {
throw AssertionError("No 'ordinal' parameter in enum constructor: $enumClassConstructor")
}
result.putValueArgument(0, IrGetValueImpl(startOffset, endOffset, nameParameter, origin))
result.putValueArgument(1, IrGetValueImpl(startOffset, endOffset, ordinalParameter, origin))
return result
}
override fun transform(delegatingConstructorCall: IrDelegatingConstructorCall): IrExpression {
val descriptor = delegatingConstructorCall.descriptor
val startOffset = delegatingConstructorCall.startOffset
val endOffset = delegatingConstructorCall.endOffset
val loweredDelegatedConstructor = loweredEnumConstructors.getOrElse(descriptor) {
throw AssertionError("Constructor called in enum entry initializer should've been lowered: $descriptor")
}
val result = IrDelegatingConstructorCallImpl(startOffset, endOffset, loweredDelegatedConstructor)
result.putValueArgument(0, IrGetValueImpl(startOffset, endOffset, enumClassConstructor.valueParameters[0]))
result.putValueArgument(1, IrGetValueImpl(startOffset, endOffset, enumClassConstructor.valueParameters[1]))
descriptor.valueParameters.forEach { valueParameter ->
val i = valueParameter.index
result.putValueArgument(i + 2, delegatingConstructorCall.getValueArgument(i))
}
return result
}
}
private abstract inner class InEnumEntry(private val enumEntry: ClassDescriptor) : EnumConstructorCallTransformer {
override fun transform(enumConstructorCall: IrEnumConstructorCall): IrExpression {
val name = enumEntry.name.asString()
val ordinal = enumEntryOrdinals[enumEntry]!!
val descriptor = enumConstructorCall.descriptor
val startOffset = enumConstructorCall.startOffset
val endOffset = enumConstructorCall.endOffset
val loweredConstructor = loweredEnumConstructors.getOrElse(descriptor) {
throw AssertionError("Constructor called in enum entry initializer should've been lowered: $descriptor")
}
val result = createConstructorCall(startOffset, endOffset, loweredConstructor)
result.putValueArgument(0, IrConstImpl.string(startOffset, endOffset, context.builtIns.stringType, name))
result.putValueArgument(1, IrConstImpl.int(startOffset, endOffset, context.builtIns.intType, ordinal))
descriptor.valueParameters.forEach { valueParameter ->
val i = valueParameter.index
result.putValueArgument(i + 2, enumConstructorCall.getValueArgument(i))
}
return result
}
override fun transform(delegatingConstructorCall: IrDelegatingConstructorCall): IrExpression {
throw AssertionError("Unexpected delegating constructor call within enum entry: $enumEntry")
}
abstract fun createConstructorCall(startOffset: Int, endOffset: Int, loweredConstructor: ClassConstructorDescriptor): IrMemberAccessExpression
}
private inner class InEnumEntryClassConstructor(enumEntry: ClassDescriptor) : InEnumEntry(enumEntry) {
override fun createConstructorCall(startOffset: Int, endOffset: Int, loweredConstructor: ClassConstructorDescriptor)
= IrDelegatingConstructorCallImpl(startOffset, endOffset, loweredConstructor)
}
private inner class InEnumEntryInitializer(enumEntry: ClassDescriptor) : InEnumEntry(enumEntry) {
override fun createConstructorCall(startOffset: Int, endOffset: Int, loweredConstructor: ClassConstructorDescriptor)
= IrCallImpl(startOffset, endOffset, defaultEnumEntryConstructors[loweredConstructor] ?: loweredConstructor)
}
private inner class EnumClassBodyTransformer : IrElementTransformerVoid() {
private var enumConstructorCallTransformer: EnumConstructorCallTransformer? = null
override fun visitEnumEntry(declaration: IrEnumEntry): IrStatement {
assert(enumConstructorCallTransformer == null) { "Nested enum entry initialization:\n${declaration.dump()}" }
enumConstructorCallTransformer = InEnumEntryInitializer(declaration.descriptor)
var result: IrEnumEntry = IrEnumEntryImpl(declaration.startOffset, declaration.endOffset, declaration.origin,
declaration.descriptor, null, declaration.initializerExpression)
result = super.visitEnumEntry(result) as IrEnumEntry
enumConstructorCallTransformer = null
return result
}
override fun visitConstructor(declaration: IrConstructor): IrStatement {
val constructorDescriptor = declaration.descriptor
val containingClass = constructorDescriptor.containingDeclaration
// TODO local (non-enum) class in enum class constructor?
val previous = enumConstructorCallTransformer
if (containingClass.kind == ClassKind.ENUM_ENTRY) {
assert(enumConstructorCallTransformer == null) { "Nested enum entry initialization:\n${declaration.dump()}" }
enumConstructorCallTransformer = InEnumEntryClassConstructor(containingClass)
} else if (containingClass.kind == ClassKind.ENUM_CLASS) {
assert(enumConstructorCallTransformer == null) { "Nested enum entry initialization:\n${declaration.dump()}" }
enumConstructorCallTransformer = InEnumClassConstructor(constructorDescriptor)
}
val result = super.visitConstructor(declaration)
enumConstructorCallTransformer = previous
return result
}
override fun visitEnumConstructorCall(expression: IrEnumConstructorCall): IrExpression {
expression.transformChildrenVoid(this)
val callTransformer = enumConstructorCallTransformer ?:
throw AssertionError("Enum constructor call outside of enum entry initialization or enum class constructor:\n" + irClass.dump())
return callTransformer.transform(expression)
}
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall): IrExpression {
expression.transformChildrenVoid(this)
if (expression.descriptor.containingDeclaration.kind == ClassKind.ENUM_CLASS) {
val callTransformer = enumConstructorCallTransformer ?:
throw AssertionError("Enum constructor call outside of enum entry initialization or enum class constructor:\n" + irClass.dump())
return callTransformer.transform(expression)
}
return expression
}
override fun visitGetValue(expression: IrGetValue): IrExpression {
val loweredParameter = loweredEnumConstructorParameters[expression.descriptor]
if (loweredParameter != null)
return IrGetValueImpl(expression.startOffset, expression.endOffset, loweredParameter, expression.origin)
else
return expression
}
}
}
}
@@ -74,13 +74,13 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoid(
return parametersNew
}
//-------------------------------------------------------------------------//
fun inlineFunction(irCall: IrCall): IrBlock {
val functionDescriptor = irCall.descriptor as FunctionDescriptor
val functionDeclaration = context.ir.moduleIndex.functions[functionDescriptor] // Get FunctionDeclaration by FunctionDescriptor.
val functionDeclaration = context.ir.originalModuleIndex
.functions[functionDescriptor] // Get FunctionDeclaration by FunctionDescriptor.
val copyFuncDeclaration = functionDeclaration!!.accept(DeepCopyIrTree(), null) as IrFunction // Create copy of the function.
val startOffset = copyFuncDeclaration.startOffset
@@ -111,11 +111,11 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoid(
override fun visitCall(expression: IrCall): IrExpression {
val functionDescriptor = expression.descriptor as FunctionDescriptor
if (!functionDescriptor.isInline) return super.visitCall(expression) // Function is not to be inlined - do nothing.
if (!functionDescriptor.isInline) return super.visitCall(expression) // Function is not to be inlined - do nothing.
val functionDeclaration = context.ir.moduleIndex.functions[functionDescriptor] // Get FunctionDeclaration by FunctionDescriptor.
if (functionDeclaration == null) return super.visitCall(expression) // Function is declared in another module.
return inlineFunction(expression) // Return newly created IrBlock instead of IrCall.
val functionDeclaration = context.ir.originalModuleIndex.functions[functionDescriptor] // Get FunctionDeclaration by FunctionDescriptor.
if (functionDeclaration == null) return super.visitCall(expression) // Function is declared in another module.
return inlineFunction(expression) // Return newly created IrBlock instead of IrCall.
}
//-------------------------------------------------------------------------//
@@ -0,0 +1,141 @@
package org.jetbrains.kotlin.backend.konan.lower
import org.jetbrains.kotlin.backend.common.ClassLoweringPass
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOriginImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrFieldImpl
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.util.dump
import org.jetbrains.kotlin.ir.util.transformFlat
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver
import org.jetbrains.kotlin.utils.addToStdlib.singletonList
internal class InnerClassLowering(val context: Context) : ClassLoweringPass {
override fun lower(irClass: IrClass) {
InnerClassTransformer(irClass).lowerInnerClass()
}
private inner class InnerClassTransformer(val irClass: IrClass) {
val classDescriptor = irClass.descriptor
lateinit var outerThisFieldDescriptor: PropertyDescriptor
fun lowerInnerClass() {
if (!irClass.descriptor.isInner) return
createOuterThisField()
lowerOuterThisReferences()
lowerConstructors()
}
object DECLARATION_ORIGIN_FIELD_FOR_OUTER_THIS :
IrDeclarationOriginImpl("FIELD_FOR_OUTER_THIS")
private fun createOuterThisField() {
outerThisFieldDescriptor = context.specialDescriptorsFactory.getOuterThisFieldDescriptor(classDescriptor)
irClass.declarations.add(IrFieldImpl(
irClass.startOffset, irClass.endOffset,
DECLARATION_ORIGIN_FIELD_FOR_OUTER_THIS,
outerThisFieldDescriptor
))
}
private fun lowerConstructors() {
irClass.declarations.transformFlat { irMember ->
if (irMember is IrConstructor)
lowerConstructor(irMember).singletonList()
else
null
}
}
private fun lowerConstructor(irConstructor: IrConstructor): IrConstructor {
val descriptor = irConstructor.descriptor
val startOffset = irConstructor.startOffset
val endOffset = irConstructor.endOffset
val dispatchReceiver = descriptor.dispatchReceiverParameter!!
val blockBody = irConstructor.body as? IrBlockBody ?: throw AssertionError("Unexpected constructor body: ${irConstructor.body}")
val instanceInitializerIndex = blockBody.statements.indexOfFirst { it is IrInstanceInitializerCall }
if (instanceInitializerIndex >= 0) {
// Initializing constructor: initialize 'this.this$0' with '$outer'.
blockBody.statements.add(
instanceInitializerIndex,
IrSetFieldImpl(
startOffset, endOffset, outerThisFieldDescriptor,
IrGetValueImpl(startOffset, endOffset, classDescriptor.thisAsReceiverParameter),
IrGetValueImpl(startOffset, endOffset, dispatchReceiver)
)
)
}
else {
// Delegating constructor: invoke old constructor with dispatch receiver '$outer'.
val delegatingConstructorCall = (blockBody.statements.find { it is IrDelegatingConstructorCall } ?:
throw AssertionError("Delegating constructor call expected: ${irConstructor.dump()}")
) as IrDelegatingConstructorCall
delegatingConstructorCall.dispatchReceiver = IrGetValueImpl(
delegatingConstructorCall.startOffset, delegatingConstructorCall.endOffset, dispatchReceiver
)
}
return irConstructor
}
private fun lowerOuterThisReferences() {
irClass.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitGetValue(expression: IrGetValue): IrExpression {
expression.transformChildrenVoid(this)
val implicitThisClass = expression.descriptor.getClassDescriptorForImplicitThis() ?:
return expression
if (implicitThisClass == classDescriptor) return expression
val startOffset = expression.startOffset
val endOffset = expression.endOffset
val origin = expression.origin
var irThis: IrExpression = IrGetValueImpl(startOffset, endOffset, classDescriptor.thisAsReceiverParameter, origin)
var innerClass = classDescriptor
while (innerClass != implicitThisClass) {
if (!innerClass.isInner) {
// Captured 'this' unrelated to inner classes nesting hierarchy, leave it as is -
// should be transformed by closures conversion.
return expression
}
val outerThisField = context.specialDescriptorsFactory.getOuterThisFieldDescriptor(innerClass)
irThis = IrGetFieldImpl(startOffset, endOffset, outerThisField, irThis, origin)
val outer = classDescriptor.containingDeclaration
innerClass = outer as? ClassDescriptor ?:
throw AssertionError("Unexpected containing declaration for inner class $innerClass: $outer")
}
return irThis
}
})
}
private fun ValueDescriptor.getClassDescriptorForImplicitThis(): ClassDescriptor? {
if (this is ReceiverParameterDescriptor) {
val receiverValue = value
if (receiverValue is ImplicitClassReceiver) {
return receiverValue.classDescriptor
}
}
return null
}
}
}
@@ -1,10 +1,12 @@
package org.jetbrains.kotlin.backend.konan.lower
import org.jetbrains.kotlin.backend.common.FunctionLoweringPass
import org.jetbrains.kotlin.backend.common.lower.createFunctionIrGenerator
import org.jetbrains.kotlin.backend.common.lower.irBlock
import org.jetbrains.kotlin.backend.common.lower.*
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.descriptors.getKonanInternalFunctions
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.IrFunction
@@ -13,6 +15,11 @@ import org.jetbrains.kotlin.ir.expressions.IrTypeOperator
import org.jetbrains.kotlin.ir.expressions.IrTypeOperatorCall
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
import org.jetbrains.kotlin.types.typeUtil.makeNullable
/**
* This lowering pass lowers some [IrTypeOperatorCall]s.
@@ -27,19 +34,83 @@ internal class TypeOperatorLowering(val context: Context) : FunctionLoweringPass
private class TypeOperatorTransformer(val context: Context, val function: FunctionDescriptor) : IrElementTransformerVoid() {
private val generator = context.createFunctionIrGenerator(function)
private val builder = context.createFunctionIrBuilder(function)
val throwClassCastException by lazy {
context.builtIns.getKonanInternalFunctions("ThrowClassCastException").single()
}
override fun visitFunction(declaration: IrFunction): IrStatement {
// ignore inner functions during this pass
return declaration
}
private fun KotlinType.erasure(): KotlinType {
val descriptor = this.constructor.declarationDescriptor
return when (descriptor) {
is ClassDescriptor -> this
is TypeParameterDescriptor -> {
val upperBound = descriptor.upperBounds.singleOrNull() ?:
TODO("$descriptor : ${descriptor.upperBounds}")
if (this.isMarkedNullable) {
// `T?`
upperBound.erasure().makeNullable()
} else {
upperBound.erasure()
}
}
else -> TODO(this.toString())
}
}
private fun lowerCast(expression: IrTypeOperatorCall): IrExpression {
builder.at(expression)
val typeOperand = expression.typeOperand.erasure()
assert (!TypeUtils.hasNullableSuperType(typeOperand)) // So that `isNullable()` <=> `isMarkedNullable`.
// TODO: consider the case when expression type is wrong e.g. due to generics-related unchecked casts.
return when {
expression.argument.type.isSubtypeOf(typeOperand) -> expression.argument
expression.argument.type.isNullable() -> {
with (builder) {
irLet(expression.argument) { argument ->
irIfThenElse(
type = expression.type,
condition = irEqeqeq(irGet(argument), irNull()),
thenPart = if (typeOperand.isMarkedNullable)
irNull()
else
irCall(throwClassCastException),
elsePart = irAs(irGet(argument), typeOperand.makeNotNullable())
)
}
}
}
typeOperand.isMarkedNullable -> builder.irAs(expression.argument, typeOperand.makeNotNullable())
typeOperand == expression.typeOperand -> expression
else -> builder.irAs(expression.argument, typeOperand)
}
}
private fun KotlinType.isNullable() = TypeUtils.isNullableType(this)
private fun lowerSafeCast(expression: IrTypeOperatorCall): IrExpression {
return generator.irBlock(expression) {
val typeOperand = expression.typeOperand.erasure()
return builder.irBlock(expression) {
+irLet(expression.argument) { variable ->
irIfThenElse(expression.type,
condition = irIs(irGet(variable), expression.typeOperand),
thenPart = irImplicitCast(irGet(variable), expression.typeOperand),
condition = irIs(irGet(variable), typeOperand),
thenPart = irImplicitCast(irGet(variable), typeOperand),
elsePart = irNull())
}
}
@@ -50,6 +121,7 @@ private class TypeOperatorTransformer(val context: Context, val function: Functi
return when (expression.operator) {
IrTypeOperator.SAFE_CAST -> lowerSafeCast(expression)
IrTypeOperator.CAST -> lowerCast(expression)
else -> expression
}
}
@@ -0,0 +1,218 @@
package org.jetbrains.kotlin.backend.konan.lower
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.backend.common.FunctionLoweringPass
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.KonanPlatform
import org.jetbrains.kotlin.backend.konan.ir.ir2string
import org.jetbrains.kotlin.backend.konan.lower.VarargInjectionLowering.Companion.kIntType
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.visitors.*
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.types.KotlinType
class VarargInjectionLowering internal constructor(val context: Context): FunctionLoweringPass {
override fun lower(irFunction: IrFunction) {
if (irFunction.body != null)
lower(irFunction.descriptor, irFunction.body!!)
}
fun noVarargArguments(descriptor: FunctionDescriptor) = descriptor.valueParameters.none { it.varargElementType != null }
val irContext = IrGeneratorContext(context.ir.irModule.irBuiltins)
private fun lower(owner:FunctionDescriptor, irBody: IrBody) {
irBody.transformChildrenVoid(object: IrElementTransformerVoid(){
override fun visitCall(expression: IrCall): IrExpression {
super.visitCall(expression)
val functionDescriptor = expression.descriptor as FunctionDescriptor
if (noVarargArguments(functionDescriptor))
return expression
val varargToBlock = blockPerVararg(expression, owner)
val scope = owner.scope()
offset(expression, scope){
val originalCall = irCall(
descriptor = expression.descriptor,
typeArguments = expression.descriptor.original.typeParameters.map { it to expression.getTypeArgument(it)!! }.toMap())
originalCall.dispatchReceiver = expression.dispatchReceiver
originalCall.extensionReceiver = expression.extensionReceiver
functionDescriptor.valueParameters.forEach {
originalCall.putValueArgument(it.index, if (varargToBlock.containsKey(it)) varargToBlock[it] else expression.getValueArgument(it))
}
return originalCall
}
}
})
}
private fun blockPerVararg(expression: IrCall, owner: FunctionDescriptor): Map<ValueParameterDescriptor, IrBlock> {
val varargArgs = mutableMapOf<ValueParameterDescriptor, IrBlock>()
val arrayConstructor = kArrayType.constructors.find { it.valueParameters.size == 1 }!!
val scope = owner.scope()
val calleeDescriptor = expression.descriptor
calleeDescriptor.valueParameters
.filter{ it.varargElementType != null && expression.getValueArgument(it) is IrVararg?}
.forEach {
val type = it.varargElementType!!
val parameterExpression = expression.getValueArgument(it) as IrVararg?
offset(expression, scope) {
val hasSpreadElement = hasSpreadElement(parameterExpression)
if (!hasSpreadElement && parameterExpression?.elements?.all { it is IrConst<*> && KotlinBuiltIns.isString(it.type)}?:false) {
log("skipped vararg expression because it's string array literal")
return@forEach
}
val block = irBlock(kArrayType.defaultType)
val arrayConstructorCall = irCall(
descriptor = arrayConstructor,
typeArguments = mapOf(arrayConstructor.typeParameters[0] to type))
if (parameterExpression == null) {
arrayConstructorCall.putValueArgument(0, kIntZero)
block.statements.add(arrayConstructorCall)
varargArgs.put(it, block)
return@forEach
}
val vars = parameterExpression.elements.map {
val initVar = scope.createTemporaryVariable((it as? IrSpreadElement)?.expression ?: it as IrExpression, "__elem\$", true)
block.statements.add(initVar)
it to initVar
}.toMap()
arrayConstructorCall.putValueArgument(0, calculateArraySize(hasSpreadElement, scope, parameterExpression, vars))
val arrayTmpVariable = scope.createTemporaryVariable(arrayConstructorCall, "__array\$", true)
val indexTmpVariable = scope.createTemporaryVariable(kIntZero, "__index\$", true)
block.statements.add(arrayTmpVariable)
if (hasSpreadElement) {
block.statements.add(indexTmpVariable)
}
parameterExpression.elements.forEachIndexed { i, element ->
offset(parameterExpression, scope) {
log("element:$i> ${ir2string(element)}")
val dst = vars[element]!!
if (element !is IrSpreadElement) {
val setArrayElementCall = irCall(
descriptor = kArraySetFunctionDescriptor,
typeArguments = null
)
setArrayElementCall.dispatchReceiver = irGet(arrayTmpVariable.descriptor)
setArrayElementCall.putValueArgument(0, if (hasSpreadElement) irGet(indexTmpVariable.descriptor) else irConstInt(i))
setArrayElementCall.putValueArgument(1, irGet(dst.descriptor))
block.statements.add(setArrayElementCall)
if (hasSpreadElement) {
block.statements.add(incrementVariable(indexTmpVariable.descriptor, kIntOne))
}
} else {
val arraySizeVariable = scope.createTemporaryVariable(irArraySize(irGet(dst.descriptor)), "__length\$")
block.statements.add(arraySizeVariable)
val copyCall = irCall(kCopyRangeToDescriptor, null).apply {
extensionReceiver = irGet(dst.descriptor)
putValueArgument(0, irGet(arrayTmpVariable.descriptor)) /* destination */
putValueArgument(1, kIntZero) /* fromIndex */
putValueArgument(2, irGet(arraySizeVariable.descriptor)) /* toIndex */
putValueArgument(3, irGet(indexTmpVariable.descriptor)) /* destinationIndex */
}
block.statements.add(copyCall)
block.statements.add(incrementVariable(indexTmpVariable.descriptor,
irGet(arraySizeVariable.descriptor)))
log("element:$i:spread element> ${ir2string(element.expression)}")
}
}
}
block.statements.add(irGet(arrayTmpVariable.descriptor))
varargArgs.put(it, block)
}
}
return varargArgs
}
private fun Offset.intPlus() = irCall(kIntPlusDescriptor, null)
private fun Offset.increment(expression: IrExpression, value: IrExpression): IrExpression {
return intPlus().apply {
dispatchReceiver = expression
putValueArgument(0, value)
}
}
private fun Offset.incrementVariable(descriptor: VariableDescriptor, value: IrExpression): IrExpression {
return irSetVar(descriptor, intPlus().apply {
dispatchReceiver = irGet(descriptor)
putValueArgument(0, value)
})
}
private fun calculateArraySize(hasSpreadElement: Boolean, scope:Scope, expression: IrVararg, vars: Map<IrVarargElement, IrVariable>): IrExpression? {
offset(expression, scope) {
if (!hasSpreadElement)
return irConstInt(expression.elements.size)
val notSpreadElementCount = expression.elements.filter { it !is IrSpreadElement}.size
val initialValue = irConstInt(notSpreadElementCount) as IrExpression
return vars.filter{it.key is IrSpreadElement}.toList().fold( initial = initialValue) { result, it ->
val arraySize = irArraySize(irGet(it.second.descriptor))
increment(result, arraySize)
}
}
}
private fun Offset.irArraySize(expression: IrExpression): IrExpression {
val arraySize = irCall((kArraySizeFunctionDescriptor as PropertyDescriptor).getter as FunctionDescriptor, null).apply {
dispatchReceiver = expression
}
return arraySize
}
private fun hasSpreadElement(expression: IrVararg?) = expression?.elements?.any { it is IrSpreadElement }?:false
private fun log(msg:String) {
context.log("VARARG-INJECTOR: $msg")
}
companion object {
val kArrayType = KonanPlatform.builtIns.array
val kCollectionsPackageDescriptor = KonanPlatform.builtIns.builtInsModule.getPackage(FqName("kotlin.collections")).memberScope
val kCopyRangeToDescriptor = DescriptorUtils.getAllDescriptors(kCollectionsPackageDescriptor).filter {
it.name.asString() == "copyRangeTo"
&& DescriptorUtils.getClassDescriptorForType((it as FunctionDescriptor).extensionReceiverParameter!!.type) == kArrayType}.first() as CallableDescriptor
val unsubstitutedMemberScope = kArrayType.unsubstitutedMemberScope
val kArraySetFunctionDescriptor = DescriptorUtils.getFunctionByName(unsubstitutedMemberScope, Name.identifier("set"))
val kArraySizeFunctionDescriptor = DescriptorUtils.getAllDescriptors(unsubstitutedMemberScope).find { it.name.asString() == "size" }
val kInt = KonanPlatform.builtIns.int
val kIntType = KonanPlatform.builtIns.intType
val kIntPlusDescriptor = DescriptorUtils.getAllDescriptors(kInt.unsubstitutedMemberScope).find {
it.name.asString() == "plus"
&& (it as FunctionDescriptor).valueParameters[0].type == kIntType} as FunctionDescriptor
}
inline internal fun <R> offset(ir:IrElement, scope:Scope, block: Offset.() -> R):R = offset(irContext, scope, ir.startOffset, ir.endOffset, block)
}
internal class Offset(context:IrGeneratorContext, scope: Scope, startOffset:Int, endOfset:Int) : IrBuilderWithScope(context, scope, startOffset, endOfset) {
val kIntZero = irConstInt(0)
val kIntOne = irConstInt(1)
companion object {
internal inline fun <R> use(context:IrGeneratorContext, scope: Scope, startOffset: Int, endOfset: Int, block: Offset.() -> R):R = Offset(context, scope, startOffset, endOfset).block()
}
}
internal fun CallableDescriptor.scope() = Scope(this)
private fun Offset.irConstInt(value: Int): IrConst<Int> = irConst<Int>(kind = IrConstKind.Int, value = value, type = kIntType)
private fun <T> Offset.irConst(kind: IrConstKind<T>, value: T, type: KotlinType): IrConst<T> = IrConstImpl<T>(startOffset, endOffset,type, kind,value)
private fun Offset.irBlock(type: KotlinType): IrBlock = IrBlockImpl(startOffset, endOffset, type)
private fun Offset.irCall(descriptor: CallableDescriptor, typeArguments: Map<TypeParameterDescriptor, KotlinType>?): IrCall = IrCallImpl(startOffset, endOffset, descriptor, typeArguments)
inline internal fun <R> offset(context:IrGeneratorContext, scope: Scope, startOffset: Int, endOffset: Int, block: Offset.() -> R):R {
@Suppress("NON_PUBLIC_CALL_FROM_PUBLIC_INLINE")
return Offset.use(context, scope, startOffset, endOffset, block)
}
+29 -6
View File
@@ -1,19 +1,42 @@
// TODO: utilize substitution mechanism from interop.
// macbook
arch.osx = x86_64
sysRoot.osx = target-sysroot-1-darwin-macos
llvmHome.osx = clang+llvm-3.8.0-darwin-macos
sysRoot.linux = target-gcc-toolchain-3-linux-x86-64/x86_64-unknown-linux-gnu/sysroot
llvmHome.linux = clang+llvm-3.8.0-linux-x86-64
libGcc.linux = target-gcc-toolchain-3-linux-x86-64/lib/gcc/x86_64-unknown-linux-gnu/4.8.5
llvmLtoFlags.osx = -O3 -function-sections -exported-symbol=_main
llvmLlcFlags.osx = -mtriple=x86_64-apple-macosx10.10.0
linkerKonanFlags.osx = -lc++
linkerOptimizationFlags.osx = -dead_strip
macosVersionMin.osx = 10.10.0
osVersionMin.osx = -macosx_version_min 10.10.0
// iphone
arch.osx-ios = arm64
sysRoot.osx-ios = target-sysroot-1-darwin-macos
targetSysRoot.osx-ios = target-sysroot-1-darwin-ios
llvmHome.osx-ios = clang+llvm-3.8.0-darwin-macos
llvmLtoFlags.osx-ios = -O3 -function-sections -exported-symbol=_main
llvmLlcFlags.osx-ios = -mtriple=arm64-apple-ios5.0.0
linkerKonanFlags.osx-ios = -lc++
linkerOptimizationFlags.osx-ios = -dead_strip
osVersionMin.osx-ios = -iphoneos_version_min 5.0.0
// iphone_sim
arch.osx-ios-sim = x86_64
sysRoot.osx-ios-sim = target-sysroot-1-darwin-macos
targetSysRoot.osx-ios-sim = target-sysroot-1-darwin-ios-sim
llvmHome.osx-ios-sim = clang+llvm-3.8.0-darwin-macos
llvmLtoFlags.osx-ios-sim = -O3 -function-sections -exported-symbol=_main
llvmLlcFlags.osx-ios-sim = -mtriple=x86_64-apple-ios5.0.0
linkerKonanFlags.osx-ios-sim = -lc++
linkerOptimizationFlags.osx-ios-sim = -dead_strip
osVersionMin.osx-ios-sim = -ios_simulator_version_min 5.0.0
// linux
sysRoot.linux = target-gcc-toolchain-3-linux-x86-64/x86_64-unknown-linux-gnu/sysroot
llvmHome.linux = clang+llvm-3.8.0-linux-x86-64
libGcc.linux = target-gcc-toolchain-3-linux-x86-64/lib/gcc/x86_64-unknown-linux-gnu/4.8.5
llvmLtoFlags.linux = -O3 -function-sections -exported-symbol=main
llvmLlcFlags.linux = -march=x86-64
linkerKonanFlags.linux = -Bstatic -lstdc++ -Bdynamic -ldl -lm -lpthread
+206 -96
View File
@@ -1,3 +1,6 @@
import groovy.json.JsonOutput
import org.jetbrains.kotlin.*
configurations {
cli_bc
}
@@ -6,107 +9,76 @@ dependencies {
cli_bc project(path: ':backend.native', configuration: 'cli_bc')
}
def testOutputRoot = rootProject.file("test.output").absolutePath
def externalTestsDir = project.file("external")
allprojects {
// Root directories for test output (logs, compiled files, statistics etc). Only single path must be in each set.
sourceSets {
// backend.native/tests
testOutputLocal {
output.dir(rootProject.file("$testOutputRoot/local"))
}
abstract class KonanTest extends DefaultTask {
protected String source
def backendNative = project.project(":backend.native")
def runtimeProject = project.project(":runtime")
def dist = project.parent.parent.file("dist")
def llvmLlc = llvmTool("llc")
def runtimeBc = new File("${dist.canonicalPath}/lib/runtime.bc").absolutePath
def launcherBc = new File("${dist.canonicalPath}/lib/launcher.bc").absolutePath
def startKtBc = new File("${dist.canonicalPath}/lib/start.kt.bc").absolutePath
def stdlibKtBc = new File("${dist.canonicalPath}/lib/stdlib.kt.bc").absolutePath
def mainC = 'main.c'
String goldValue = null
String testData = null
List<String> arguments = null
boolean enabled = true
public void setDisabled(boolean value) {
this.enabled = !value
}
public KonanTest(){
// TODO: that's a long reach up the project tree.
// May be we should reorganize a little.
dependsOn(project.parent.parent.tasks['dist'])
}
abstract void compileTest(String source, String exe)
protected void runCompiler(String source, String output, List<String> moreArgs) {
project.javaexec {
main = 'org.jetbrains.kotlin.cli.bc.K2NativeKt'
classpath = project.configurations.cli_bc
jvmArgs "-ea",
"-Dkonan.home=${dist.canonicalPath}",
"-Djava.library.path=${dist.canonicalPath}/konan/nativelib"
args("-output", output,
source,
*moreArgs,
*project.globalArgs)
// backend.native/tests/external
testOutputExternal {
output.dir(rootProject.file("$testOutputRoot/external"))
}
}
@TaskAction
void executeTest() {
def sourceKt = project.file(source).absolutePath
def exe = sourceKt.replace(".kt", ".kt.exe")
compileTest(sourceKt, exe)
println "execution :$exe"
def out = null
project.exec {
commandLine exe
if (arguments != null) {
args arguments
}
if (testData != null) {
standardInput = new ByteArrayInputStream(testData.bytes)
}
if (goldValue != null) {
out = new ByteArrayOutputStream()
standardOutput = out
}
}
if (goldValue != null && goldValue != out.toString())
throw new RuntimeException("test failed")
}
private String llvmTool(String tool) {
return "${project.llvmDir}/bin/${tool}"
}
protected List<String> clangLinkArgs() {
return project.clangLinkArgs
}
}
class RunKonanTest extends KonanTest {
void compileTest(String source, String exe) {
runCompiler(source, exe, [])
task clean {
doLast {
delete(rootProject.file(testOutputRoot))
}
}
class LinkKonanTest extends KonanTest {
protected String lib
void compileTest(String source, String exe) {
def libDir = project.file(lib).absolutePath
def libBc = "${libDir}.bc"
runCompiler(lib, libBc, ['-nolink', '-nostdlib'])
runCompiler(source, exe, ['-library', libBc])
}
}
task run() {
dependsOn(tasks.withType(KonanTest).matching { it.enabled })
dependsOn(tasks.withType(KonanTest).matching { !(it instanceof RunExternalTestGroup) && it.enabled })
}
task run_external () {
// TODO Consider test output directory cleaning before execution.
// TODO Consider using some logger instead of println
// Create tasks for external tests
externalTestsDir.eachDirRecurse {
// Skip build directory and directories without *.kt files.
if (it.name == "build" || it.listFiles().count {it.name.endsWith(".kt")} == 0) {
return
}
def taskDirectory = project.relativePath(it)
def taskName = taskDirectory.replaceAll("[-/]", '_')
task("$taskName", type: RunExternalTestGroup){
groupDirectory = "$taskDirectory"
}
}
def testTasks = tasks.withType(RunExternalTestGroup).matching { it.enabled }
dependsOn(testTasks)
doLast {
Map<String, Map<String, RunExternalTestGroup.TestResult>> results = [:]
def statistics = new RunExternalTestGroup.Statistics()
testTasks.matching { it.state.executed }.each {
results.put(it.name, it.results)
statistics.add(it.statistics)
}
def output = ["statistics" : statistics, "tests" : results]
def json = JsonOutput.toJson(output)
def reportFile = new File(sourceSets.testOutputExternal.output.getDirs().getSingleFile(), "results.json")
reportFile.write(JsonOutput.prettyPrint(json))
println("DONE.\n\n" +
"TOTAL: $statistics.total\n" +
"PASSED: $statistics.passed\n" +
"FAILED: $statistics.failed\n" +
"SKIPPED: $statistics.skipped")
}
}
task daily() {
dependsOn(run_external)
}
task sum (type:RunKonanTest) {
@@ -127,7 +99,6 @@ task fields1(type: RunKonanTest) {
}
task fields2(type: RunKonanTest) {
disabled = true
goldValue =
"Set global = 1\n" +
"Set member = 42\n" +
@@ -166,6 +137,11 @@ task safe_cast(type: RunKonanTest) {
source = "codegen/basics/safe_cast.kt"
}
task typealias1(type: RunKonanTest) {
goldValue = "42\n"
source = "codegen/basics/typealias1.kt"
}
task aritmetic(type: RunKonanTest) {
source = "codegen/function/arithmetic.kt"
}
@@ -202,13 +178,11 @@ task defaults4(type: RunKonanTest) {
}
task defaults5(type: RunKonanTest) {
disabled = true
goldValue = "5\n6\n"
source = "codegen/function/defaults5.kt"
}
task defaults6(type: RunKonanTest) {
disabled = true
goldValue = "42\n"
source = "codegen/function/defaults6.kt"
}
@@ -231,6 +205,27 @@ task cast_simple(type: RunKonanTest) {
source = "codegen/basics/cast_simple.kt"
}
task cast_null(type: RunKonanTest) {
goldValue = "Ok\n"
source = "codegen/basics/cast_null.kt"
}
task unchecked_cast1(type: RunKonanTest) {
goldValue = "17\n17\n42\n42\n"
source = "codegen/basics/unchecked_cast1.kt"
}
task unchecked_cast2(type: RunKonanTest) {
disabled = true
goldValue = "Ok\n"
source = "codegen/basics/unchecked_cast2.kt"
}
task unchecked_cast3(type: RunKonanTest) {
goldValue = "Ok\n"
source = "codegen/basics/unchecked_cast3.kt"
}
task null_check(type: RunKonanTest) {
source = "codegen/basics/null_check.kt"
}
@@ -297,6 +292,76 @@ task empty_substring(type: RunKonanTest) {
source = "runtime/basic/empty_substring.kt"
}
task superFunCall(type: RunKonanTest) {
goldValue = "<fun:C><fun:C1>\n<fun:C><fun:C3>\n"
source = "codegen/basics/superFunCall.kt"
}
task superGetterCall(type: RunKonanTest) {
goldValue = "<prop:C><prop:C1>\n<prop:C><prop:C3>\n"
source = "codegen/basics/superGetterCall.kt"
}
task superSetterCall(type: RunKonanTest) {
goldValue = "<prop:C1><prop:C>zzz\n<prop:C3><prop:C>zzz\n"
source = "codegen/basics/superSetterCall.kt"
}
task enum0(type: RunKonanTest) {
goldValue = "VALUE\n"
source = "codegen/enum/test0.kt"
}
task enum1(type: RunKonanTest) {
goldValue = "z12\n"
source = "codegen/enum/test1.kt"
}
task enum_valueOf(type: RunKonanTest) {
goldValue = "E1\n"
source = "codegen/enum/valueOf.kt"
}
task enum_values(type: RunKonanTest) {
goldValue = "E2\n"
source = "codegen/enum/values.kt"
}
task enum_vCallNoEntryClass(type: RunKonanTest) {
goldValue = "('z3', 3)\n"
source = "codegen/enum/vCallNoEntryClass.kt"
}
task enum_vCallWithEntryClass(type: RunKonanTest) {
goldValue = "z1z2\n"
source = "codegen/enum/vCallWithEntryClass.kt"
}
task enum_companionObject(type: RunKonanTest) {
goldValue = "OK\n"
source = "codegen/enum/companionObject.kt"
}
task innerClass_simple(type: RunKonanTest) {
goldValue = "OK\n"
source = "codegen/innerClass/simple.kt"
}
task innerClass_getOuterVal(type: RunKonanTest) {
goldValue = "OK\n"
source = "codegen/innerClass/getOuterVal.kt"
}
task innerClass_generic(type: RunKonanTest) {
goldValue = "OK\n"
source = "codegen/innerClass/generic.kt"
}
task innerClass_qualifiedThis(type: RunKonanTest) {
goldValue = "OK\n"
source = "codegen/innerClass/qualifiedThis.kt"
}
task array0(type: RunKonanTest) {
goldValue = "5\n6\n7\n8\n9\n10\n11\n12\n13\n"
source = "runtime/collections/array0.kt"
@@ -337,6 +402,11 @@ task when8(type: RunKonanTest) {
goldValue = "true\n"
}
task when9(type: RunKonanTest) {
goldValue = "Ok\n"
source = "codegen/branching/when9.kt"
}
task when_through(type: RunKonanTest) {
source = "codegen/branching/when_through.kt"
}
@@ -505,6 +575,17 @@ task boxing13(type: RunKonanTest) {
source = "codegen/boxing/boxing13.kt"
}
task boxing14(type: RunKonanTest) {
goldValue = "42\n"
source = "codegen/boxing/boxing14.kt"
}
task boxing15(type: RunKonanTest) {
disabled = true
goldValue = "17\n"
source = "codegen/boxing/boxing15.kt"
}
task interface0(type: RunKonanTest) {
goldValue = "PASSED\n"
source = "runtime/basic/interface0.kt"
@@ -572,6 +653,11 @@ task catch7(type: RunKonanTest) {
source = "runtime/exceptions/catch7.kt"
}
task extend_exception(type: RunKonanTest) {
goldValue = "OK\n"
source = "runtime/exceptions/extend0.kt"
}
task catch3(type: RunKonanTest) {
goldValue = "Before\nCaught Throwable\nDone\n"
source = "codegen/try/catch3.kt"
@@ -814,6 +900,21 @@ task lambda13(type: RunKonanTest) {
source = "codegen/lambda/lambda13.kt"
}
task objectExpression1(type: RunKonanTest) {
goldValue = "aabb\n"
source = "codegen/objectExpression/1.kt"
}
task objectExpression2(type: RunKonanTest) {
goldValue = "a\n"
source = "codegen/objectExpression/2.kt"
}
task objectExpression3(type: RunKonanTest) {
goldValue = "\n1\n2\n"
source = "codegen/objectExpression/3.kt"
}
task initializers2(type: RunKonanTest) {
goldValue = "init globalValue2\n" +
"init globalValue3\n" +
@@ -829,11 +930,15 @@ task initializers3(type: RunKonanTest) {
}
task initializers4(type: RunKonanTest) {
disabled = true
goldValue = "fixme\n"
goldValue = "1073741824\ntrue\n"
source = "runtime/basic/initializers4.kt"
}
task initializers5(type: RunKonanTest) {
goldValue = "42\n"
source = "runtime/basic/initializers5.kt"
}
task expression_as_statement(type: RunKonanTest) {
goldValue = "Ok\n"
source = "codegen/basics/expression_as_statement.kt"
@@ -905,6 +1010,7 @@ task inline3(type: RunKonanTest) {
source = "codegen/inline/inline3.kt"
}
<<<<<<< HEAD
task inline4(type: RunKonanTest) {
goldValue = "3\n"
source = "codegen/inline/inline4.kt"
@@ -918,4 +1024,8 @@ task inline5(type: RunKonanTest) {
task inline9(type: RunKonanTest) {
goldValue = "hello\n6\n"
source = "codegen/inline/inline9.kt"
=======
task vararg0(type: RunKonanTest) {
source = "lower/vararg.kt"
>>>>>>> b4ee0e86d2890b3aca7eab4e1910a90fedff2b55
}
@@ -0,0 +1,48 @@
fun main(args: Array<String>) {
testCast(null, false)
testCastToNullable(null, true)
testCastToNullable(Test(), true)
testCastToNullable("", false)
testCastNotNullableToNullable(Test(), true)
testCastNotNullableToNullable("", false)
println("Ok")
}
class Test
fun ensure(b: Boolean) {
if (!b) {
println("Error")
}
}
fun testCast(x: Any?, expectSuccess: Boolean) {
try {
x as Test
} catch (e: Throwable) {
ensure(!expectSuccess)
return
}
ensure(expectSuccess)
}
fun testCastToNullable(x: Any?, expectSuccess: Boolean) {
try {
x as Test?
} catch (e: Throwable) {
ensure(!expectSuccess)
return
}
ensure(expectSuccess)
}
fun testCastNotNullableToNullable(x: Any, expectSuccess: Boolean) {
try {
x as Test?
} catch (e: Throwable) {
ensure(!expectSuccess)
return
}
ensure(expectSuccess)
}
@@ -0,0 +1,19 @@
open class C {
open fun f() = "<fun:C>"
}
class C1: C() {
override fun f() = super<C>.f() + "<fun:C1>"
}
open class C2: C() {
}
class C3: C2() {
override fun f() = super<C2>.f() + "<fun:C3>"
}
fun main(args: Array<String>) {
println(C1().f())
println(C3().f())
}
@@ -0,0 +1,19 @@
open class C {
open val p1 = "<prop:C>"
}
class C1: C() {
override val p1 = super<C>.p1 + "<prop:C1>"
}
open class C2: C() {
}
class C3: C2() {
override val p1 = super<C2>.p1 + "<prop:C3>"
}
fun main(args: Array<String>) {
println(C1().p1)
println(C3().p1)
}
@@ -0,0 +1,32 @@
open class C {
open var p2 = "<prop:C>"
set(value) { field = "<prop:C>" + value }
}
class C1: C() {
override var p2 = super<C>.p2 + "<prop:C1>"
set(value) {
super<C>.p2 = value
field = "<prop:C1>" + super<C>.p2
}
}
open class C2: C() {
}
class C3: C2() {
override var p2 = super<C2>.p2 + "<prop:C3>"
set(value) {
super<C2>.p2 = value
field = "<prop:C3>" + super<C2>.p2
}
}
fun main(args: Array<String>) {
val c1 = C1()
val c3 = C3()
c1.p2 = "zzz"
c3.p2 = "zzz"
println(c1.p2)
println(c3.p2)
}
@@ -0,0 +1,6 @@
fun main(args: Array<String>) {
println(Bar(42).x)
}
class Foo(val x: Int)
typealias Bar = Foo
@@ -0,0 +1,16 @@
fun main(args: Array<String>) {
foo<String>("17")
bar<String>("17")
foo<String>(42)
bar<String>(42)
}
fun <T> foo(x: Any?) {
val y = x as T
println(y.toString())
}
fun <T> bar(x: Any?) {
val y = x as? T
println(y.toString())
}
@@ -0,0 +1,10 @@
fun main(args: Array<String>) {
try {
val x = cast<String>(Any())
println(x.length)
} catch (e: Throwable) {
println("Ok")
}
}
fun <T> cast(x: Any?) = x as T
@@ -0,0 +1,35 @@
fun main(args: Array<String>) {
testCast<Test>(Test(), true)
testCast<Test>(null, false)
testCastToNullable<Test>(null, true)
println("Ok")
}
class Test
fun ensure(b: Boolean) {
if (!b) {
println("Error")
}
}
fun <T : Any> testCast(x: Any?, expectSuccess: Boolean) {
try {
x as T
} catch (e: Throwable) {
ensure(!expectSuccess)
return
}
ensure(expectSuccess)
}
fun <T : Any> testCastToNullable(x: Any?, expectSuccess: Boolean) {
try {
x as T?
} catch (e: Throwable) {
ensure(!expectSuccess)
return
}
ensure(expectSuccess)
}
@@ -0,0 +1,5 @@
fun main(args: Array<String>) {
42.println()
}
fun <T> T.println() = println(this.toString())
@@ -0,0 +1,5 @@
fun main(args: Array<String>) {
println(foo(17))
}
fun <T : Int> foo(x: T): Int = x
@@ -0,0 +1,10 @@
fun main(args: Array<String>) {
foo(0)
println("Ok")
}
fun foo(x: Int) {
when (x) {
0 -> 0
}
}
@@ -0,0 +1,24 @@
enum class Game {
ROCK,
PAPER,
SCISSORS;
companion object {
fun foo() = ROCK
val bar = PAPER
val values2 = values()
val scissors = valueOf("SCISSORS")
}
}
fun box(): String {
if (Game.foo() != Game.ROCK) return "Fail 1"
if (Game.bar != Game.PAPER) return "Fail 2: ${Game.bar}"
if (Game.values().size != 3) return "Fail 3"
if (Game.valueOf("SCISSORS") != Game.SCISSORS) return "Fail 4"
if (Game.values2.size != 3) return "Fail 5"
if (Game.scissors != Game.SCISSORS) return "Fail 6"
return "OK"
}
fun main(args: Array<String>) = println(box())
@@ -0,0 +1,9 @@
val TOP_LEVEL = 5
enum class MyEnum(value: Int) {
VALUE(TOP_LEVEL)
}
fun main(args: Array<String>) {
println(MyEnum.VALUE.toString())
}
@@ -0,0 +1,8 @@
enum class Zzz(val zzz: String, val x: Int) {
Z1("z1", 1),
Z2("z2", 2)
}
fun main(args: Array<String>) {
println(Zzz.Z1.zzz + Zzz.Z2.x)
}
@@ -0,0 +1,13 @@
enum class Zzz(val zzz: String, val x: Int) {
Z1("z1", 1),
Z2("z2", 2),
Z3("z3", 3);
override fun toString(): String{
return "('$zzz', $x)"
}
}
fun main(args: Array<String>) {
println(Zzz.Z3.toString())
}
@@ -0,0 +1,15 @@
enum class Zzz {
Z1 {
override fun f() = "z1"
},
Z2 {
override fun f() = "z2"
};
open fun f() = ""
}
fun main(args: Array<String>) {
println(Zzz.Z1.f() + Zzz.Z2.f())
}
@@ -0,0 +1,8 @@
enum class E {
E1,
E2
}
fun main(args: Array<String>) {
println(E.valueOf("E1").toString())
}
@@ -0,0 +1,8 @@
enum class E {
E1,
E2
}
fun main(args: Array<String>) {
println(E.values()[1].toString())
}
@@ -2,10 +2,12 @@ open class A {
open fun foo(x: Int = 42) = println(x)
}
class B : A() {
open class B : A()
class C : B() {
override fun foo(x: Int) = println(x + 1)
}
fun main(args: Array<String>) {
B().foo()
C().foo()
}
@@ -0,0 +1,15 @@
class Outer {
inner class Inner<T>(val t: T) {
fun box() = t
}
}
fun box(): String {
if (Outer().Inner("OK").box() != "OK") return "Fail"
val x: Outer.Inner<String> = Outer().Inner("OK")
return x.box()
}
fun main(args: Array<String>) {
println(box())
}
@@ -0,0 +1,12 @@
class Outer(val s: String) {
inner class Inner {
fun box() = s
}
}
fun box() = Outer("OK").Inner().box()
fun main(args: Array<String>)
{
println(box())
}
@@ -0,0 +1,46 @@
open class ABase
{
open fun zzz() = "a_base"
}
open class BBase
{
open fun zzz() = "b_base"
}
class D() {
val z = "d"
}
class A: ABase() { // implicit label @A
val z = "a"
override fun zzz() = "a"
inner class B: BBase() { // implicit label @B
val z = "b"
override fun zzz() = "b"
fun D.foo() : String { // implicit label @foo
if(this@A.z != "a") return "Fail1"
if(this@B.z != "b") return "Fail2"
if(super@A.zzz() != "a_base") return "Fail3"
if(super<BBase>.zzz() != "b_base") return "Fail4"
if(super@B.zzz() != "b_base") return "Fail5"
if(this@A.zzz() != "a") return "Fail6"
if(this@B.zzz() != "b") return "Fail7"
if(this.z != "d") return "Fail8"
return "OK"
}
fun bar(d: D): String {
return d.foo()
}
}
}
fun box() = A().B().bar(D())
fun main(args : Array<String>) {
println(box())
}
@@ -0,0 +1,12 @@
class Outer {
inner class Inner {
fun box() = "OK"
}
}
fun box() = Outer().Inner().box()
fun main(args: Array<String>)
{
println(box())
}
@@ -0,0 +1,13 @@
fun main(args: Array<String>) {
val a = "a"
val x = object {
override fun toString(): String {
return foo(a) + foo("b")
}
fun foo(s: String) = s + s
}
println(x.toString())
}
@@ -0,0 +1,17 @@
fun main(args: Array<String>) {
val a = "a"
val x = object {
override fun toString(): String {
return foo {
a
}
}
fun foo(lambda: () -> String) = lambda()
}
print(x)
}
fun print(x: Any) = println(x.toString())
@@ -0,0 +1,17 @@
fun main(args: Array<String>) {
var cnt = 0
var x: Any = ""
for (i in 0 .. 1) {
print(x)
cnt++
val y = object {
override fun toString() = cnt.toString()
}
x = y
}
print(x)
}
fun print(x: Any) = println(x.toString())
@@ -0,0 +1,35 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS, NATIVE
// WITH_RUNTIME
// KT-5665
@Retention(AnnotationRetention.RUNTIME)
annotation class First
@Retention(AnnotationRetention.RUNTIME)
annotation class Second(val value: String)
enum class E {
@First
E1 {
fun foo() = "something"
},
@Second("OK")
E2
}
fun box(): String {
val e = E::class.java
val e1 = e.getDeclaredField(E.E1.toString()).getAnnotations()
if (e1.size != 1) return "Fail E1 size: ${e1.toList()}"
if (e1[0].annotationClass.java != First::class.java) return "Fail E1: ${e1.toList()}"
val e2 = e.getDeclaredField(E.E2.toString()).getAnnotations()
if (e2.size != 1) return "Fail E2 size: ${e2.toList()}"
if (e2[0].annotationClass.java != Second::class.java) return "Fail E2: ${e2.toList()}"
return (e2[0] as Second).value
}
@@ -0,0 +1,33 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS, NATIVE
// WITH_RUNTIME
import java.lang.reflect.Method
import kotlin.test.assertEquals
@Retention(AnnotationRetention.RUNTIME)
annotation class Ann(val x: String)
fun foo0(block: () -> Unit) = block.javaClass
fun foo1(block: (String) -> Unit) = block.javaClass
fun testMethod(method: Method, name: String) {
assertEquals("OK", method.getAnnotation(Ann::class.java).x, "On method of test named `$name`")
for ((index, annotations) in method.getParameterAnnotations().withIndex()) {
val ann = annotations.filterIsInstance<Ann>().single()
assertEquals("OK$index", ann.x, "On parameter $index of test named `$name`")
}
}
fun testClass(clazz: Class<*>, name: String) {
val invokes = clazz.getDeclaredMethods().single() { !it.isBridge() }
testMethod(invokes, name)
}
fun box(): String {
testClass(foo0( @Ann("OK") fun() {} ), "1")
testClass(foo1( @Ann("OK") fun(@Ann("OK0") x: String) {} ), "2")
return "OK"
}
@@ -0,0 +1,35 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS, NATIVE
// WITH_RUNTIME
import java.lang.reflect.Method
import kotlin.test.assertEquals
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class Ann(val x: String)
fun foo0(block: () -> Unit) = block.javaClass
fun testMethod(method: Method, name: String) {
assertEquals("OK", method.getAnnotation(Ann::class.java).x, "On method of test named `$name`")
for ((index, annotations) in method.getParameterAnnotations().withIndex()) {
val ann = annotations.filterIsInstance<Ann>().single()
assertEquals("OK$index", ann.x, "On parameter $index of test named `$name`")
}
}
fun testClass(clazz: Class<*>, name: String) {
val invokes = clazz.getDeclaredMethods().single() { !it.isBridge() }
testMethod(invokes, name)
}
fun box(): String {
testClass(foo0(@Ann("OK") { }), "1")
testClass(foo0( @Ann("OK") { }), "2")
testClass(foo0() @Ann("OK") { }, "3")
return "OK"
}
@@ -0,0 +1,49 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS, NATIVE
// WITH_RUNTIME
// FILE: Test.java
class Test {
public static Class<?> apply(Runnable x) {
return x.getClass();
}
public static interface ABC {
void apply(String x1, String x2);
}
public static Class<?> applyABC(ABC x) {
return x.getClass();
}
}
// FILE: test.kt
import java.lang.reflect.Method
import kotlin.test.assertEquals
@Retention(AnnotationRetention.RUNTIME)
annotation class Ann(val x: String)
fun testMethod(method: Method, name: String) {
assertEquals("OK", method.getAnnotation(Ann::class.java).x, "On method of test named `$name`")
for ((index, annotations) in method.getParameterAnnotations().withIndex()) {
val ann = annotations.filterIsInstance<Ann>().single()
assertEquals("OK$index", ann.x, "On parameter $index of test named `$name`")
}
}
fun testClass(clazz: Class<*>, name: String) {
val invokes = clazz.getDeclaredMethods().single() { !it.isBridge() }
testMethod(invokes, name)
}
fun box(): String {
testClass(Test.apply(@Ann("OK") fun(){}), "1")
testClass(Test.applyABC(@Ann("OK") fun(@Ann("OK0") x: String, @Ann("OK1") y: String){}), "2")
return "OK"
}
@@ -0,0 +1,40 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS, NATIVE
// WITH_RUNTIME
// FILE: Test.java
class Test {
public static Class<?> apply(Runnable x) {
return x.getClass();
}
}
// FILE: test.kt
import java.lang.reflect.Method
import kotlin.test.assertEquals
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class Ann(val x: String)
fun testMethod(method: Method, name: String) {
assertEquals("OK", method.getAnnotation(Ann::class.java).x, "On method of test named `$name`")
for ((index, annotations) in method.getParameterAnnotations().withIndex()) {
val ann = annotations.filterIsInstance<Ann>().single()
assertEquals("OK$index", ann.x, "On parameter $index of test named `$name`")
}
}
fun testClass(clazz: Class<*>, name: String) {
val invokes = clazz.getDeclaredMethods().single() { !it.isBridge() }
testMethod(invokes, name)
}
fun box(): String {
testClass(Test.apply(@Ann("OK") {}), "1")
testClass(Test.apply @Ann("OK") {}, "2")
return "OK"
}
@@ -0,0 +1,19 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS, NATIVE
// WITH_RUNTIME
annotation class Ann(val v: String = "???")
@Ann open class My
fun box(): String {
val v = @Ann("OK") object: My() {}
val klass = v.javaClass
val annotations = klass.annotations.toList()
// Ann, kotlin.Metadata
if (annotations.size != 2) return "Fail annotations size is ${annotations.size}: $annotations"
val annotation = annotations.filterIsInstance<Ann>().firstOrNull()
?: return "Fail no @Ann: $annotations"
return annotation.v
}
@@ -0,0 +1,36 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS, NATIVE
// WITH_RUNTIME
// FILE: JavaClass.java
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
public class JavaClass {
@Retention(RetentionPolicy.RUNTIME)
@interface Foo {
int value();
}
@Foo(KotlinClass.FOO_INT)
public String test() throws NoSuchMethodException {
return KotlinClass.FOO_STRING +
JavaClass.class.getMethod("test").getAnnotation(Foo.class).value();
}
}
// FILE: kotlinClass.kt
class KotlinClass {
companion object {
const val FOO_INT: Int = 10
@JvmField val FOO_STRING: String = "OK"
}
}
fun box(): String {
val test = JavaClass().test()
return if (test == "OK10") "OK" else "fail : $test"
}
@@ -0,0 +1,35 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS, NATIVE
// FILE: JavaClass.java
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
public class JavaClass {
@Retention(RetentionPolicy.RUNTIME)
@interface Foo {
int value();
}
@Foo(KotlinInterface.FOO_INT)
public String test() throws NoSuchMethodException {
return KotlinInterface.FOO_STRING +
JavaClass.class.getMethod("test").getAnnotation(Foo.class).value();
}
}
// FILE: KotlinInterface.kt
interface KotlinInterface {
companion object {
const val FOO_INT: Int = 10
const val FOO_STRING: String = "OK"
}
}
fun box(): String {
val test = JavaClass().test()
return if (test == "OK10") "OK" else "fail : $test"
}
@@ -0,0 +1,40 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS, NATIVE
// WITH_RUNTIME
import kotlin.test.assertEquals
@Retention(AnnotationRetention.RUNTIME)
annotation class Ann(val x: Int)
class A {
@Ann(1) fun foo(x: Int, y: Int = 2, z: Int) {}
@Ann(1) constructor(x: Int, y: Int = 2, z: Int)
}
class B @Ann(1) constructor(x: Int, y: Int = 2, z: Int) {}
fun test(name: String, annotations: Array<out Annotation>) {
assertEquals(1, annotations.filterIsInstance<Ann>().single().x, "$name[0]")
}
fun box(): String {
val foo = A::class.java.getDeclaredMethods().first { it.getName() == "foo" }
test("foo", foo.getDeclaredAnnotations())
val fooDefault = A::class.java.getDeclaredMethods().first { it.getName() == "foo\$default" }
test("foo", foo.getDeclaredAnnotations())
val (secondary, secondaryDefault) = A::class.java.getDeclaredConstructors().partition { it.getParameterTypes().size == 3 }
test("secondary", secondary[0].getDeclaredAnnotations())
test("secondary\$default", secondaryDefault[0].getDeclaredAnnotations())
val (primary, primaryDefault) = B::class.java.getConstructors().partition { it.getParameterTypes().size == 3 }
test("primary", primary[0].getDeclaredAnnotations())
test("primary\$default", primaryDefault[0].getDeclaredAnnotations())
return "OK"
}
@@ -0,0 +1,28 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS, NATIVE
// WITH_RUNTIME
// FULL_JDK
import kotlin.annotation.AnnotationTarget.*
import kotlin.annotation.AnnotationRetention.*
import java.lang.Class
@Target(TYPEALIAS)
@Retention(RUNTIME)
annotation class Ann(val x: Int)
@Ann(2)
typealias TA = Any
fun Class<*>.assertHasDeclaredMethodWithAnn() {
if (!declaredMethods.any { it.isSynthetic && it.getAnnotation(Ann::class.java) != null }) {
throw java.lang.AssertionError("Class ${this.simpleName} has no declared method with annotation @Ann")
}
}
fun box(): String {
Class.forName("AnnotationsOnTypeAliasesKt").assertHasDeclaredMethodWithAnn()
return "OK"
}
@@ -0,0 +1,41 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS, NATIVE
// WITH_RUNTIME
import kotlin.reflect.KClass
@Retention(AnnotationRetention.RUNTIME)
annotation class Ann(
val i: Int = 1,
val s: String = "a",
val a: Ann2 = Ann2(),
val e: MyEnum = MyEnum.A,
val c: KClass<*> = A::class,
val ia: IntArray = intArrayOf(1, 2),
val sa: Array<String> = arrayOf("a", "b")
)
fun box(): String {
val ann = MyClass::class.java.getAnnotation(Ann::class.java)
if (ann == null) return "fail: cannot find Ann on MyClass}"
if (ann.i != 1) return "fail: annotation parameter i should be 1, but was ${ann.i}"
if (ann.s != "a") return "fail: annotation parameter s should be \"a\", but was ${ann.s}"
val annSimpleName = ann.a.annotationClass.java.getSimpleName()
if (annSimpleName != "Ann2") return "fail: annotation parameter a should be of class Ann2, but was $annSimpleName"
if (ann.e != MyEnum.A) return "fail: annotation parameter e should be MyEnum.A, but was ${ann.e}"
if (ann.c.java != A::class.java) return "fail: annotation parameter c should be of class A, but was ${ann.c}"
if (ann.ia[0] != 1 || ann.ia[1] != 2) return "fail: annotation parameter ia should be [1, 2], but was ${ann.ia}"
if (ann.sa[0] != "a" || ann.sa[1] != "b") return "fail: annotation parameter ia should be [\"a\", \"b\"], but was ${ann.sa}"
return "OK"
}
annotation class Ann2
enum class MyEnum {
A
}
class A
@Ann class MyClass
@@ -0,0 +1,32 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS, NATIVE
// WITH_RUNTIME
import kotlin.reflect.KProperty
@Retention(AnnotationRetention.RUNTIME)
annotation class First
class MyClass() {
public var x: String by Delegate()
@First set
}
class Delegate {
operator fun getValue(t: Any?, p: KProperty<*>): String {
return "OK"
}
operator fun setValue(t: Any?, p: KProperty<*>, i: String) {}
}
fun box(): String {
val e = MyClass::class.java
val e1 = e.getDeclaredMethod("setX", String::class.java).getAnnotations()
if (e1.size != 1) return "Fail E1 size: ${e1.toList()}"
if (e1[0].annotationClass.java != First::class.java) return "Fail: ${e1.toList()}"
return MyClass().x
}
@@ -0,0 +1,14 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS, NATIVE
// WITH_RUNTIME
@file:StringHolder("OK")
@file:JvmName("FileClass")
@Target(AnnotationTarget.FILE)
@Retention(AnnotationRetention.RUNTIME)
public annotation class StringHolder(val value: String)
fun box(): String =
Class.forName("FileClass").getAnnotation(StringHolder::class.java)?.value ?: "null"
@@ -0,0 +1,34 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS, NATIVE
// WITH_RUNTIME
// FULL_JDK
import java.lang.reflect.Modifier
import kotlin.reflect.KProperty
class CustomDelegate {
operator fun getValue(thisRef: Any?, prop: KProperty<*>): String = prop.name
}
class C {
@Volatile var vol = 1
@Transient val tra = 1
@delegate:Transient val del: String by CustomDelegate()
@Strictfp fun str() {}
@Synchronized fun sync() {}
}
fun box(): String {
val c = C::class.java
if (c.getDeclaredField("vol").getModifiers() and Modifier.VOLATILE == 0) return "Fail: volatile"
if (c.getDeclaredField("tra").getModifiers() and Modifier.TRANSIENT == 0) return "Fail: transient"
if (c.getDeclaredField("del\$delegate").getModifiers() and Modifier.TRANSIENT == 0) return "Fail: delegate transient"
if (c.getDeclaredMethod("str").getModifiers() and Modifier.STRICT == 0) return "Fail: strict"
if (c.getDeclaredMethod("sync").getModifiers() and Modifier.SYNCHRONIZED == 0) return "Fail: synchronized"
return "OK"
}
@@ -0,0 +1,48 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS, NATIVE
// WITH_RUNTIME
@Ann(Foo.i, Foo.s, Foo.f, Foo.d, Foo.l, Foo.b, Foo.bool, Foo.c, Foo.str) class MyClass
fun box(): String {
val ann = MyClass::class.java.getAnnotation(Ann::class.java)
if (ann == null) return "fail: cannot find Ann on MyClass}"
if (ann.i != 2) return "fail: annotation parameter i should be 2, but was ${ann.i}"
if (ann.s != 2.toShort()) return "fail: annotation parameter i should be 2, but was ${ann.i}"
if (ann.f != 2.toFloat()) return "fail: annotation parameter i should be 2, but was ${ann.i}"
if (ann.d != 2.toDouble()) return "fail: annotation parameter i should be 2, but was ${ann.i}"
if (ann.l != 2.toLong()) return "fail: annotation parameter i should be 2, but was ${ann.i}"
if (ann.b != 2.toByte()) return "fail: annotation parameter i should be 2, but was ${ann.i}"
if (!ann.bool) return "fail: annotation parameter i should be true, but was ${ann.i}"
if (ann.c != 'c') return "fail: annotation parameter i should be c, but was ${ann.i}"
if (ann.str != "str") return "fail: annotation parameter i should be str, but was ${ann.i}"
return "OK"
}
@Retention(AnnotationRetention.RUNTIME)
annotation class Ann(
val i: Int,
val s: Short,
val f: Float,
val d: Double,
val l: Long,
val b: Byte,
val bool: Boolean,
val c: Char,
val str: String
)
class Foo {
companion object {
const val i: Int = 2
const val s: Short = 2
const val f: Float = 2.0.toFloat()
const val d: Double = 2.0
const val l: Long = 2
const val b: Byte = 2
const val bool: Boolean = true
const val c: Char = 'c'
const val str: String = "str"
}
}
@@ -0,0 +1,44 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS, NATIVE
// WITH_RUNTIME
@Ann(i, s, f, d, l, b, bool, c, str) class MyClass
fun box(): String {
val ann = MyClass::class.java.getAnnotation(Ann::class.java)
if (ann == null) return "fail: cannot find Ann on MyClass}"
if (ann.i != 2) return "fail: annotation parameter i should be 2, but was ${ann.i}"
if (ann.s != 2.toShort()) return "fail: annotation parameter i should be 2, but was ${ann.i}"
if (ann.f != 2.toFloat()) return "fail: annotation parameter i should be 2, but was ${ann.i}"
if (ann.d != 2.toDouble()) return "fail: annotation parameter i should be 2, but was ${ann.i}"
if (ann.l != 2.toLong()) return "fail: annotation parameter i should be 2, but was ${ann.i}"
if (ann.b != 2.toByte()) return "fail: annotation parameter i should be 2, but was ${ann.i}"
if (!ann.bool) return "fail: annotation parameter i should be true, but was ${ann.i}"
if (ann.c != 'c') return "fail: annotation parameter i should be c, but was ${ann.i}"
if (ann.str != "str") return "fail: annotation parameter i should be str, but was ${ann.i}"
return "OK"
}
@Retention(AnnotationRetention.RUNTIME)
annotation class Ann(
val i: Int,
val s: Short,
val f: Float,
val d: Double,
val l: Long,
val b: Byte,
val bool: Boolean,
val c: Char,
val str: String
)
const val i: Int = 2
const val s: Short = 2
const val f: Float = 2.0.toFloat()
const val d: Double = 2.0
const val l: Long = 2
const val b: Byte = 2
const val bool: Boolean = true
const val c: Char = 'c'
const val str: String = "str"
@@ -0,0 +1,24 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS, NATIVE
// WITH_RUNTIME
annotation class A
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class B(val items: Array<A> = arrayOf(A()))
@B
class C
fun box(): String {
val bClass = B::class.java
val cClass = C::class.java
val items = cClass.getAnnotation(bClass).items
assert(items.size == 1) { "Expected: [A()], got ${items.asList()}" }
assert(items[0] is A) { "Expected: [A()], got ${items.asList()}" }
return "OK"
}
@@ -0,0 +1,24 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS, NATIVE
// WITH_RUNTIME
@Ann(A.B.i) class MyClass
fun box(): String {
val ann = MyClass::class.java.getAnnotation(Ann::class.java)
if (ann == null) return "fail: cannot find Ann on MyClass}"
if (ann.i != 1) return "fail: annotation parameter i should be 1, but was ${ann.i}"
return "OK"
}
@Retention(AnnotationRetention.RUNTIME)
annotation class Ann(val i: Int)
class A {
class B {
companion object {
const val i = 1
}
}
}
@@ -0,0 +1,32 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS, NATIVE
// WITH_RUNTIME
@Retention(AnnotationRetention.RUNTIME)
annotation class Ann(
val b: Byte,
val s: Short,
val i: Int,
val f: Float,
val d: Double,
val l: Long,
val c: Char,
val bool: Boolean
)
fun box(): String {
val ann = MyClass::class.java.getAnnotation(Ann::class.java)
if (ann == null) return "fail: cannot find Ann on MyClass}"
if (ann.b != 1.toByte()) return "fail: annotation parameter b should be 1, but was ${ann.b}"
if (ann.s != 1.toShort()) return "fail: annotation parameter s should be 1, but was ${ann.s}"
if (ann.i != 1) return "fail: annotation parameter i should be 1, but was ${ann.i}"
if (ann.f != 1.toFloat()) return "fail: annotation parameter f should be 1, but was ${ann.f}"
if (ann.d != 1.0) return "fail: annotation parameter d should be 1, but was ${ann.d}"
if (ann.l != 1.toLong()) return "fail: annotation parameter l should be 1, but was ${ann.l}"
if (ann.c != 'c') return "fail: annotation parameter c should be 1, but was ${ann.c}"
if (!ann.bool) return "fail: annotation parameter bool should be 1, but was ${ann.bool}"
return "OK"
}
@Ann(1, 1, 1, 1.0.toFloat(), 1.0, 1, 'c', true) class MyClass
@@ -0,0 +1,19 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS, NATIVE
// WITH_RUNTIME
@Ann(i) class MyClass
fun box(): String {
val ann = MyClass::class.java.getAnnotation(Ann::class.java)
if (ann == null) return "fail: cannot find Ann on MyClass}"
if (ann.i != 1) return "fail: annotation parameter i should be 1, but was ${ann.i}"
return "OK"
}
@Retention(AnnotationRetention.RUNTIME)
annotation class Ann(val i: Int)
const val i2: Int = 1
const val i: Int = i2
@@ -0,0 +1,19 @@
// WITH_RUNTIME
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
@kotlin.internal.LowPriorityInOverloadResolution
fun foo(i: Int) = 1
fun foo(a: Any) = 2
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
@kotlin.internal.LowPriorityInOverloadResolution
fun bar(a: String?) = 3
fun bar(a: Any) = 4
fun box(): String {
if (foo(1) != 2) return "fail1"
if (bar(null) != 3) return "fail2"
return "OK"
}
@@ -0,0 +1,53 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS, NATIVE
// WITH_RUNTIME
@Retention(AnnotationRetention.RUNTIME)
annotation class Ann(vararg val p: Int)
@Ann() class MyClass1
@Ann(1) class MyClass2
@Ann(1, 2) class MyClass3
@Ann(*intArrayOf()) class MyClass4
@Ann(*intArrayOf(1)) class MyClass5
@Ann(*intArrayOf(1, 2)) class MyClass6
@Ann(p = 1) class MyClass7
@Ann(p = *intArrayOf()) class MyClass8
@Ann(p = *intArrayOf(1)) class MyClass9
@Ann(p = *intArrayOf(1, 2)) class MyClass10
fun box(): String {
test(MyClass1::class.java, "")
test(MyClass2::class.java, "1")
test(MyClass3::class.java, "12")
test(MyClass4::class.java, "")
test(MyClass5::class.java, "1")
test(MyClass6::class.java, "12")
test(MyClass7::class.java, "1")
test(MyClass8::class.java, "")
test(MyClass9::class.java, "1")
test(MyClass10::class.java, "12")
return "OK"
}
fun test(klass: Class<*>, expected: String) {
val ann = klass.getAnnotation(Ann::class.java)
if (ann == null) throw AssertionError("fail: cannot find Ann on ${klass}")
var result = ""
for (i in ann.p) {
result += i
}
if (result != expected) {
throw AssertionError("fail: expected = ${expected}, actual = ${result}")
}
}
@@ -0,0 +1,16 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS, NATIVE
fun test(a: String, b: String): String {
return a + b;
}
fun box(): String {
var res = "";
val call = test(b = {res += "K"; "K"}(), a = {res+="O"; "O"}())
if (res != "KO" || call != "OK") return "fail: $res != KO or $call != OK"
return "OK"
}
@@ -0,0 +1,34 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS, NATIVE
fun box(): String {
var invokeOrder = "";
val expectedResult = "0_1_9"
val expectedInvokeOrder = "1_0_9"
var l = 1L
var i = 0
val captured = 9L
var result = test(b = {invokeOrder += "1_"; l}(), a = {invokeOrder+="0_"; i}(), c = {invokeOrder += "$captured"; "$captured"})
if (invokeOrder != expectedInvokeOrder || result != expectedResult) return "fail 1: $invokeOrder != $expectedInvokeOrder or $result != $expectedResult"
invokeOrder = "";
result = test(b = {invokeOrder += "1_"; l}(), c = {invokeOrder += "$captured"; "$captured"}, a = {invokeOrder+="0_"; i}())
if (invokeOrder != expectedInvokeOrder || result != expectedResult) return "fail 2: $invokeOrder != $expectedInvokeOrder or $result != $expectedResult"
invokeOrder = "";
result = test(c = {invokeOrder += "$captured"; "$captured"}, b = {invokeOrder += "1_"; l}(), a = {invokeOrder+="0_"; i}())
if (invokeOrder != expectedInvokeOrder || result != expectedResult) return "fail 3: $invokeOrder != $expectedInvokeOrder or $result != $expectedResult"
invokeOrder = "";
result = test(a = {invokeOrder+="0_"; i}(), c = {invokeOrder += "$captured"; "$captured"}, b = {invokeOrder += "1_"; l}())
if (invokeOrder != "0_1_9" || result != expectedResult) return "fail 4: $invokeOrder != 0_1_9 or $result != $expectedResult"
return "OK"
}
fun test(a: Int, b: Long, c: () -> String): String {
return { "${a}_${b}_${c()}"} ()
}
@@ -0,0 +1,33 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS, NATIVE
fun box(): String {
var invokeOrder = "";
val expectedResult = "1.0_0_1_9"
val expectedInvokeOrder = "1_0_9"
var l = 1L
var i = 0
val captured = 9L
var result = 1.0.test(b = {invokeOrder += "1_"; l}(), a = {invokeOrder+="0_"; i}(), c = {invokeOrder += "$captured"; "$captured"})
if (invokeOrder != expectedInvokeOrder || result != expectedResult) return "fail 1: $invokeOrder != $expectedInvokeOrder or $result != $expectedResult"
invokeOrder = "";
result = 1.0.test(b = {invokeOrder += "1_"; l}(), c = {invokeOrder += "${captured}"; "${captured}"}, a = {invokeOrder+="0_"; i}())
if (invokeOrder != expectedInvokeOrder || result != expectedResult) return "fail 2: $invokeOrder != $expectedInvokeOrder or $result != $expectedResult"
invokeOrder = "";
result = 1.0.test(c = {invokeOrder += "${captured}"; "${captured}"}, b = {invokeOrder += "1_"; l}(), a = {invokeOrder+="0_"; i}())
if (invokeOrder != expectedInvokeOrder || result != expectedResult) return "fail 3: $invokeOrder != $expectedInvokeOrder or $result != $expectedResult"
invokeOrder = "";
result = 1.0.test(a = {invokeOrder+="0_"; i}(), c = {invokeOrder += "${captured}"; "${captured}"}, b = {invokeOrder += "1_"; l}())
if (invokeOrder != "0_1_9" || result != expectedResult) return "fail 4: $invokeOrder != 0_1_9 or $result != $expectedResult"
return "OK"
}
fun Double.test(a: Int, b: Long, c: () -> String): String {
return { "${this}_${a}_${b}_${c()}"} ()
}
@@ -0,0 +1,16 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS, NATIVE
var invokeOrder: String = ""
fun test(x: Double = { invokeOrder += "x"; 1.0 }(), a: String, y: Long = { invokeOrder += "y"; 1 }(), b: String): String {
return "" + x + a + b + y;
}
fun box(): String {
val funResult = test(b = { invokeOrder += "K"; "K" }(), a = { invokeOrder += "O"; "O" }())
if (invokeOrder != "KOxy" || funResult != "1.0OK1") return "fail: $invokeOrder != KOxy or $funResult != 1.0OK1"
return "OK"
}
@@ -0,0 +1,33 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS, NATIVE
fun box(): String {
var invokeOrder = "";
val expectedResult = "1.0_0_1_L"
val expectedInvokeOrder = "1_0_L"
var l = 1L
var i = 0
var result = 1.0.test(b = {invokeOrder += "1_"; l}(), a = {invokeOrder+="0_"; i}(), c = {invokeOrder += "L"; "L"})
if (invokeOrder != expectedInvokeOrder || result != expectedResult) return "fail 1: $invokeOrder != $expectedInvokeOrder or $result != $expectedResult"
invokeOrder = "";
result = 1.0.test(b = {invokeOrder += "1_"; l}(), c = {invokeOrder += "L"; "L"}, a = {invokeOrder+="0_"; i}())
if (invokeOrder != expectedInvokeOrder || result != expectedResult) return "fail 2: $invokeOrder != $expectedInvokeOrder or $result != $expectedResult"
invokeOrder = "";
result = 1.0.test(c = {invokeOrder += "L"; "L"}, b = {invokeOrder += "1_"; l}(), a = {invokeOrder+="0_"; i}())
if (invokeOrder != expectedInvokeOrder || result != expectedResult) return "fail 3: $invokeOrder != $expectedInvokeOrder or $result != $expectedResult"
invokeOrder = "";
result = 1.0.test(a = {invokeOrder+="0_"; i}(), c = {invokeOrder += "L"; "L"}, b = {invokeOrder += "1_"; l}())
if (invokeOrder != "0_1_L" || result != expectedResult) return "fail 4: $invokeOrder != 0_1_L or $result != $expectedResult"
return "OK"
}
fun Double.test(a: Int, b: Long, c: () -> String): String {
return "${this}_${a}_${b}_${c()}"
}
@@ -0,0 +1,40 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS, NATIVE
fun box(): String {
return Z().test()
}
class Z {
fun Double.test(a: Int, b: Long, c: () -> String): String {
return "${this}_${a}_${b}_${c()}"
}
fun test(): String {
var invokeOrder = "";
val expectedResult = "1.0_0_1_L"
val expectedInvokeOrder = "1_0_L"
var l = 1L
var i = 0
var result = 1.0.test(b = { invokeOrder += "1_"; l }(), a = { invokeOrder += "0_"; i }(), c = { invokeOrder += "L"; "L" })
if (invokeOrder != expectedInvokeOrder || result != expectedResult) return "fail 1: $invokeOrder != $expectedInvokeOrder or $result != $expectedResult"
invokeOrder = "";
result = 1.0.test(b = { invokeOrder += "1_"; l }(), c = { invokeOrder += "L"; "L" }, a = { invokeOrder += "0_"; i }())
if (invokeOrder != expectedInvokeOrder || result != expectedResult) return "fail 2: $invokeOrder != $expectedInvokeOrder or $result != $expectedResult"
invokeOrder = "";
result = 1.0.test(c = { invokeOrder += "L"; "L" }, b = { invokeOrder += "1_"; l }(), a = { invokeOrder += "0_"; i }())
if (invokeOrder != expectedInvokeOrder || result != expectedResult) return "fail 3: $invokeOrder != $expectedInvokeOrder or $result != $expectedResult"
invokeOrder = "";
result = 1.0.test(a = { invokeOrder += "0_"; i }(), c = { invokeOrder += "L"; "L" }, b = { invokeOrder += "1_"; l }())
if (invokeOrder != "0_1_L" || result != expectedResult) return "fail 4: $invokeOrder != 0_1_L or $result != $expectedResult"
return "OK"
}
}

Some files were not shown because too many files have changed in this diff Show More