Merge
This commit is contained in:
@@ -21,6 +21,7 @@ kotstd/kotstd.iml
|
|||||||
*.kt.S
|
*.kt.S
|
||||||
*.kt.exe
|
*.kt.exe
|
||||||
*.log
|
*.log
|
||||||
|
test.output
|
||||||
|
|
||||||
# Ignore Gradle GUI config
|
# Ignore Gradle GUI config
|
||||||
gradle-app.setting
|
gradle-app.setting
|
||||||
|
|||||||
+40
-29
@@ -143,7 +143,7 @@ kotlinNativeInterop {
|
|||||||
compilerOpts '-fPIC'
|
compilerOpts '-fPIC'
|
||||||
linkerOpts '-fPIC'
|
linkerOpts '-fPIC'
|
||||||
linker 'clang++'
|
linker 'clang++'
|
||||||
linkOutputs ':common:compileHash'
|
linkOutputs ':common:hostHash'
|
||||||
|
|
||||||
headers fileTree('../common/src/hash/headers') {
|
headers fileTree('../common/src/hash/headers') {
|
||||||
include '**/*.h'
|
include '**/*.h'
|
||||||
@@ -182,47 +182,58 @@ dependencies {
|
|||||||
|
|
||||||
build.dependsOn 'compilerClasses','cli_bcClasses','bc_frontendClasses'
|
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,
|
// These files are built before the 'dist' is complete,
|
||||||
// so we provide custom values for
|
// so we provide custom values for
|
||||||
// -runtime, -properties, -library and -Djava.library.path
|
// -runtime, -properties, -library and -Djava.library.path
|
||||||
|
|
||||||
task stdlib(type: JavaExec) {
|
targetList.each { target ->
|
||||||
main = 'org.jetbrains.kotlin.cli.bc.K2NativeKt'
|
task("${target}Stdlib", type: JavaExec) {
|
||||||
classpath = project.configurations.cli_bc
|
main = 'org.jetbrains.kotlin.cli.bc.K2NativeKt'
|
||||||
jvmArgs "-ea",
|
classpath = project.configurations.cli_bc
|
||||||
"-Dkonan.home=${project.parent.file('dist')}",
|
jvmArgs "-ea",
|
||||||
"-Djava.library.path=${project.buildDir}/nativelibs"
|
"-Dkonan.home=${project.parent.file('dist')}",
|
||||||
args('-output', project(':runtime').file('build/stdlib.kt.bc'),
|
"-Djava.library.path=${project.buildDir}/nativelibs"
|
||||||
'-nolink', '-nostdlib',
|
args('-output', project(':runtime').file("build/${target}/stdlib.kt.bc"),
|
||||||
'-runtime', project(':runtime').file('build/runtime.bc'),
|
'-nolink', '-nostdlib',
|
||||||
'-properties', project(':backend.native').file('konan.properties'),
|
'-target', target,
|
||||||
|
'-runtime', project(':runtime').file("build/${target}/runtime.bc"),
|
||||||
|
'-properties', project(':backend.native').file('konan.properties'),
|
||||||
project(':runtime').file('src/main/kotlin'),
|
project(':runtime').file('src/main/kotlin'),
|
||||||
*project.globalArgs)
|
*project.globalArgs)
|
||||||
|
|
||||||
inputs.dir(project(':runtime').file('src/main/kotlin'))
|
inputs.dir(project(':runtime').file('src/main/kotlin'))
|
||||||
outputs.file(project(':runtime').file('build/stdlib.kt.bc'))
|
outputs.file(project(':runtime').file("build/${target}/stdlib.kt.bc"))
|
||||||
|
|
||||||
dependsOn ':runtime:build'
|
dependsOn ":runtime:${target}Runtime"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
task start(type: JavaExec) {
|
targetList.each { target ->
|
||||||
main = 'org.jetbrains.kotlin.cli.bc.K2NativeKt'
|
task("${target}Start", type: JavaExec) {
|
||||||
classpath = project.configurations.cli_bc
|
main = 'org.jetbrains.kotlin.cli.bc.K2NativeKt'
|
||||||
jvmArgs "-ea",
|
classpath = project.configurations.cli_bc
|
||||||
"-Dkonan.home=${project.parent.file('dist')}",
|
jvmArgs "-ea",
|
||||||
"-Djava.library.path=${project.buildDir}/nativelibs"
|
"-Dkonan.home=${project.parent.file('dist')}",
|
||||||
args('-output', project(':runtime').file('build/start.kt.bc'),
|
"-Djava.library.path=${project.buildDir}/nativelibs"
|
||||||
'-nolink', '-nostdlib',
|
args('-output', project(':runtime').file("build/${target}/start.kt.bc"),
|
||||||
'-library', project(':runtime').file('build/stdlib.kt.bc'),
|
'-nolink', '-nostdlib',
|
||||||
'-runtime', project(':runtime').file('build/runtime.bc'),
|
'-target', target,
|
||||||
'-properties', project(':backend.native').file('konan.properties'),
|
'-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(':runtime').file('src/launcher/kotlin'),
|
||||||
*project.globalArgs)
|
*project.globalArgs)
|
||||||
|
|
||||||
inputs.dir(project(':runtime').file('src/launcher/kotlin'))
|
inputs.dir(project(':runtime').file('src/launcher/kotlin'))
|
||||||
outputs.file(project(':runtime').file('build/start.kt.bc'))
|
outputs.file(project(':runtime').file("build/${target}/start.kt.bc"))
|
||||||
|
|
||||||
dependsOn ':runtime:build', 'stdlib'
|
dependsOn ":runtime:${target}Runtime", "${target}Stdlib"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
task run {
|
task run {
|
||||||
@@ -237,7 +248,7 @@ task jars(type: Jar) {
|
|||||||
'build/classes/hashInteropStubs',
|
'build/classes/hashInteropStubs',
|
||||||
'build/classes/llvmInteropStubs'
|
'build/classes/llvmInteropStubs'
|
||||||
|
|
||||||
dependsOn 'build', ':runtime:build', 'external_jars'
|
dependsOn 'build', ':runtime:hostRuntime', 'external_jars'
|
||||||
}
|
}
|
||||||
|
|
||||||
task external_jars(type: Copy) {
|
task external_jars(type: Copy) {
|
||||||
|
|||||||
@@ -77,11 +77,13 @@ class K2Native : CLICompiler<K2NativeCompilerArguments>() {
|
|||||||
|
|
||||||
put(EXECUTABLE_FILE,
|
put(EXECUTABLE_FILE,
|
||||||
arguments.outputFile ?: "program.kexe")
|
arguments.outputFile ?: "program.kexe")
|
||||||
put(RUNTIME_FILE,
|
if (arguments.runtimeFile != null)
|
||||||
arguments.runtimeFile ?: Distribution.runtime)
|
put(RUNTIME_FILE, arguments.runtimeFile)
|
||||||
put(PROPERTY_FILE,
|
if (arguments.propertyFile != null)
|
||||||
arguments.propertyFile ?: Distribution.propertyFile)
|
put(PROPERTY_FILE, arguments.propertyFile)
|
||||||
|
if (arguments.target != null)
|
||||||
|
put(TARGET, arguments.target)
|
||||||
|
put(LIST_TARGETS, arguments.listTargets)
|
||||||
put(OPTIMIZATION, arguments.optimization)
|
put(OPTIMIZATION, arguments.optimization)
|
||||||
|
|
||||||
put(PRINT_IR, arguments.printIr)
|
put(PRINT_IR, arguments.printIr)
|
||||||
|
|||||||
@@ -30,6 +30,13 @@ public class K2NativeCompilerArguments extends CommonCompilerArguments {
|
|||||||
@Argument(value = "opt", description = "Enable optimizations during compilation")
|
@Argument(value = "opt", description = "Enable optimizations during compilation")
|
||||||
public boolean optimization;
|
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")
|
@Argument(value = "print_ir", description = "Print IR")
|
||||||
public boolean printIr;
|
public boolean printIr;
|
||||||
|
|
||||||
|
|||||||
+109
@@ -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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+13
-4
@@ -37,6 +37,15 @@ abstract class AbstractValueUsageTransformer(val builtIns: KotlinBuiltIns): IrEl
|
|||||||
protected open fun IrExpression.useAsArgument(parameter: ParameterDescriptor): IrExpression =
|
protected open fun IrExpression.useAsArgument(parameter: ParameterDescriptor): IrExpression =
|
||||||
this.useAsValue(parameter)
|
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 =
|
protected open fun IrExpression.useForVariable(variable: VariableDescriptor): IrExpression =
|
||||||
this.useAsValue(variable)
|
this.useAsValue(variable)
|
||||||
|
|
||||||
@@ -55,12 +64,12 @@ abstract class AbstractValueUsageTransformer(val builtIns: KotlinBuiltIns): IrEl
|
|||||||
expression.transformChildrenVoid(this)
|
expression.transformChildrenVoid(this)
|
||||||
|
|
||||||
with(expression) {
|
with(expression) {
|
||||||
dispatchReceiver = dispatchReceiver?.useAsArgument(descriptor.dispatchReceiverParameter!!)
|
dispatchReceiver = dispatchReceiver?.useAsDispatchReceiver(descriptor)
|
||||||
extensionReceiver = extensionReceiver?.useAsArgument(descriptor.extensionReceiverParameter!!)
|
extensionReceiver = extensionReceiver?.useAsExtensionReceiver(descriptor)
|
||||||
for (index in descriptor.valueParameters.indices) {
|
for (index in descriptor.valueParameters.indices) {
|
||||||
val argument = getValueArgument(index) ?: continue
|
val argument = getValueArgument(index) ?: continue
|
||||||
val parameter = descriptor.valueParameters[index]
|
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 ->
|
declaration.descriptor.valueParameters.forEach { parameter ->
|
||||||
val defaultValue = declaration.getDefault(parameter)
|
val defaultValue = declaration.getDefault(parameter)
|
||||||
if (defaultValue is IrExpressionBody) {
|
if (defaultValue is IrExpressionBody) {
|
||||||
defaultValue.expression = defaultValue.expression.useAsArgument(parameter)
|
defaultValue.expression = defaultValue.expression.useAsValueArgument(parameter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+655
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+68
-16
@@ -1,36 +1,88 @@
|
|||||||
package org.jetbrains.kotlin.backend.common.lower
|
package org.jetbrains.kotlin.backend.common.lower
|
||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.BackendContext
|
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.IrElement
|
||||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||||
import org.jetbrains.kotlin.ir.builders.*
|
import org.jetbrains.kotlin.ir.builders.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
|
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.types.KotlinType
|
||||||
|
import org.jetbrains.kotlin.utils.Printer
|
||||||
|
|
||||||
class IrLoweringContext(backendContext: BackendContext) : IrGeneratorContext(backendContext.irBuiltIns)
|
class IrLoweringContext(backendContext: BackendContext) : IrGeneratorContext(backendContext.irBuiltIns)
|
||||||
|
|
||||||
class FunctionIrGenerator(backendContext: BackendContext, functionDescriptor: FunctionDescriptor) : IrGeneratorWithScope {
|
class FunctionIrBuilder(backendContext: BackendContext, functionDescriptor: FunctionDescriptor) :
|
||||||
override val context = IrLoweringContext(backendContext)
|
IrBuilderWithScope(
|
||||||
override val scope = Scope(functionDescriptor)
|
IrLoweringContext(backendContext),
|
||||||
}
|
Scope(functionDescriptor),
|
||||||
|
UNDEFINED_OFFSET,
|
||||||
|
UNDEFINED_OFFSET
|
||||||
|
)
|
||||||
|
|
||||||
fun BackendContext.createFunctionIrGenerator(functionDescriptor: FunctionDescriptor) =
|
fun BackendContext.createFunctionIrBuilder(functionDescriptor: FunctionDescriptor) =
|
||||||
FunctionIrGenerator(this, functionDescriptor)
|
FunctionIrBuilder(this, functionDescriptor)
|
||||||
|
|
||||||
class FunctionIrBuilder(context: IrLoweringContext, scope: Scope, startOffset: Int, endOffset: Int) :
|
fun <T : IrBuilder> T.at(element: IrElement) = this.at(element.startOffset, element.endOffset)
|
||||||
IrBuilderWithScope(context, scope, startOffset, endOffset)
|
|
||||||
|
|
||||||
fun FunctionIrGenerator.createIrBuilder() = FunctionIrBuilder(context, scope, UNDEFINED_OFFSET, UNDEFINED_OFFSET)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds [IrBlock] to be used instead of given expression.
|
* Builds [IrBlock] to be used instead of given expression.
|
||||||
*/
|
*/
|
||||||
inline fun FunctionIrGenerator.irBlock(expression: IrExpression, origin: IrStatementOrigin? = null,
|
inline fun IrGeneratorWithScope.irBlock(expression: IrExpression, origin: IrStatementOrigin? = null,
|
||||||
resultType: KotlinType? = expression.type,
|
resultType: KotlinType? = expression.type,
|
||||||
body: IrBlockBuilder.() -> Unit) =
|
body: IrBlockBuilder.() -> Unit) =
|
||||||
this.irBlock(expression.startOffset, expression.endOffset, origin, resultType, body)
|
this.irBlock(expression.startOffset, expression.endOffset, origin, resultType, body)
|
||||||
|
|
||||||
inline fun FunctionIrGenerator.irBlockBody(irElement: IrElement, body: IrBlockBodyBuilder.() -> Unit) =
|
inline fun IrGeneratorWithScope.irBlockBody(irElement: IrElement, body: IrBlockBodyBuilder.() -> Unit) =
|
||||||
this.irBlockBody(irElement.startOffset, irElement.endOffset, body)
|
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")
|
||||||
|
|
||||||
|
}
|
||||||
+34
-14
@@ -1,28 +1,47 @@
|
|||||||
package org.jetbrains.kotlin.backend.konan
|
package org.jetbrains.kotlin.backend.konan
|
||||||
|
|
||||||
import llvm.*
|
import llvm.LLVMDumpModule
|
||||||
import org.jetbrains.kotlin.backend.konan.ir.Ir
|
import llvm.LLVMModuleRef
|
||||||
import org.jetbrains.kotlin.backend.konan.ir.ModuleIndex
|
import org.jetbrains.kotlin.backend.jvm.descriptors.initialize
|
||||||
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 org.jetbrains.kotlin.backend.konan.descriptors.deepPrint
|
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.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.ir.util.DumpIrTreeVisitor
|
||||||
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver
|
||||||
import java.lang.System.out
|
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() {
|
internal final class Context(val config: KonanConfig) : KonanBackendContext() {
|
||||||
|
|
||||||
var moduleDescriptor: ModuleDescriptor? = null
|
var moduleDescriptor: ModuleDescriptor? = null
|
||||||
|
|
||||||
|
val specialDescriptorsFactory = SpecialDescriptorsFactory()
|
||||||
|
|
||||||
|
// TODO: make lateinit?
|
||||||
var irModule: IrModuleFragment? = null
|
var irModule: IrModuleFragment? = null
|
||||||
set(module: IrModuleFragment?) {
|
set(module: IrModuleFragment?) {
|
||||||
if (field != null) {
|
if (field != null) {
|
||||||
@@ -49,6 +68,7 @@ internal final class Context(val config: KonanConfig) : KonanBackendContext() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
lateinit var llvm: Llvm
|
lateinit var llvm: Llvm
|
||||||
|
lateinit var llvmDeclarations: LlvmDeclarations
|
||||||
|
|
||||||
var phase: KonanPhase? = null
|
var phase: KonanPhase? = null
|
||||||
var depth: Int = 0
|
var depth: Int = 0
|
||||||
|
|||||||
+34
-28
@@ -1,36 +1,42 @@
|
|||||||
package org.jetbrains.kotlin.backend.konan
|
package org.jetbrains.kotlin.backend.konan
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.Properties
|
import java.util.Properties
|
||||||
|
|
||||||
public class Distribution(val properties: KonanProperties,
|
public class Distribution(val config: CompilerConfiguration) {
|
||||||
val os: String, val target: String) {
|
|
||||||
|
|
||||||
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 {
|
private fun findKonanHome(): String {
|
||||||
val value = System.getProperty("konan.home", "dist")
|
val value = System.getProperty("konan.home", "dist")
|
||||||
val path = File(value).absolutePath
|
val path = File(value).absolutePath
|
||||||
return path
|
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"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val llvmHome = "$dependencies/${properties.propertyString("llvmHome.$os")}"
|
val konanHome = findKonanHome()
|
||||||
val sysRoot = "$dependencies/${properties.propertyString("sysRoot.$os")}"
|
val propertyFile = config.get(KonanConfigKeys.PROPERTY_FILE)
|
||||||
val libGcc = "$dependencies/${properties.propertyString("libGcc.$os")}"
|
?: "$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 llvmBin = "$llvmHome/bin"
|
||||||
val llvmLib = "$llvmHome/lib"
|
val llvmLib = "$llvmHome/lib"
|
||||||
@@ -40,9 +46,9 @@ public class Distribution(val properties: KonanProperties,
|
|||||||
val llvmLto = "$llvmBin/llvm-lto"
|
val llvmLto = "$llvmBin/llvm-lto"
|
||||||
val llvmLink = "$llvmBin/llvm-link"
|
val llvmLink = "$llvmBin/llvm-link"
|
||||||
val libCppAbi = "$llvmLib/libc++abi.a"
|
val libCppAbi = "$llvmLib/libc++abi.a"
|
||||||
val libLTO = when (os) {
|
val libLTO = when (TargetManager.host) {
|
||||||
"osx" -> "$llvmLib/libLTO.dylib"
|
KonanTarget.MACBOOK -> "$llvmLib/libLTO.dylib"
|
||||||
"linux" -> "$llvmLib/libLTO.so"
|
KonanTarget.LINUX -> "$llvmLib/libLTO.so"
|
||||||
else -> error("Don't know libLTO location for the platform.")
|
else -> error("Don't know libLTO location for this platform.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-1
@@ -16,13 +16,15 @@ import java.io.File
|
|||||||
|
|
||||||
class KonanConfig(val project: Project, val configuration: CompilerConfiguration) {
|
class KonanConfig(val project: Project, val configuration: CompilerConfiguration) {
|
||||||
|
|
||||||
|
internal val distribution = Distribution(configuration)
|
||||||
|
|
||||||
internal val libraries: List<String>
|
internal val libraries: List<String>
|
||||||
get() {
|
get() {
|
||||||
val fromCommandLine = configuration.getList(KonanConfigKeys.LIBRARY_FILES)
|
val fromCommandLine = configuration.getList(KonanConfigKeys.LIBRARY_FILES)
|
||||||
if (configuration.get(KonanConfigKeys.NOSTDLIB) ?: false) {
|
if (configuration.get(KonanConfigKeys.NOSTDLIB) ?: false) {
|
||||||
return fromCommandLine
|
return fromCommandLine
|
||||||
}
|
}
|
||||||
return fromCommandLine + Distribution.stdlib
|
return fromCommandLine + distribution.stdlib
|
||||||
}
|
}
|
||||||
|
|
||||||
private val loadedDescriptors = loadLibMetadata(libraries)
|
private val loadedDescriptors = loadLibMetadata(libraries)
|
||||||
|
|||||||
+30
-26
@@ -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.config.CompilerConfigurationKey
|
||||||
import org.jetbrains.kotlin.serialization.js.ModuleKind;
|
import org.jetbrains.kotlin.serialization.js.ModuleKind
|
||||||
|
|
||||||
class KonanConfigKeys {
|
class KonanConfigKeys {
|
||||||
companion object {
|
companion object {
|
||||||
val LIBRARY_FILES: CompilerConfigurationKey<List<String>>
|
val LIBRARY_FILES: CompilerConfigurationKey<List<String>>
|
||||||
= CompilerConfigurationKey.create("library file paths");
|
= CompilerConfigurationKey.create("library file paths")
|
||||||
val BITCODE_FILE: CompilerConfigurationKey<String>
|
val BITCODE_FILE: CompilerConfigurationKey<String>
|
||||||
= CompilerConfigurationKey.create("emitted bitcode file path");
|
= CompilerConfigurationKey.create("emitted bitcode file path")
|
||||||
val EXECUTABLE_FILE: CompilerConfigurationKey<String>
|
val EXECUTABLE_FILE: CompilerConfigurationKey<String>
|
||||||
= CompilerConfigurationKey.create("final executable file path");
|
= CompilerConfigurationKey.create("final executable file path")
|
||||||
val RUNTIME_FILE: CompilerConfigurationKey<String?>
|
val RUNTIME_FILE: CompilerConfigurationKey<String?>
|
||||||
= CompilerConfigurationKey.create("override default runtime file path");
|
= CompilerConfigurationKey.create("override default runtime file path")
|
||||||
val PROPERTY_FILE: CompilerConfigurationKey<String?>
|
val PROPERTY_FILE: CompilerConfigurationKey<String?>
|
||||||
= CompilerConfigurationKey.create("override default property file path");
|
= CompilerConfigurationKey.create("override default property file path")
|
||||||
val ABI_VERSION: CompilerConfigurationKey<Int>
|
val ABI_VERSION: CompilerConfigurationKey<Int>
|
||||||
= CompilerConfigurationKey.create("current abi version");
|
= CompilerConfigurationKey.create("current abi version")
|
||||||
val OPTIMIZATION: CompilerConfigurationKey<Boolean>
|
val OPTIMIZATION: CompilerConfigurationKey<Boolean>
|
||||||
= CompilerConfigurationKey.create("optimized compilation");
|
= CompilerConfigurationKey.create("optimized compilation")
|
||||||
val NOSTDLIB: CompilerConfigurationKey<Boolean>
|
val NOSTDLIB: CompilerConfigurationKey<Boolean>
|
||||||
= CompilerConfigurationKey.create("don't link with stdlib");
|
= CompilerConfigurationKey.create("don't link with stdlib")
|
||||||
val NOLINK: CompilerConfigurationKey<Boolean>
|
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>>
|
val SOURCE_MAP: CompilerConfigurationKey<List<String>>
|
||||||
= CompilerConfigurationKey.create("generate source map");
|
= CompilerConfigurationKey.create("generate source map")
|
||||||
val META_INFO: CompilerConfigurationKey<List<String>>
|
val META_INFO: CompilerConfigurationKey<List<String>>
|
||||||
= CompilerConfigurationKey.create("generate metadata");
|
= CompilerConfigurationKey.create("generate metadata")
|
||||||
val MODULE_KIND: CompilerConfigurationKey<ModuleKind>
|
val MODULE_KIND: CompilerConfigurationKey<ModuleKind>
|
||||||
= CompilerConfigurationKey.create("module kind");
|
= CompilerConfigurationKey.create("module kind")
|
||||||
|
|
||||||
val VERIFY_IR: CompilerConfigurationKey<Boolean>
|
val VERIFY_IR: CompilerConfigurationKey<Boolean>
|
||||||
= CompilerConfigurationKey.create("verify ir");
|
= CompilerConfigurationKey.create("verify ir")
|
||||||
val VERIFY_DESCRIPTORS: CompilerConfigurationKey<Boolean>
|
val VERIFY_DESCRIPTORS: CompilerConfigurationKey<Boolean>
|
||||||
= CompilerConfigurationKey.create("verify descriptors");
|
= CompilerConfigurationKey.create("verify descriptors")
|
||||||
val VERIFY_BITCODE: CompilerConfigurationKey<Boolean>
|
val VERIFY_BITCODE: CompilerConfigurationKey<Boolean>
|
||||||
= CompilerConfigurationKey.create("verify bitcode");
|
= CompilerConfigurationKey.create("verify bitcode")
|
||||||
|
|
||||||
val PRINT_IR: CompilerConfigurationKey<Boolean>
|
val PRINT_IR: CompilerConfigurationKey<Boolean>
|
||||||
= CompilerConfigurationKey.create("print ir");
|
= CompilerConfigurationKey.create("print ir")
|
||||||
val PRINT_DESCRIPTORS: CompilerConfigurationKey<Boolean>
|
val PRINT_DESCRIPTORS: CompilerConfigurationKey<Boolean>
|
||||||
= CompilerConfigurationKey.create("print descriptors");
|
= CompilerConfigurationKey.create("print descriptors")
|
||||||
val PRINT_BITCODE: CompilerConfigurationKey<Boolean>
|
val PRINT_BITCODE: CompilerConfigurationKey<Boolean>
|
||||||
= CompilerConfigurationKey.create("print bitcode");
|
= CompilerConfigurationKey.create("print bitcode")
|
||||||
|
|
||||||
val ENABLED_PHASES: CompilerConfigurationKey<List<String>>
|
val ENABLED_PHASES: CompilerConfigurationKey<List<String>>
|
||||||
= CompilerConfigurationKey.create("enable backend phases");
|
= CompilerConfigurationKey.create("enable backend phases")
|
||||||
val DISABLED_PHASES: CompilerConfigurationKey<List<String>>
|
val DISABLED_PHASES: CompilerConfigurationKey<List<String>>
|
||||||
= CompilerConfigurationKey.create("disable backend phases");
|
= CompilerConfigurationKey.create("disable backend phases")
|
||||||
val VERBOSE_PHASES: CompilerConfigurationKey<List<String>>
|
val VERBOSE_PHASES: CompilerConfigurationKey<List<String>>
|
||||||
= CompilerConfigurationKey.create("verbose backend phases");
|
= CompilerConfigurationKey.create("verbose backend phases")
|
||||||
val LIST_PHASES: CompilerConfigurationKey<Boolean>
|
val LIST_PHASES: CompilerConfigurationKey<Boolean>
|
||||||
= CompilerConfigurationKey.create("list backend phases");
|
= CompilerConfigurationKey.create("list backend phases")
|
||||||
val TIME_PHASES: CompilerConfigurationKey<Boolean>
|
val TIME_PHASES: CompilerConfigurationKey<Boolean>
|
||||||
= CompilerConfigurationKey.create("time backend phases");
|
= CompilerConfigurationKey.create("time backend phases")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
@@ -4,12 +4,14 @@ import org.jetbrains.kotlin.analyzer.AnalysisResult
|
|||||||
import org.jetbrains.kotlin.backend.konan.llvm.emitLLVM
|
import org.jetbrains.kotlin.backend.konan.llvm.emitLLVM
|
||||||
import org.jetbrains.kotlin.backend.konan.util.profile
|
import org.jetbrains.kotlin.backend.konan.util.profile
|
||||||
import org.jetbrains.kotlin.backend.konan.Context
|
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.CLICompiler
|
||||||
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||||
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
|
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||||
|
import org.jetbrains.kotlin.config.kotlinSourceRoots
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.psi2ir.Psi2IrConfiguration
|
import org.jetbrains.kotlin.psi2ir.Psi2IrConfiguration
|
||||||
@@ -34,11 +36,18 @@ public fun runTopLevelPhases(konanConfig: KonanConfig, environment: KotlinCoreEn
|
|||||||
|
|
||||||
val config = konanConfig.configuration
|
val config = konanConfig.configuration
|
||||||
|
|
||||||
|
val targets = TargetManager(config)
|
||||||
|
if (config.get(KonanConfigKeys.LIST_TARGETS) ?: false) {
|
||||||
|
targets.list()
|
||||||
|
}
|
||||||
|
|
||||||
KonanPhases.config(konanConfig)
|
KonanPhases.config(konanConfig)
|
||||||
if (config.get(KonanConfigKeys.LIST_PHASES) ?: false) {
|
if (config.get(KonanConfigKeys.LIST_PHASES) ?: false) {
|
||||||
KonanPhases.list()
|
KonanPhases.list()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config.kotlinSourceRoots.isEmpty()) return
|
||||||
|
|
||||||
val collector = config.getNotNull(
|
val collector = config.getNotNull(
|
||||||
CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
|
CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
|
||||||
|
|
||||||
@@ -67,6 +76,7 @@ public fun runTopLevelPhases(konanConfig: KonanConfig, environment: KotlinCoreEn
|
|||||||
phaser.phase(KonanPhase.BACKEND) {
|
phaser.phase(KonanPhase.BACKEND) {
|
||||||
phaser.phase(KonanPhase.LOWER) {
|
phaser.phase(KonanPhase.LOWER) {
|
||||||
KonanLower(context).lower()
|
KonanLower(context).lower()
|
||||||
|
context.ir.moduleIndexForCodegen = ModuleIndex(context.ir.irModule)
|
||||||
}
|
}
|
||||||
phaser.phase(KonanPhase.BITCODE) {
|
phaser.phase(KonanPhase.BITCODE) {
|
||||||
emitLLVM(context)
|
emitLLVM(context)
|
||||||
|
|||||||
+10
-1
@@ -20,6 +20,15 @@ internal class KonanLower(val context: Context) {
|
|||||||
phaser.phase(KonanPhase.LOWER_INLINE) {
|
phaser.phase(KonanPhase.LOWER_INLINE) {
|
||||||
FunctionInlining(context).inline(irFile)
|
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) {
|
phaser.phase(KonanPhase.LOWER_DEFAULT_PARAMETER_EXTENT) {
|
||||||
DefaultParameterStubGenerator(context).runOnFilePostfix(irFile)
|
DefaultParameterStubGenerator(context).runOnFilePostfix(irFile)
|
||||||
DefaultParameterInjector(context).runOnFilePostfix(irFile)
|
DefaultParameterInjector(context).runOnFilePostfix(irFile)
|
||||||
@@ -34,7 +43,7 @@ internal class KonanLower(val context: Context) {
|
|||||||
SharedVariablesLowering(context).runOnFilePostfix(irFile)
|
SharedVariablesLowering(context).runOnFilePostfix(irFile)
|
||||||
}
|
}
|
||||||
phaser.phase(KonanPhase.LOWER_LOCAL_FUNCTIONS) {
|
phaser.phase(KonanPhase.LOWER_LOCAL_FUNCTIONS) {
|
||||||
LocalFunctionsLowering(context).runOnFilePostfix(irFile)
|
LocalDeclarationsLowering(context).runOnFilePostfix(irFile)
|
||||||
}
|
}
|
||||||
phaser.phase(KonanPhase.LOWER_CALLABLES) {
|
phaser.phase(KonanPhase.LOWER_CALLABLES) {
|
||||||
CallableReferenceLowering(context).runOnFilePostfix(irFile)
|
CallableReferenceLowering(context).runOnFilePostfix(irFile)
|
||||||
|
|||||||
+3
@@ -11,6 +11,7 @@ enum class KonanPhase(val description: String,
|
|||||||
/* */ BACKEND("All backend"),
|
/* */ BACKEND("All backend"),
|
||||||
/* ... */ LOWER("IR Lowering"),
|
/* ... */ LOWER("IR Lowering"),
|
||||||
/* ... ... */ LOWER_BUILTIN_OPERATORS("BuiltIn Operators Lowering"),
|
/* ... ... */ LOWER_BUILTIN_OPERATORS("BuiltIn Operators Lowering"),
|
||||||
|
/* ... ... */ LOWER_VARARG("Vararg lowering"),
|
||||||
/* ... ... */ LOWER_DEFAULT_PARAMETER_EXTENT("Default Parameter Extent Lowering"),
|
/* ... ... */ LOWER_DEFAULT_PARAMETER_EXTENT("Default Parameter Extent Lowering"),
|
||||||
/* ... ... */ LOWER_TYPE_OPERATORS("Type operators lowering"),
|
/* ... ... */ LOWER_TYPE_OPERATORS("Type operators lowering"),
|
||||||
/* ... ... */ LOWER_SHARED_VARIABLES("Shared Variable Lowering"),
|
/* ... ... */ LOWER_SHARED_VARIABLES("Shared Variable Lowering"),
|
||||||
@@ -18,6 +19,8 @@ enum class KonanPhase(val description: String,
|
|||||||
/* ... ... */ LOWER_CALLABLES("Callable references Lowering"),
|
/* ... ... */ LOWER_CALLABLES("Callable references Lowering"),
|
||||||
/* ... ... */ LOWER_INLINE("Functions inlining"),
|
/* ... ... */ LOWER_INLINE("Functions inlining"),
|
||||||
/* ... ... */ AUTOBOX("Autoboxing of primitive types"),
|
/* ... ... */ AUTOBOX("Autoboxing of primitive types"),
|
||||||
|
/* ... ... */ LOWER_ENUMS("Enum classes lowering"),
|
||||||
|
/* ... ... */ LOWER_INNER_CLASSES("Inner classes lowering"),
|
||||||
/* ... */ BITCODE("LLVM BitCode Generation"),
|
/* ... */ BITCODE("LLVM BitCode Generation"),
|
||||||
/* ... ... */ RTTI("RTTI Generation"),
|
/* ... ... */ RTTI("RTTI Generation"),
|
||||||
/* ... ... */ CODEGEN("Code Generation"),
|
/* ... ... */ CODEGEN("Code Generation"),
|
||||||
|
|||||||
+2
-3
@@ -1,15 +1,14 @@
|
|||||||
package org.jetbrains.kotlin.backend.konan
|
package org.jetbrains.kotlin.backend.konan
|
||||||
|
|
||||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.Properties
|
import java.util.Properties
|
||||||
|
|
||||||
public class KonanProperties(config: CompilerConfiguration) {
|
public class KonanProperties(val propertyFile: String) {
|
||||||
|
|
||||||
val properties = Properties()
|
val properties = Properties()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val file = File(config.get(KonanConfigKeys.PROPERTY_FILE))
|
val file = File(propertyFile)
|
||||||
file.bufferedReader()?.use { reader ->
|
file.bufferedReader()?.use { reader ->
|
||||||
properties.load(reader)
|
properties.load(reader)
|
||||||
}
|
}
|
||||||
|
|||||||
+106
@@ -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)
|
||||||
|
}
|
||||||
|
|
||||||
+74
-53
@@ -10,8 +10,8 @@ typealias ExecutableFile = String
|
|||||||
|
|
||||||
// Use "clang -v -save-temps" to write linkCommand() method
|
// Use "clang -v -save-temps" to write linkCommand() method
|
||||||
// for another implementation of this class.
|
// for another implementation of this class.
|
||||||
internal abstract class PlatformFlags(val distrib: Distribution,
|
internal abstract class PlatformFlags(val distribution: Distribution) {
|
||||||
val properties: KonanProperties) {
|
val properties = distribution.properties
|
||||||
|
|
||||||
abstract val llvmLtoFlags: List<String>
|
abstract val llvmLtoFlags: List<String>
|
||||||
abstract val llvmLlcFlags: List<String>
|
abstract val llvmLlcFlags: List<String>
|
||||||
@@ -25,8 +25,8 @@ internal abstract class PlatformFlags(val distrib: Distribution,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
internal class MacOSPlatform(distrib: Distribution,
|
internal open class MacOSPlatform(distribution: Distribution)
|
||||||
properties: KonanProperties) : PlatformFlags(distrib, properties) {
|
: PlatformFlags(distribution) {
|
||||||
|
|
||||||
override val llvmLtoFlags = properties.propertyList("llvmLtoFlags.osx")
|
override val llvmLtoFlags = properties.propertyList("llvmLtoFlags.osx")
|
||||||
override val llvmLlcFlags = properties.propertyList("llvmLlcFlags.osx")
|
override val llvmLlcFlags = properties.propertyList("llvmLlcFlags.osx")
|
||||||
@@ -34,28 +34,49 @@ internal class MacOSPlatform(distrib: Distribution,
|
|||||||
override val linkerOptimizationFlags =
|
override val linkerOptimizationFlags =
|
||||||
properties.propertyList("linkerOptimizationFlags.osx")
|
properties.propertyList("linkerOptimizationFlags.osx")
|
||||||
override val linkerKonanFlags = properties.propertyList("linkerKonanFlags.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> {
|
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") +
|
return mutableListOf<String>(linker, "-demangle") +
|
||||||
if (optimize) listOf("-object_path_lto", "temporary.o", "-lto_library", distrib.libLTO) else {listOf<String>()} +
|
if (optimize) listOf("-object_path_lto", "temporary.o", "-lto_library", distribution.libLTO) else {listOf<String>()} +
|
||||||
listOf( "-dynamic", "-arch", "x86_64", "-macosx_version_min") +
|
listOf( "-dynamic", "-arch", arch) +
|
||||||
properties.propertyList("macosVersionMin.osx") +
|
osVersionMin +
|
||||||
listOf("-syslibroot", "$sysRoot",
|
listOf("-syslibroot", "$targetSysRoot",
|
||||||
"-o", executable) +
|
"-o", executable) +
|
||||||
objectFiles +
|
objectFiles +
|
||||||
if (optimize) linkerOptimizationFlags else {listOf<String>()} +
|
if (optimize) linkerOptimizationFlags else {listOf<String>()} +
|
||||||
linkerKonanFlags +
|
linkerKonanFlags +
|
||||||
listOf("-lSystem", "$llvmLib/clang/3.8.0/lib/darwin/libclang_rt.osx.a")
|
listOf("-lSystem")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class LinuxPlatform(distrib: Distribution,
|
internal class IPhoneOSfromMacOSPlatform(distribution: Distribution)
|
||||||
properties: KonanProperties) : PlatformFlags(distrib, properties) {
|
: 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 llvmLtoFlags = properties.propertyList("llvmLtoFlags.linux")
|
||||||
override val llvmLlcFlags = properties.propertyList("llvmLlcFlags.linux")
|
override val llvmLlcFlags = properties.propertyList("llvmLlcFlags.linux")
|
||||||
@@ -63,16 +84,16 @@ internal class LinuxPlatform(distrib: Distribution,
|
|||||||
override val linkerOptimizationFlags =
|
override val linkerOptimizationFlags =
|
||||||
properties.propertyList("linkerOptimizationFlags.linux")
|
properties.propertyList("linkerOptimizationFlags.linux")
|
||||||
override val linkerKonanFlags = properties.propertyList("linkerKonanFlags.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 =
|
val pluginOptimizationFlags =
|
||||||
properties.propertyList("pluginOptimizationFlags.linux")
|
properties.propertyList("pluginOptimizationFlags.linux")
|
||||||
|
|
||||||
override fun linkCommand(objectFiles: List<String>, executable: String, optimize: Boolean): List<String> {
|
override fun linkCommand(objectFiles: List<String>, executable: String, optimize: Boolean): List<String> {
|
||||||
|
|
||||||
val sysRoot = distrib.sysRoot
|
val sysRoot = distribution.sysRoot
|
||||||
val llvmLib = distrib.llvmLib
|
val llvmLib = distribution.llvmLib
|
||||||
val libGcc = distrib.libGcc
|
val libGcc = distribution.libGcc
|
||||||
|
|
||||||
// TODO: Can we extract more to the konan.properties?
|
// TODO: Can we extract more to the konan.properties?
|
||||||
return mutableListOf<String>("$linker",
|
return mutableListOf<String>("$linker",
|
||||||
@@ -97,41 +118,40 @@ internal class LinuxPlatform(distrib: Distribution,
|
|||||||
|
|
||||||
|
|
||||||
internal class LinkStage(val context: Context) {
|
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 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 optimize = config.get(KonanConfigKeys.OPTIMIZATION) ?: false
|
||||||
val emitted = config.get(KonanConfigKeys.BITCODE_FILE)!!
|
val emitted = config.get(KonanConfigKeys.BITCODE_FILE)!!
|
||||||
val nostdlib = config.get(KonanConfigKeys.NOSTDLIB) ?: false
|
val nostdlib = config.get(KonanConfigKeys.NOSTDLIB) ?: false
|
||||||
val libraries = context.config.libraries
|
val libraries = context.config.libraries
|
||||||
|
|
||||||
fun llvmLto(files: List<BitcodeFile>): ObjectFile {
|
fun llvmLto(files: List<BitcodeFile>): ObjectFile {
|
||||||
// TODO: Make it a temporary file.
|
val tmpCombined = File.createTempFile("combined", ".o")
|
||||||
val combined = "combined.o"
|
tmpCombined.deleteOnExit()
|
||||||
|
val combined = tmpCombined.absolutePath
|
||||||
|
|
||||||
val tool = distrib.llvmLto
|
val tool = distribution.llvmLto
|
||||||
val command = mutableListOf(tool, "-o", combined)
|
val command = mutableListOf(tool, "-o", combined)
|
||||||
if (optimize) {
|
if (optimize) {
|
||||||
command.addAll(platform.llvmLtoFlags)
|
command.addAll(platform.llvmLtoFlags)
|
||||||
@@ -143,12 +163,13 @@ internal class LinkStage(val context: Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun llvmLlc(file: BitcodeFile): ObjectFile {
|
fun llvmLlc(file: BitcodeFile): ObjectFile {
|
||||||
// TODO: Make it a temporary file.
|
val tmpObjectFile = File.createTempFile(File(file).name, ".o")
|
||||||
val objectFile = "$file.o"
|
tmpObjectFile.deleteOnExit()
|
||||||
|
val objectFile = tmpObjectFile.absolutePath
|
||||||
|
|
||||||
val tool = distrib.llvmLlc
|
val tool = distribution.llvmLlc
|
||||||
val command = listOf(distrib.llvmLlc, "-o", objectFile, "-filetype=obj") +
|
val command = listOf(distribution.llvmLlc, "-o", objectFile, "-filetype=obj") +
|
||||||
properties.propertyList("llvmLlcFlags.$os") + listOf(file)
|
properties.propertyList("llvmLlcFlags.$suffix") + listOf(file)
|
||||||
runTool(*command.toTypedArray())
|
runTool(*command.toTypedArray())
|
||||||
|
|
||||||
return objectFile
|
return objectFile
|
||||||
@@ -186,10 +207,10 @@ internal class LinkStage(val context: Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun linkStage() {
|
fun linkStage() {
|
||||||
context.log("# Compiler root: ${Distribution.konanHome}")
|
context.log("# Compiler root: ${distribution.konanHome}")
|
||||||
|
|
||||||
val bitcodeFiles = listOf<BitcodeFile>(emitted, Distribution.start, Distribution.runtime,
|
val bitcodeFiles = listOf<BitcodeFile>(emitted, distribution.start, distribution.runtime,
|
||||||
Distribution.launcher) + libraries
|
distribution.launcher) + libraries
|
||||||
|
|
||||||
val objectFiles = if (optimize) {
|
val objectFiles = if (optimize) {
|
||||||
listOf( llvmLto(bitcodeFiles ) )
|
listOf( llvmLto(bitcodeFiles ) )
|
||||||
|
|||||||
+37
-27
@@ -5,13 +5,20 @@ import org.jetbrains.kotlin.backend.konan.llvm.functionName
|
|||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.builtins.isFunctionType
|
import org.jetbrains.kotlin.builtins.isFunctionType
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
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.incremental.components.NoLookupLocation
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
import org.jetbrains.kotlin.resolve.OverridingUtil
|
import org.jetbrains.kotlin.resolve.OverridingUtil
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.*
|
import org.jetbrains.kotlin.resolve.descriptorUtil.*
|
||||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
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.TypeUtils
|
||||||
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
||||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
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
|
* TODO: this method is actually a part of resolve and probably duplicates another one
|
||||||
*/
|
*/
|
||||||
internal val FunctionDescriptor.implementation: FunctionDescriptor
|
internal fun FunctionDescriptor.resolveFakeOverride(): FunctionDescriptor {
|
||||||
get() {
|
if (this.kind.isReal) {
|
||||||
if (this.kind.isReal) {
|
return this
|
||||||
return this
|
} else {
|
||||||
} else {
|
val overridden = OverridingUtil.getOverriddenDeclarations(this)
|
||||||
val overridden = OverridingUtil.getOverriddenDeclarations(this)
|
val filtered = OverridingUtil.filterOutOverridden(overridden)
|
||||||
val filtered = OverridingUtil.filterOutOverridden(overridden)
|
// TODO: is it correct to take first?
|
||||||
return filtered.first { it.modality != Modality.ABSTRACT } as FunctionDescriptor
|
return filtered.first { it.modality != Modality.ABSTRACT } as FunctionDescriptor
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private val intrinsicAnnotation = FqName("konan.internal.Intrinsic")
|
private val intrinsicAnnotation = FqName("konan.internal.Intrinsic")
|
||||||
|
|
||||||
@@ -128,16 +135,18 @@ internal fun KotlinType.isUnboundCallableReference(): Boolean {
|
|||||||
|
|
||||||
internal val CallableDescriptor.allValueParameters: List<ParameterDescriptor>
|
internal val CallableDescriptor.allValueParameters: List<ParameterDescriptor>
|
||||||
get() {
|
get() {
|
||||||
val constructorReceiver = if (this is ConstructorDescriptor) {
|
val receivers = mutableListOf<ParameterDescriptor>()
|
||||||
this.constructedClass.thisAsReceiverParameter
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
val receivers = listOf(
|
if (this is ConstructorDescriptor)
|
||||||
constructorReceiver,
|
receivers.add(this.constructedClass.thisAsReceiverParameter)
|
||||||
this.dispatchReceiverParameter,
|
|
||||||
this.extensionReceiverParameter).filterNotNull()
|
val dispatchReceiverParameter = this.dispatchReceiverParameter
|
||||||
|
if (dispatchReceiverParameter != null)
|
||||||
|
receivers.add(dispatchReceiverParameter)
|
||||||
|
|
||||||
|
val extensionReceiverParameter = this.extensionReceiverParameter
|
||||||
|
if (extensionReceiverParameter != null)
|
||||||
|
receivers.add(extensionReceiverParameter)
|
||||||
|
|
||||||
return receivers + this.valueParameters
|
return receivers + this.valueParameters
|
||||||
}
|
}
|
||||||
@@ -152,7 +161,7 @@ internal val FunctionDescriptor.isFunctionInvoke: Boolean
|
|||||||
|
|
||||||
internal fun ClassDescriptor.isUnit() = this.defaultType.isUnit()
|
internal fun ClassDescriptor.isUnit() = this.defaultType.isUnit()
|
||||||
|
|
||||||
internal val ClassDescriptor.сontributedMethods: List<FunctionDescriptor>
|
internal val ClassDescriptor.contributedMethods: List<FunctionDescriptor>
|
||||||
get() {
|
get() {
|
||||||
val contributedDescriptors = unsubstitutedMemberScope.getContributedDescriptors()
|
val contributedDescriptors = unsubstitutedMemberScope.getContributedDescriptors()
|
||||||
// (includes declarations from supers)
|
// (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
|
fun ClassDescriptor.isAbstract() = this.modality == Modality.SEALED || this.modality == Modality.ABSTRACT
|
||||||
|
|| this.kind == ClassKind.ENUM_CLASS
|
||||||
|
|
||||||
// TODO: optimize
|
// TODO: optimize
|
||||||
val ClassDescriptor.vtableEntries: List<FunctionDescriptor>
|
val ClassDescriptor.vtableEntries: List<FunctionDescriptor>
|
||||||
@@ -183,18 +193,15 @@ val ClassDescriptor.vtableEntries: List<FunctionDescriptor>
|
|||||||
this.getSuperClassOrAny().vtableEntries
|
this.getSuperClassOrAny().vtableEntries
|
||||||
}
|
}
|
||||||
|
|
||||||
val methods = this.сontributedMethods
|
val methods = this.contributedMethods
|
||||||
|
|
||||||
val inheritedVtableSlots = superVtableEntries.map { superMethod ->
|
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 }
|
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 {
|
fun ClassDescriptor.vtableIndex(function: FunctionDescriptor): Int {
|
||||||
this.vtableEntries.forEachIndexed { index, functionDescriptor ->
|
this.vtableEntries.forEachIndexed { index, functionDescriptor ->
|
||||||
if (functionDescriptor == function.original) return index
|
if (functionDescriptor == function.original) return index
|
||||||
@@ -204,8 +211,11 @@ fun ClassDescriptor.vtableIndex(function: FunctionDescriptor): Int {
|
|||||||
|
|
||||||
val ClassDescriptor.methodTableEntries: List<FunctionDescriptor>
|
val ClassDescriptor.methodTableEntries: List<FunctionDescriptor>
|
||||||
get() {
|
get() {
|
||||||
assert (!this.isAbstract())
|
assert(!this.isAbstract())
|
||||||
|
return this.contributedMethods.filter {
|
||||||
return this.сontributedMethods.filter { it.isOverridableOrOverrides }
|
// 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
|
// TODO: probably method table should contain all accessible methods to improve binary compatibility
|
||||||
}
|
}
|
||||||
+3
-1
@@ -37,5 +37,7 @@ val PropertyDescriptor.backingField: PropertyDescriptor?
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun DeclarationDescriptor.deepPrint() {
|
fun DeclarationDescriptor.deepPrint() {
|
||||||
this!!.accept(DeepPrintVisitor(PrintVisitor()), 0)
|
this.accept(DeepPrintVisitor(PrintVisitor()), 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal val String.synthesizedName get() = Name.identifier("\$" + this)
|
||||||
|
|||||||
+7
-1
@@ -1,6 +1,8 @@
|
|||||||
package org.jetbrains.kotlin.backend.konan.ir
|
package org.jetbrains.kotlin.backend.konan.ir
|
||||||
|
|
||||||
import org.jetbrains.kotlin.backend.konan.Context
|
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.ir.declarations.IrModuleFragment
|
||||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
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) {
|
internal class Ir(val context: Context, val irModule: IrModuleFragment) {
|
||||||
val propertiesWithBackingFields = mutableSetOf<PropertyDescriptor>()
|
val propertiesWithBackingFields = mutableSetOf<PropertyDescriptor>()
|
||||||
|
|
||||||
val moduleIndex = ModuleIndex(irModule)
|
val defaultParameterDescriptions = mutableMapOf<FunctionDescriptor, DefaultParameterDescription>()
|
||||||
|
|
||||||
|
val originalModuleIndex = ModuleIndex(irModule)
|
||||||
|
|
||||||
|
lateinit var moduleIndexForCodegen: ModuleIndex
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-8
@@ -1,5 +1,6 @@
|
|||||||
package org.jetbrains.kotlin.backend.konan.ir
|
package org.jetbrains.kotlin.backend.konan.ir
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.ir.IrElement
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
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.IrElementVisitorVoid
|
||||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||||
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
||||||
import org.jetbrains.kotlin.name.ClassId
|
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
|
|
||||||
|
|
||||||
class ModuleIndex(val module: IrModuleFragment) {
|
class ModuleIndex(val module: IrModuleFragment) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains all classes declared in [module]
|
* Contains all classes declared in [module]
|
||||||
*/
|
*/
|
||||||
val classes: Map<ClassId, IrClass>
|
val classes: Map<ClassDescriptor, IrClass>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains all functions declared in [module]
|
* Contains all functions declared in [module]
|
||||||
@@ -24,7 +23,7 @@ class ModuleIndex(val module: IrModuleFragment) {
|
|||||||
val functions = mutableMapOf<FunctionDescriptor, IrFunction>()
|
val functions = mutableMapOf<FunctionDescriptor, IrFunction>()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val map = mutableMapOf<ClassId, IrClass>()
|
val map = mutableMapOf<ClassDescriptor, IrClass>()
|
||||||
|
|
||||||
module.acceptVoid(object : IrElementVisitorVoid {
|
module.acceptVoid(object : IrElementVisitorVoid {
|
||||||
override fun visitElement(element: IrElement) {
|
override fun visitElement(element: IrElement) {
|
||||||
@@ -34,10 +33,7 @@ class ModuleIndex(val module: IrModuleFragment) {
|
|||||||
override fun visitClass(declaration: IrClass) {
|
override fun visitClass(declaration: IrClass) {
|
||||||
super.visitClass(declaration)
|
super.visitClass(declaration)
|
||||||
|
|
||||||
val classId = declaration.descriptor.classId
|
map[declaration.descriptor] = declaration
|
||||||
if (classId != null) {
|
|
||||||
map[classId] = declaration
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitFunction(declaration: IrFunction) {
|
override fun visitFunction(declaration: IrFunction) {
|
||||||
|
|||||||
+160
@@ -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"
|
||||||
|
}
|
||||||
+82
-21
@@ -17,14 +17,27 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
|
|||||||
// TODO: remove, to make CodeGenerator descriptor-agnostic.
|
// TODO: remove, to make CodeGenerator descriptor-agnostic.
|
||||||
var constructedClass: ClassDescriptor? = null
|
var constructedClass: ClassDescriptor? = null
|
||||||
val vars = VariableManager(this)
|
val vars = VariableManager(this)
|
||||||
var returnSlot: LLVMValueRef? = null
|
|
||||||
var slotsPhi: LLVMValueRef? = null
|
|
||||||
var slotCount = 0
|
|
||||||
var functionDescriptor: FunctionDescriptor? = null
|
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) {
|
fun prologue(descriptor: FunctionDescriptor) {
|
||||||
prologue(llvmFunction(descriptor),
|
val llvmFunction = llvmFunction(descriptor)
|
||||||
|
|
||||||
|
prologue(llvmFunction,
|
||||||
LLVMGetReturnType(getLlvmFunctionType(descriptor))!!)
|
LLVMGetReturnType(getLlvmFunctionType(descriptor))!!)
|
||||||
|
|
||||||
|
if (!descriptor.isExported()) {
|
||||||
|
LLVMSetLinkage(llvmFunction, LLVMLinkage.LLVMInternalLinkage)
|
||||||
|
// (Cannot do this before the function body is created).
|
||||||
|
}
|
||||||
|
|
||||||
if (descriptor is ConstructorDescriptor) {
|
if (descriptor is ConstructorDescriptor) {
|
||||||
constructedClass = descriptor.constructedClass
|
constructedClass = descriptor.constructedClass
|
||||||
}
|
}
|
||||||
@@ -47,16 +60,21 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
|
|||||||
cleanupLandingpad = LLVMAppendBasicBlock(function, "cleanup_landingpad")!!
|
cleanupLandingpad = LLVMAppendBasicBlock(function, "cleanup_landingpad")!!
|
||||||
positionAtEnd(entryBb!!)
|
positionAtEnd(entryBb!!)
|
||||||
slotsPhi = phi(kObjHeaderPtrPtr)
|
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() {
|
fun epilogue() {
|
||||||
appendingTo(prologueBb!!) {
|
appendingTo(prologueBb!!) {
|
||||||
val slots = if (slotCount > 0)
|
val slots = if (needSlots)
|
||||||
LLVMBuildArrayAlloca(builder, kObjHeaderPtr, Int32(slotCount).llvm, "")!!
|
LLVMBuildArrayAlloca(builder, kObjHeaderPtr, Int32(slotCount).llvm, "")!!
|
||||||
else
|
else
|
||||||
kNullObjHeaderPtrPtr
|
kNullObjHeaderPtrPtr
|
||||||
if (slotCount > 0) {
|
if (needSlots) {
|
||||||
// Zero-init slots.
|
// Zero-init slots.
|
||||||
val slotsMem = bitcast(kInt8Ptr, slots)
|
val slotsMem = bitcast(kInt8Ptr, slots)
|
||||||
val pointerSize = LLVMABISizeOfType(llvmTargetData, kObjHeaderPtr).toInt()
|
val pointerSize = LLVMABISizeOfType(llvmTargetData, kObjHeaderPtr).toInt()
|
||||||
@@ -81,7 +99,7 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
|
|||||||
val returnPhi = phi(returnType!!)
|
val returnPhi = phi(returnType!!)
|
||||||
addPhiIncoming(returnPhi, *returns.toList().toTypedArray())
|
addPhiIncoming(returnPhi, *returns.toList().toTypedArray())
|
||||||
if (returnSlot != null) {
|
if (returnSlot != null) {
|
||||||
updateLocalRef(returnPhi, returnSlot!!)
|
updateReturnRef(returnPhi, returnSlot!!)
|
||||||
}
|
}
|
||||||
releaseVars()
|
releaseVars()
|
||||||
LLVMBuildRet(builder, returnPhi)
|
LLVMBuildRet(builder, returnPhi)
|
||||||
@@ -104,9 +122,14 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
|
|||||||
slotsPhi = null
|
slotsPhi = null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun releaseVars() {
|
private val needSlots: Boolean
|
||||||
if (slotCount > 0) {
|
get() {
|
||||||
call(context.llvm.releaseLocalRefsFunction,
|
return slotCount > 1 || localAllocs > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun releaseVars() {
|
||||||
|
if (needSlots) {
|
||||||
|
call(context.llvm.leaveFrameFunction,
|
||||||
listOf(slotsPhi!!, Int32(slotCount).llvm))
|
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 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 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 */
|
/* integers comparisons */
|
||||||
fun icmpEq(arg0: LLVMValueRef, arg1: LLVMValueRef, name: String = ""): LLVMValueRef = LLVMBuildICmp(builder, LLVMIntPredicate.LLVMIntEQ, arg0, arg1, name)!!
|
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)!!
|
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 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 {
|
fun alloca(type: LLVMTypeRef?, name: String = ""): LLVMValueRef {
|
||||||
if (isObjectType(type!!)) {
|
if (isObjectType(type!!)) {
|
||||||
@@ -150,6 +176,16 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
|
|||||||
return LLVMBuildAlloca(builder, type, name)!!
|
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 {
|
fun load(value: LLVMValueRef, name: String = ""): LLVMValueRef {
|
||||||
val result = LLVMBuildLoad(builder, value, name)!!
|
val result = LLVMBuildLoad(builder, value, name)!!
|
||||||
// Use loadSlot() API for that.
|
// 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.
|
// Only use ignoreOld, when sure that memory is freshly inited and have no value.
|
||||||
fun updateLocalRef(value: LLVMValueRef, address: LLVMValueRef, ignoreOld: Boolean = false) {
|
fun updateLocalRef(value: LLVMValueRef, address: LLVMValueRef, ignoreOld: Boolean = false) {
|
||||||
call(if (ignoreOld) context.llvm.setLocalRefFunction else context.llvm.updateLocalRefFunction,
|
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 = "") =
|
fun callAtFunctionScope(llvmFunction: LLVMValueRef, args: List<LLVMValueRef>,
|
||||||
call(llvmFunction, args, this::cleanupLandingpad, name)
|
lifetime: Lifetime) =
|
||||||
|
call(llvmFunction, args, lifetime, this::cleanupLandingpad)
|
||||||
|
|
||||||
fun call(llvmFunction: LLVMValueRef, args: List<LLVMValueRef>,
|
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 {
|
memScoped {
|
||||||
val rargs = allocArrayOf(args)[0].ptr
|
val rargs = allocArrayOf(args)[0].ptr
|
||||||
|
|
||||||
if (LLVMIsAFunction(llvmFunction) != null /* the function declaration */ &&
|
if (LLVMIsAFunction(llvmFunction) != null /* the function declaration */ &&
|
||||||
LLVMAttribute.LLVMNoUnwindAttribute in LLVMGetFunctionAttrSet(llvmFunction)) {
|
LLVMAttribute.LLVMNoUnwindAttribute in LLVMGetFunctionAttrSet(llvmFunction)) {
|
||||||
|
|
||||||
return LLVMBuildCall(builder, llvmFunction, rargs, args.size, name)!!
|
return LLVMBuildCall(builder, llvmFunction, rargs, args.size, "")!!
|
||||||
} else {
|
} else {
|
||||||
val landingpad = lazyLandingpad()
|
val landingpad = lazyLandingpad()
|
||||||
|
|
||||||
@@ -232,7 +297,7 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val success = basicBlock()
|
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)
|
positionAtEnd(success)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@@ -264,7 +329,6 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
|
|||||||
//-------------------------------------------------------------------------//
|
//-------------------------------------------------------------------------//
|
||||||
|
|
||||||
/* to class descriptor */
|
/* to class descriptor */
|
||||||
fun classType(descriptor: ClassDescriptor): LLVMTypeRef = LLVMGetTypeByName(context.llvmModule, descriptor.symbolName)!!
|
|
||||||
fun typeInfoValue(descriptor: ClassDescriptor): LLVMValueRef = descriptor.llvmTypeInfoPtr
|
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 countParams(fn: FunctionDescriptor) = LLVMCountParams(fn.llvmFunction)
|
||||||
|
|
||||||
fun indexInClass(p:PropertyDescriptor):Int = (p.containingDeclaration as ClassDescriptor).fields.indexOf(p)
|
|
||||||
|
|
||||||
|
|
||||||
fun basicBlock(name: String = "label_"): LLVMBasicBlockRef {
|
fun basicBlock(name: String = "label_"): LLVMBasicBlockRef {
|
||||||
val currentBlock = this.currentBlock
|
val currentBlock = this.currentBlock
|
||||||
val result = LLVMInsertBasicBlock(currentBlock, name)!!
|
val result = LLVMInsertBasicBlock(currentBlock, name)!!
|
||||||
|
|||||||
+65
-81
@@ -3,32 +3,61 @@ package org.jetbrains.kotlin.backend.konan.llvm
|
|||||||
import kotlinx.cinterop.*
|
import kotlinx.cinterop.*
|
||||||
import llvm.*
|
import llvm.*
|
||||||
import org.jetbrains.kotlin.backend.konan.Context
|
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.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.ClassDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
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.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.FqName
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
|
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
|
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import org.jetbrains.kotlin.types.TypeUtils
|
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.
|
* Provides utility methods to the implementer.
|
||||||
*/
|
*/
|
||||||
internal interface ContextUtils {
|
internal interface ContextUtils : RuntimeAware {
|
||||||
val context: Context
|
val context: Context
|
||||||
|
|
||||||
val runtime: Runtime
|
override val runtime: Runtime
|
||||||
get() = context.llvm.runtime
|
get() = context.llvm.runtime
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -42,53 +71,7 @@ internal interface ContextUtils {
|
|||||||
val staticData: StaticData
|
val staticData: StaticData
|
||||||
get() = context.llvm.staticData
|
get() = context.llvm.staticData
|
||||||
|
|
||||||
/**
|
fun isExternal(descriptor: DeclarationDescriptor) = descriptor.module != context.ir.irModule.descriptor
|
||||||
* 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 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* LLVM function generated from the Kotlin function.
|
* LLVM function generated from the Kotlin function.
|
||||||
@@ -97,13 +80,15 @@ internal interface ContextUtils {
|
|||||||
val FunctionDescriptor.llvmFunction: LLVMValueRef
|
val FunctionDescriptor.llvmFunction: LLVMValueRef
|
||||||
get() {
|
get() {
|
||||||
assert (this.kind.isReal)
|
assert (this.kind.isReal)
|
||||||
val globalName = this.symbolName
|
if (this is TypeAliasConstructorDescriptor) {
|
||||||
val module = context.llvmModule
|
return this.underlyingConstructorDescriptor.llvmFunction
|
||||||
|
}
|
||||||
|
|
||||||
val functionType = getLlvmFunctionType(this)
|
return if (isExternal(this)) {
|
||||||
|
context.llvm.externalFunction(this.symbolName, getLlvmFunctionType(this))
|
||||||
return LLVMGetNamedFunction(module, globalName) ?:
|
} else {
|
||||||
LLVMAddFunction(module, globalName, functionType)!!
|
context.llvmDeclarations.forFunction(this).llvmFunction
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -115,17 +100,14 @@ internal interface ContextUtils {
|
|||||||
return constValue(result)
|
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
|
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.
|
* 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 {
|
internal fun externalFunction(name: String, type: LLVMTypeRef): LLVMValueRef {
|
||||||
val found = LLVMGetNamedFunction(context.llvmModule, name)
|
val found = LLVMGetNamedFunction(llvmModule, name)
|
||||||
if (found != null) {
|
if (found != null) {
|
||||||
assert (getFunctionType(found) == type)
|
assert (getFunctionType(found) == type)
|
||||||
|
assert (LLVMGetLinkage(found) == LLVMLinkage.LLVMExternalLinkage)
|
||||||
return found
|
return found
|
||||||
} else {
|
} 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 staticData = StaticData(context)
|
||||||
|
|
||||||
val runtimeFile = context.config.configuration.get(KonanConfigKeys.RUNTIME_FILE)!!
|
val runtimeFile = context.config.distribution.runtime
|
||||||
val runtime = Runtime(runtimeFile) // TODO: dispose
|
val runtime = Runtime(runtimeFile) // TODO: dispose
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@@ -254,13 +237,14 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
|
|||||||
var globalInitIndex:Int = 0
|
var globalInitIndex:Int = 0
|
||||||
|
|
||||||
val allocInstanceFunction = importRtFunction("AllocInstance")
|
val allocInstanceFunction = importRtFunction("AllocInstance")
|
||||||
val initInstanceFunction = importRtFunction("InitInstance")
|
|
||||||
val allocArrayFunction = importRtFunction("AllocArrayInstance")
|
val allocArrayFunction = importRtFunction("AllocArrayInstance")
|
||||||
|
val initInstanceFunction = importRtFunction("InitInstance")
|
||||||
|
val updateReturnRefFunction = importRtFunction("UpdateReturnRef")
|
||||||
val setLocalRefFunction = importRtFunction("SetLocalRef")
|
val setLocalRefFunction = importRtFunction("SetLocalRef")
|
||||||
val setGlobalRefFunction = importRtFunction("SetGlobalRef")
|
val setGlobalRefFunction = importRtFunction("SetGlobalRef")
|
||||||
val updateLocalRefFunction = importRtFunction("UpdateLocalRef")
|
val updateLocalRefFunction = importRtFunction("UpdateLocalRef")
|
||||||
val updateGlobalRefFunction = importRtFunction("UpdateGlobalRef")
|
val updateGlobalRefFunction = importRtFunction("UpdateGlobalRef")
|
||||||
val releaseLocalRefsFunction = importRtFunction("ReleaseLocalRefs")
|
val leaveFrameFunction = importRtFunction("LeaveFrame")
|
||||||
val setArrayFunction = importRtFunction("Kotlin_Array_set")
|
val setArrayFunction = importRtFunction("Kotlin_Array_set")
|
||||||
val copyImplArrayFunction = importRtFunction("Kotlin_Array_copyImpl")
|
val copyImplArrayFunction = importRtFunction("Kotlin_Array_copyImpl")
|
||||||
val lookupFieldOffset = importRtFunction("LookupFieldOffset")
|
val lookupFieldOffset = importRtFunction("LookupFieldOffset")
|
||||||
|
|||||||
+2
-2
@@ -6,7 +6,7 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
|||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
||||||
|
|
||||||
internal fun ContextUtils.getLLVMType(type: KotlinType): LLVMTypeRef {
|
internal fun RuntimeAware.getLLVMType(type: KotlinType): LLVMTypeRef {
|
||||||
return when {
|
return when {
|
||||||
// Nullable types must be represented as objects for boxing.
|
// Nullable types must be represented as objects for boxing.
|
||||||
type.isMarkedNullable -> this.kObjHeaderPtr
|
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 {
|
return when {
|
||||||
type.isUnit() -> LLVMVoidType()!!
|
type.isUnit() -> LLVMVoidType()!!
|
||||||
// TODO: stdlib have methods taking Nothing, such as kotlin.collections.EmptySet.contains().
|
// TODO: stdlib have methods taking Nothing, such as kotlin.collections.EmptySet.contains().
|
||||||
|
|||||||
+38
@@ -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))
|
||||||
|
}
|
||||||
+133
-110
@@ -20,7 +20,6 @@ import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
|||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
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.fqNameSafe
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
@@ -33,9 +32,16 @@ import org.jetbrains.kotlin.utils.addToStdlib.singletonList
|
|||||||
internal fun emitLLVM(context: Context) {
|
internal fun emitLLVM(context: Context) {
|
||||||
|
|
||||||
val irModule = context.irModule!!
|
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
|
val llvmModule = LLVMModuleCreateWithName("out")!! // TODO: dispose
|
||||||
context.llvmModule = llvmModule
|
context.llvmModule = llvmModule
|
||||||
|
|
||||||
|
context.llvmDeclarations = createLlvmDeclarations(context)
|
||||||
|
|
||||||
val phaser = PhaseManager(context)
|
val phaser = PhaseManager(context)
|
||||||
|
|
||||||
phaser.phase(KonanPhase.RTTI) {
|
phaser.phase(KonanPhase.RTTI) {
|
||||||
@@ -78,11 +84,6 @@ internal class RTTIGeneratorVisitor(context: Context) : IrElementVisitorVoid {
|
|||||||
override fun visitClass(declaration: IrClass) {
|
override fun visitClass(declaration: IrClass) {
|
||||||
super.visitClass(declaration)
|
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) {
|
if (declaration.descriptor.isIntrinsic) {
|
||||||
// do not generate any code for intrinsic classes as they require special handling
|
// do not generate any code for intrinsic classes as they require special handling
|
||||||
return
|
return
|
||||||
@@ -118,7 +119,7 @@ internal class MetadatorVisitor(val context: Context) : IrElementVisitorVoid {
|
|||||||
/**
|
/**
|
||||||
* Defines how to generate context-dependent operations.
|
* Defines how to generate context-dependent operations.
|
||||||
*/
|
*/
|
||||||
interface CodeContext {
|
internal interface CodeContext {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates `return` [value] operation.
|
* Generates `return` [value] operation.
|
||||||
@@ -131,7 +132,7 @@ interface CodeContext {
|
|||||||
|
|
||||||
fun genContinue(destination: IrContinue)
|
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)
|
fun genThrow(exception: LLVMValueRef)
|
||||||
|
|
||||||
@@ -167,6 +168,7 @@ interface CodeContext {
|
|||||||
internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid {
|
internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid {
|
||||||
|
|
||||||
val codegen = CodeGenerator(context)
|
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 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()
|
override fun genThrow(exception: LLVMValueRef) = unsupported()
|
||||||
|
|
||||||
@@ -235,6 +237,8 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
|||||||
override fun visitModuleFragment(module: IrModuleFragment) {
|
override fun visitModuleFragment(module: IrModuleFragment) {
|
||||||
context.log("visitModule : ${ir2string(module)}")
|
context.log("visitModule : ${ir2string(module)}")
|
||||||
|
|
||||||
|
computeLifetimes(module, resultLifetimes)
|
||||||
|
|
||||||
module.acceptChildrenVoid(this)
|
module.acceptChildrenVoid(this)
|
||||||
appendLlvmUsed(context.llvm.usedFunctions)
|
appendLlvmUsed(context.llvm.usedFunctions)
|
||||||
appendStaticInitializers(context.llvm.staticInitializers)
|
appendStaticInitializers(context.llvm.staticInitializers)
|
||||||
@@ -246,7 +250,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
|||||||
val kNodeInitType = LLVMGetTypeByName(context.llvmModule, "struct.InitNode")!!
|
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
|
val initFunction = LLVMAddFunction(context.llvmModule, initName, kVoidFuncType)!! // create LLVM function
|
||||||
codegen.prologue(initFunction, voidType)
|
codegen.prologue(initFunction, voidType)
|
||||||
using(FunctionScope(initFunction)) {
|
using(FunctionScope(initFunction)) {
|
||||||
@@ -254,33 +258,33 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
|||||||
val irField = it as IrField
|
val irField = it as IrField
|
||||||
val descriptor = irField.descriptor
|
val descriptor = irField.descriptor
|
||||||
val initialization = evaluateExpression(irField.initializer!!.expression)
|
val initialization = evaluateExpression(irField.initializer!!.expression)
|
||||||
val globalPtr = LLVMGetNamedGlobal(context.llvmModule, descriptor.symbolName)
|
val globalPtr = context.llvmDeclarations.forStaticField(descriptor).storage
|
||||||
codegen.storeAnyGlobal(initialization, globalPtr!!)
|
codegen.storeAnyGlobal(initialization, globalPtr)
|
||||||
}
|
}
|
||||||
codegen.ret(null)
|
codegen.ret(null)
|
||||||
}
|
}
|
||||||
codegen.epilogue()
|
codegen.epilogue()
|
||||||
|
|
||||||
|
return initFunction
|
||||||
}
|
}
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
//-------------------------------------------------------------------------//
|
||||||
// Creates static struct InitNode $nodeName = {$initName, NULL};
|
// Creates static struct InitNode $nodeName = {$initName, NULL};
|
||||||
|
|
||||||
fun createInitNode(initName: String, nodeName: String) {
|
fun createInitNode(initFunction: LLVMValueRef, nodeName: String): LLVMValueRef {
|
||||||
memScoped {
|
memScoped {
|
||||||
val initFunction = LLVMGetNamedFunction(context.llvmModule, initName)!! // Get initialization function.
|
|
||||||
val nextInitNode = LLVMConstNull(pointerType(kNodeInitType)) // Set InitNode.next = NULL.
|
val nextInitNode = LLVMConstNull(pointerType(kNodeInitType)) // Set InitNode.next = NULL.
|
||||||
val argList = allocArrayOf(initFunction, nextInitNode)[0].ptr // Allocate array of args.
|
val argList = allocArrayOf(initFunction, nextInitNode)[0].ptr // Allocate array of args.
|
||||||
val initNode = LLVMConstNamedStruct(kNodeInitType, argList, 2)!! // Create static object of class InitNode.
|
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.
|
val ctorFunction = LLVMAddFunction(context.llvmModule, ctorName, kVoidFuncType)!! // Create constructor function.
|
||||||
codegen.prologue(ctorFunction, voidType)
|
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.call(context.llvm.appendToInitalizersTail, initNodePtr.singletonList()) // Add node to the tail of initializers list.
|
||||||
codegen.ret(null)
|
codegen.ret(null)
|
||||||
codegen.epilogue()
|
codegen.epilogue()
|
||||||
@@ -305,9 +309,9 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
|||||||
val nodeName = "${fileName}_node_${context.llvm.globalInitIndex}"
|
val nodeName = "${fileName}_node_${context.llvm.globalInitIndex}"
|
||||||
val ctorName = "${fileName}_ctor_${context.llvm.globalInitIndex++}"
|
val ctorName = "${fileName}_ctor_${context.llvm.globalInitIndex++}"
|
||||||
|
|
||||||
createInitBody(initName)
|
val initFunction = createInitBody(initName)
|
||||||
createInitNode(initName, nodeName)
|
val initNode = createInitNode(initFunction, nodeName)
|
||||||
createInitCtor(ctorName, nodeName)
|
createInitCtor(ctorName, initNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
//-------------------------------------------------------------------------//
|
||||||
@@ -380,15 +384,13 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
|||||||
|
|
||||||
if (constructorDescriptor.isPrimary) {
|
if (constructorDescriptor.isPrimary) {
|
||||||
if (DescriptorUtils.isObject(classDescriptor)) {
|
if (DescriptorUtils.isObject(classDescriptor)) {
|
||||||
if (classDescriptor.isUnit()) {
|
if (!classDescriptor.isUnit()) {
|
||||||
context.llvm.staticData.createUnitInstance(classDescriptor)
|
|
||||||
} else {
|
|
||||||
val objectPtr = objectPtrByName(classDescriptor)
|
val objectPtr = objectPtrByName(classDescriptor)
|
||||||
|
|
||||||
LLVMSetInitializer(objectPtr, codegen.kNullObjHeaderPtr)
|
LLVMSetInitializer(objectPtr, codegen.kNullObjHeaderPtr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val irOfCurrentClass = context.ir.moduleIndex.classes[classDescriptor.classId]
|
val irOfCurrentClass = context.ir.moduleIndexForCodegen.classes[classDescriptor]
|
||||||
irOfCurrentClass!!.acceptChildrenVoid(object : IrElementVisitorVoid {
|
irOfCurrentClass!!.acceptChildrenVoid(object : IrElementVisitorVoid {
|
||||||
override fun visitElement(element: IrElement) {
|
override fun visitElement(element: IrElement) {
|
||||||
element.acceptChildrenVoid(this)
|
element.acceptChildrenVoid(this)
|
||||||
@@ -527,14 +529,14 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun genCall(function: LLVMValueRef, args: List<LLVMValueRef>) =
|
override fun genCall(function: LLVMValueRef, args: List<LLVMValueRef>, resultLifetime: Lifetime) =
|
||||||
codegen.callAtFunctionScope(function, args)
|
codegen.callAtFunctionScope(function, args, resultLifetime)
|
||||||
|
|
||||||
override fun genThrow(exception: LLVMValueRef) {
|
override fun genThrow(exception: LLVMValueRef) {
|
||||||
val objHeaderPtr = codegen.bitcast(codegen.kObjHeaderPtr, exception)
|
val objHeaderPtr = codegen.bitcast(codegen.kObjHeaderPtr, exception)
|
||||||
val args = listOf(objHeaderPtr)
|
val args = listOf(objHeaderPtr)
|
||||||
|
|
||||||
this.genCall(context.llvm.throwExceptionFunction, args)
|
this.genCall(context.llvm.throwExceptionFunction, args, Lifetime.IRRELEVANT)
|
||||||
codegen.unreachable()
|
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) {
|
override fun visitProperty(declaration: IrProperty) {
|
||||||
declaration.acceptChildrenVoid(this)
|
declaration.acceptChildrenVoid(this)
|
||||||
}
|
}
|
||||||
@@ -597,13 +605,18 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
|||||||
context.log("visitField : ${ir2string(expression)}")
|
context.log("visitField : ${ir2string(expression)}")
|
||||||
val descriptor = expression.descriptor
|
val descriptor = expression.descriptor
|
||||||
if (descriptor.containingDeclaration is PackageFragmentDescriptor) {
|
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<*>) {
|
if (expression.initializer!!.expression is IrConst<*>) {
|
||||||
LLVMSetInitializer(globalProperty, evaluateExpression(expression.initializer!!.expression))
|
LLVMSetInitializer(globalProperty, evaluateExpression(expression.initializer!!.expression))
|
||||||
} else {
|
} else {
|
||||||
LLVMSetInitializer(globalProperty, codegen.kNullObjHeaderPtr)
|
LLVMSetInitializer(globalProperty, LLVMConstNull(type))
|
||||||
context.llvm.fileInitializers.add(expression)
|
context.llvm.fileInitializers.add(expression)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LLVMSetLinkage(globalProperty, LLVMLinkage.LLVMInternalLinkage)
|
||||||
|
// (Cannot do this before the global is initialized).
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -679,11 +692,10 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
|||||||
|
|
||||||
codegen.positionAtEnd(bbInit)
|
codegen.positionAtEnd(bbInit)
|
||||||
val typeInfo = codegen.typeInfoValue(value.descriptor)
|
val typeInfo = codegen.typeInfoValue(value.descriptor)
|
||||||
val allocHint = Int32(1).llvm
|
|
||||||
val initFunction = value.descriptor.constructors.first { it.valueParameters.size == 0 }
|
val initFunction = value.descriptor.constructors.first { it.valueParameters.size == 0 }
|
||||||
val ctor = codegen.llvmFunction(initFunction)
|
val ctor = codegen.llvmFunction(initFunction)
|
||||||
val args = listOf(objectPtr, typeInfo, allocHint, ctor)
|
val args = listOf(objectPtr, typeInfo, ctor)
|
||||||
val newValue = call(context.llvm.initInstanceFunction, args)
|
val newValue = call(context.llvm.initInstanceFunction, args, Lifetime.GLOBAL)
|
||||||
val bbInitResult = codegen.currentBlock
|
val bbInitResult = codegen.currentBlock
|
||||||
codegen.br(bbExit)
|
codegen.br(bbExit)
|
||||||
|
|
||||||
@@ -790,9 +802,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
|||||||
codegen.plus(sum, size!!)
|
codegen.plus(sum, size!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
val typeInfo = codegen.typeInfoValue(value.type)!!
|
val array = codegen.allocArray(codegen.typeInfoValue(value.type)!!, finalLength, Lifetime.GLOBAL)
|
||||||
val arrayCreationArgs = listOf(typeInfo, kImmInt32One, finalLength)
|
|
||||||
val array = call(context.llvm.allocArrayFunction, arrayCreationArgs)
|
|
||||||
elements.fold(kImmZero) { sum, (exp, size, isArray) ->
|
elements.fold(kImmZero) { sum, (exp, size, isArray) ->
|
||||||
if (!isArray) {
|
if (!isArray) {
|
||||||
call(context.llvm.setArrayFunction, listOf(array, sum, exp))
|
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 kStringLength = KonanPlatform.builtIns.string.getter2Descriptor(kNameLength)
|
||||||
val kStringBuilderToString = kStringBuilder.signature2Descriptor(kNameToString)
|
val kStringBuilderToString = kStringBuilder.signature2Descriptor(kNameToString)
|
||||||
|
|
||||||
|
//TODO: make it lowering pass.
|
||||||
private fun evaluateStringConcatenation(value: IrStringConcatenation): LLVMValueRef {
|
private fun evaluateStringConcatenation(value: IrStringConcatenation): LLVMValueRef {
|
||||||
data class Element(val string: LLVMValueRef, val llvmLenght: LLVMValueRef?, val length: Int)
|
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)
|
return@map Element(codegen.staticData.kotlinStringLiteral(it as IrConst<String>).llvm, null, string.length)
|
||||||
} else {
|
} else {
|
||||||
val toStringDescriptor = getToString(it.type)
|
val toStringDescriptor = getToString(it.type)
|
||||||
val string = if (KotlinBuiltIns.isString(it.type)) evaluationResult
|
val string =
|
||||||
else evaluateSimpleFunctionCall(toStringDescriptor, listOf(evaluationResult))
|
if (KotlinBuiltIns.isString(it.type)) evaluationResult
|
||||||
|
else evaluateSimpleFunctionCall(
|
||||||
|
toStringDescriptor, listOf(evaluationResult), Lifetime.LOCAL)
|
||||||
val length = call(codegen.llvmFunction(kStringLength!!), listOf(string))
|
val length = call(codegen.llvmFunction(kStringLength!!), listOf(string))
|
||||||
return@map Element(string, length, -1)
|
return@map Element(string, length, -1)
|
||||||
}
|
}
|
||||||
@@ -845,8 +858,8 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
|||||||
|
|
||||||
val constructor = kStringBuilder!!.constructors
|
val constructor = kStringBuilder!!.constructors
|
||||||
.firstOrNull { it -> it.valueParameters.size == 1 && KotlinBuiltIns.isInt(it.valueParameters[0].type) }!!
|
.firstOrNull { it -> it.valueParameters.size == 1 && KotlinBuiltIns.isInt(it.valueParameters[0].type) }!!
|
||||||
val stringBuilderObj = call(context.llvm.allocInstanceFunction,
|
val stringBuilderObj = codegen.allocInstance(codegen.typeInfoValue(kStringBuilder), Lifetime.LOCAL)
|
||||||
listOf(codegen.typeInfoValue(kStringBuilder.defaultType)!!, kImmOne))
|
|
||||||
call(codegen.llvmFunction(constructor), listOf(stringBuilderObj, totalLength))
|
call(codegen.llvmFunction(constructor), listOf(stringBuilderObj, totalLength))
|
||||||
|
|
||||||
stringsWithLengths.fold(stringBuilderObj) { sum, (string, _, _) ->
|
stringsWithLengths.fold(stringBuilderObj) { sum, (string, _, _) ->
|
||||||
@@ -854,7 +867,8 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
|||||||
return@fold sum
|
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.
|
// The call inside [CatchingScope] must be configured to dispatch exception to the scope's handler.
|
||||||
override fun genCall(function: LLVMValueRef, args: List<LLVMValueRef>): LLVMValueRef {
|
override fun genCall(function: LLVMValueRef, args: List<LLVMValueRef>, lifetime: Lifetime): LLVMValueRef {
|
||||||
val res = codegen.call(function, args, this::landingpad)
|
val res = codegen.call(function, args, lifetime, this::landingpad)
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1144,9 +1158,13 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
|||||||
if (!isNothing) // If "when" has "exit".
|
if (!isNothing) // If "when" has "exit".
|
||||||
bbExit = codegen.basicBlock() // Create basic block to process "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.appendingTo(bbExit!!) {
|
||||||
codegen.phi(codegen.getLLVMType(expression.type))
|
codegen.phi(llvmType)
|
||||||
}
|
}
|
||||||
|
|
||||||
expression.branches.forEach { // Iterate through "when" branches (clauses).
|
expression.branches.forEach { // Iterate through "when" branches (clauses).
|
||||||
@@ -1160,6 +1178,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
|||||||
// FIXME: remove the hacks.
|
// FIXME: remove the hacks.
|
||||||
isUnit -> codegen.theUnitInstanceRef.llvm
|
isUnit -> codegen.theUnitInstanceRef.llvm
|
||||||
isNothing -> codegen.kNothingFakeValue
|
isNothing -> codegen.kNothingFakeValue
|
||||||
|
hasNoValue -> LLVMGetUndef(llvmType)!!
|
||||||
else -> resultPhi!!
|
else -> resultPhi!!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1279,7 +1298,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
|||||||
val srcArg = evaluateExpression(value.argument) // Evaluate src expression.
|
val srcArg = evaluateExpression(value.argument) // Evaluate src expression.
|
||||||
val srcObjInfoPtr = codegen.bitcast(codegen.kObjHeaderPtr, srcArg) // Cast src to ObjInfoPtr.
|
val srcObjInfoPtr = codegen.bitcast(codegen.kObjHeaderPtr, srcArg) // Cast src to ObjInfoPtr.
|
||||||
val args = listOf(srcObjInfoPtr, dstTypeInfo) // Create arg list.
|
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
|
return srcArg
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1317,6 +1336,8 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
|||||||
|
|
||||||
private fun genInstanceOf(obj: LLVMValueRef, type: KotlinType): LLVMValueRef {
|
private fun genInstanceOf(obj: LLVMValueRef, type: KotlinType): LLVMValueRef {
|
||||||
val dstDescriptor = TypeUtils.getClassDescriptor(type) // Get class descriptor for dst type.
|
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 dstTypeInfo = codegen.typeInfoValue(dstDescriptor!!) // Get TypeInfo for dst type.
|
||||||
val srcObjInfoPtr = codegen.bitcast(codegen.kObjHeaderPtr, obj) // Cast src to ObjInfoPtr.
|
val srcObjInfoPtr = codegen.bitcast(codegen.kObjHeaderPtr, obj) // Cast src to ObjInfoPtr.
|
||||||
val args = listOf(srcObjInfoPtr, dstTypeInfo) // Create arg list.
|
val args = listOf(srcObjInfoPtr, dstTypeInfo) // Create arg list.
|
||||||
@@ -1343,7 +1364,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
assert (value.receiver == null)
|
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())
|
return codegen.loadSlot(ptr, value.descriptor.isVar())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1352,13 +1373,12 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
|||||||
|
|
||||||
private fun objectPtrByName(descriptor: ClassDescriptor): LLVMValueRef {
|
private fun objectPtrByName(descriptor: ClassDescriptor): LLVMValueRef {
|
||||||
assert (!descriptor.isUnit())
|
assert (!descriptor.isUnit())
|
||||||
val objName = descriptor.fqNameSafe.asString()
|
return if (codegen.isExternal(descriptor)) {
|
||||||
var objectPtr = LLVMGetNamedGlobal(context.llvmModule, objName)
|
|
||||||
if (objectPtr == null) {
|
|
||||||
val llvmType = codegen.getLLVMType(descriptor.defaultType)
|
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 {
|
else {
|
||||||
assert (value.receiver == null)
|
assert (value.receiver == null)
|
||||||
val globalValue = LLVMGetNamedGlobal(context.llvmModule, value.descriptor.symbolName)
|
val globalValue = context.llvmDeclarations.forStaticField(value.descriptor).storage
|
||||||
codegen.storeAnyGlobal(valueToAssign, globalValue!!)
|
codegen.storeAnyGlobal(valueToAssign, globalValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
assert (value.type.isUnit())
|
assert (value.type.isUnit())
|
||||||
@@ -1415,13 +1435,14 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
private fun fieldPtrOfClass(thisPtr: LLVMValueRef, value: PropertyDescriptor): LLVMValueRef {
|
private fun fieldPtrOfClass(thisPtr: LLVMValueRef, value: PropertyDescriptor): LLVMValueRef {
|
||||||
val objHeaderPtr = codegen.bitcast(codegen.kObjHeaderPtr, thisPtr)
|
val fieldInfo = context.llvmDeclarations.forField(value)
|
||||||
val typePtr = pointerType(codegen.classType(value.containingDeclaration as ClassDescriptor))
|
|
||||||
|
val typePtr = pointerType(fieldInfo.classBodyType)
|
||||||
memScoped {
|
memScoped {
|
||||||
val args = allocArrayOf(kImmOne)
|
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 typedObjPtr = codegen.bitcast(typePtr, objectPtr!!)
|
||||||
val fieldPtr = LLVMBuildStructGEP(codegen.builder, typedObjPtr, codegen.indexInClass(value), "")
|
val fieldPtr = LLVMBuildStructGEP(codegen.builder, typedObjPtr, fieldInfo.index, "")
|
||||||
return fieldPtr!!
|
return fieldPtr!!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1619,7 +1640,8 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
|||||||
value is IrDelegatingConstructorCall ->
|
value is IrDelegatingConstructorCall ->
|
||||||
return delegatingConstructorCall(value.descriptor, args)
|
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 -> {
|
else -> {
|
||||||
TODO("${ir2string(value)}")
|
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
|
val descriptor:FunctionDescriptor = callee.descriptor as FunctionDescriptor
|
||||||
|
|
||||||
if (descriptor.isFunctionInvoke) {
|
if (descriptor.isFunctionInvoke) {
|
||||||
return evaluateFunctionInvoke(descriptor, args)
|
return evaluateFunctionInvoke(descriptor, args, resultLifetime)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (descriptor.isIntrinsic) {
|
if (descriptor.isIntrinsic) {
|
||||||
@@ -1667,8 +1690,9 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
|||||||
|
|
||||||
when (descriptor) {
|
when (descriptor) {
|
||||||
is IrBuiltinOperatorDescriptorBase -> return evaluateOperatorCall (callee, args)
|
is IrBuiltinOperatorDescriptorBase -> return evaluateOperatorCall (callee, args)
|
||||||
is ClassConstructorDescriptor -> return evaluateConstructorCall (callee, args)
|
is ConstructorDescriptor -> return evaluateConstructorCall (callee, args)
|
||||||
else -> return evaluateSimpleFunctionCall(descriptor, 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,
|
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
|
// Note: the whole function code below is written in the assumption that
|
||||||
// `invoke` method receiver is passed as first argument.
|
// `invoke` method receiver is passed as first argument.
|
||||||
@@ -1698,22 +1722,24 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
|||||||
|
|
||||||
// Get `functionImpl.unboundRef`:
|
// Get `functionImpl.unboundRef`:
|
||||||
val unboundRef = evaluateSimpleFunctionCall(functionImplUnboundRefGetter,
|
val unboundRef = evaluateSimpleFunctionCall(functionImplUnboundRefGetter,
|
||||||
listOf(functionImpl))
|
listOf(functionImpl), Lifetime.IRRELEVANT /* unboundRef isn't managed reference */)
|
||||||
|
|
||||||
// Cast `functionImpl.unboundRef` to pointer to function:
|
// Cast `functionImpl.unboundRef` to pointer to function:
|
||||||
val entryPtr = codegen.bitcast(pointerType(unboundRefType), unboundRef, "entry")
|
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)}")
|
//context.log("evaluateSimpleFunctionCall : $tmpVariableName = ${ir2string(value)}")
|
||||||
if (descriptor.isOverridable)
|
if (descriptor.isOverridable && superClass == null)
|
||||||
return callVirtual(descriptor, args)
|
return callVirtual(descriptor, args, resultLifetime)
|
||||||
else
|
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)
|
if (descriptor.dispatchReceiverParameter != null)
|
||||||
args.add(evaluateExpression(value.dispatchReceiver!!)) //add this ptr
|
args.add(evaluateExpression(value.dispatchReceiver!!)) //add this ptr
|
||||||
args.add(evaluateExpression(value.getValueArgument(0)!!))
|
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 {
|
private fun evaluateConstructorCall(callee: IrCall, args: List<LLVMValueRef>): LLVMValueRef {
|
||||||
context.log("evaluateConstructorCall : ${ir2string(callee)}")
|
context.log("evaluateConstructorCall : ${ir2string(callee)}")
|
||||||
memScoped {
|
memScoped {
|
||||||
val containingClass = (callee.descriptor as ClassConstructorDescriptor).containingDeclaration
|
val constructedClass = (callee.descriptor as ConstructorDescriptor).constructedClass
|
||||||
val typeInfo = codegen.typeInfoValue(containingClass)
|
val thisValue = if (constructedClass.isArray) {
|
||||||
val allocHint = Int32(1).llvm
|
|
||||||
val thisValue = if (containingClass.isArray) {
|
|
||||||
assert(args.size >= 1 && args[0].type == int32Type)
|
assert(args.size >= 1 && args[0].type == int32Type)
|
||||||
val allocArrayInstanceArgs = listOf(typeInfo, allocHint, args[0])
|
codegen.allocArray(codegen.typeInfoValue(constructedClass), args[0],
|
||||||
call(context.llvm.allocArrayFunction, allocArrayInstanceArgs)
|
resultLifetime(callee))
|
||||||
} else {
|
} else {
|
||||||
call(context.llvm.allocInstanceFunction, listOf(typeInfo, allocHint))
|
codegen.allocInstance(codegen.typeInfoValue(constructedClass), resultLifetime(callee))
|
||||||
}
|
}
|
||||||
val constructorParams: MutableList<LLVMValueRef> = mutableListOf()
|
evaluateSimpleFunctionCall(callee.descriptor as FunctionDescriptor,
|
||||||
constructorParams += thisValue
|
listOf(thisValue) + args, Lifetime.IRRELEVANT /* constructor doesn't return anything */)
|
||||||
constructorParams += args
|
|
||||||
evaluateSimpleFunctionCall(callee.descriptor as FunctionDescriptor, constructorParams)
|
|
||||||
return thisValue
|
return thisValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1758,8 +1784,8 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
|||||||
|
|
||||||
return when (name) {
|
return when (name) {
|
||||||
"konan.internal.areEqualByValue" -> {
|
"konan.internal.areEqualByValue" -> {
|
||||||
val arg0 = args[0]!!
|
val arg0 = args[0]
|
||||||
val arg1 = args[1]!!
|
val arg1 = args[1]
|
||||||
assert (arg0.type == arg1.type)
|
assert (arg0.type == arg1.type)
|
||||||
|
|
||||||
when (LLVMGetTypeKind(arg0.type)) {
|
when (LLVMGetTypeKind(arg0.type)) {
|
||||||
@@ -1787,12 +1813,12 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
|||||||
val descriptor = callee.descriptor
|
val descriptor = callee.descriptor
|
||||||
val ib = context.irModule!!.irBuiltins
|
val ib = context.irModule!!.irBuiltins
|
||||||
when (descriptor) {
|
when (descriptor) {
|
||||||
ib.eqeqeq -> return codegen.icmpEq(args[0]!!, args[1]!!)
|
ib.eqeqeq -> return codegen.icmpEq(args[0], args[1])
|
||||||
ib.gt0 -> return codegen.icmpGt(args[0]!!, kImmZero)
|
ib.gt0 -> return codegen.icmpGt(args[0], kImmZero)
|
||||||
ib.gteq0 -> return codegen.icmpGe(args[0]!!, kImmZero)
|
ib.gteq0 -> return codegen.icmpGe(args[0], kImmZero)
|
||||||
ib.lt0 -> return codegen.icmpLt(args[0]!!, kImmZero)
|
ib.lt0 -> return codegen.icmpLt(args[0], kImmZero)
|
||||||
ib.lteq0 -> return codegen.icmpLe(args[0]!!, kImmZero)
|
ib.lteq0 -> return codegen.icmpLe(args[0], kImmZero)
|
||||||
ib.booleanNot -> return codegen.icmpNe(args[0]!!, kTrue)
|
ib.booleanNot -> return codegen.icmpNe(args[0], kTrue)
|
||||||
else -> {
|
else -> {
|
||||||
TODO(descriptor.name.toString())
|
TODO(descriptor.name.toString())
|
||||||
}
|
}
|
||||||
@@ -1824,7 +1850,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
|||||||
if (resultPhi != null && !isNothing)
|
if (resultPhi != null && !isNothing)
|
||||||
codegen.assignPhis(resultPhi to brResult)
|
codegen.assignPhis(resultPhi to brResult)
|
||||||
if (bbExit != null && !isNothing)
|
if (bbExit != null && !isNothing)
|
||||||
codegen.br(bbExit!!)
|
codegen.br(bbExit)
|
||||||
if (bbNext != null) // Switch generation to next or exit.
|
if (bbNext != null) // Switch generation to next or exit.
|
||||||
codegen.positionAtEnd(bbNext)
|
codegen.positionAtEnd(bbNext)
|
||||||
else if (bbExit != null)
|
else if (bbExit != null)
|
||||||
@@ -1840,15 +1866,17 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
|||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
//-------------------------------------------------------------------------//
|
||||||
|
|
||||||
fun callDirect(descriptor: FunctionDescriptor, args: List<LLVMValueRef>): LLVMValueRef {
|
fun callDirect(descriptor: FunctionDescriptor, args: List<LLVMValueRef>,
|
||||||
val realDescriptor = DescriptorUtils.unwrapFakeOverride(descriptor)
|
resultLifetime: Lifetime): LLVMValueRef {
|
||||||
|
val realDescriptor = descriptor.resolveFakeOverride().original
|
||||||
val llvmFunction = codegen.functionLlvmValue(realDescriptor)
|
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 typeInfoPtrPtr = LLVMBuildStructGEP(codegen.builder, args[0], 0 /* type_info */, "")!!
|
||||||
val typeInfoPtr = codegen.load(typeInfoPtrPtr)
|
val typeInfoPtr = codegen.load(typeInfoPtrPtr)
|
||||||
assert (typeInfoPtr.type == codegen.kTypeInfoPtr)
|
assert (typeInfoPtr.type == codegen.kTypeInfoPtr)
|
||||||
@@ -1864,18 +1892,17 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
|||||||
val slot = codegen.gep(vtable, Int32(index).llvm)
|
val slot = codegen.gep(vtable, Int32(index).llvm)
|
||||||
codegen.load(slot)
|
codegen.load(slot)
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, call via hashtable.
|
// Otherwise, call by hash.
|
||||||
// TODO: optimize by storing interface number in lower bits of 'this' pointer
|
// 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
|
// when passing object as an interface. This way we can use those bits as index
|
||||||
// for an additional per-interface vtable.
|
// for an additional per-interface vtable.
|
||||||
val methodHash = codegen.functionHash(descriptor) // Calculate hash of the method to be invoked
|
val methodHash = codegen.functionHash(descriptor) // Calculate hash of the method to be invoked
|
||||||
val lookupArgs = listOf(typeInfoPtr, methodHash) // Prepare args for lookup
|
val lookupArgs = listOf(typeInfoPtr, methodHash) // Prepare args for lookup
|
||||||
call(context.llvm.lookupOpenMethodFunction,
|
call(context.llvm.lookupOpenMethodFunction, lookupArgs)
|
||||||
lookupArgs)
|
|
||||||
}
|
}
|
||||||
val functionPtrType = pointerType(codegen.getLlvmFunctionType(descriptor)) // Construct type of the method to be invoked
|
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
|
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.
|
// instead of a plain list.
|
||||||
// In such case it would be possible to check that all args are available and in the correct order.
|
// 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.
|
// However, it currently requires some refactoring to be performed.
|
||||||
private fun call(descriptor: FunctionDescriptor, function: LLVMValueRef, args: List<LLVMValueRef>): LLVMValueRef {
|
private fun call(descriptor: FunctionDescriptor, function: LLVMValueRef, args: List<LLVMValueRef>,
|
||||||
val result = call(function, args)
|
resultLifetime: Lifetime): LLVMValueRef {
|
||||||
|
val result = call(function, args, resultLifetime)
|
||||||
if (descriptor.returnType?.isNothing() == true) {
|
if (descriptor.returnType?.isNothing() == true) {
|
||||||
codegen.unreachable()
|
codegen.unreachable()
|
||||||
}
|
}
|
||||||
@@ -1897,15 +1925,9 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun call(function: LLVMValueRef, args: List<LLVMValueRef>): LLVMValueRef {
|
private fun call(function: LLVMValueRef, args: List<LLVMValueRef>,
|
||||||
if (codegen.isObjectReturn(function.type)) {
|
resultLifetime: Lifetime = Lifetime.IRRELEVANT): LLVMValueRef {
|
||||||
// If function returns an object - create slot for the returned value.
|
return currentCodeContext.genCall(function, args, resultLifetime)
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
//-------------------------------------------------------------------------//
|
||||||
@@ -1923,7 +1945,8 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
|||||||
codegen.bitcast(thisPtrArgType, thisPtr)
|
codegen.bitcast(thisPtrArgType, thisPtr)
|
||||||
}
|
}
|
||||||
|
|
||||||
return callDirect(descriptor, listOf(thisPtrArg) + args)
|
return callDirect(descriptor, listOf(thisPtrArg) + args,
|
||||||
|
Lifetime.IRRELEVANT /* no value returned */)
|
||||||
}
|
}
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
//-------------------------------------------------------------------------//
|
||||||
|
|||||||
+335
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
+21
-24
@@ -111,25 +111,27 @@ internal val ContextUtils.kTheAnyTypeInfo: LLVMValueRef
|
|||||||
get() = KonanPlatform.builtIns.any.llvmTypeInfoPtr
|
get() = KonanPlatform.builtIns.any.llvmTypeInfoPtr
|
||||||
internal val ContextUtils.kTheArrayTypeInfo: LLVMValueRef
|
internal val ContextUtils.kTheArrayTypeInfo: LLVMValueRef
|
||||||
get() = KonanPlatform.builtIns.array.llvmTypeInfoPtr
|
get() = KonanPlatform.builtIns.array.llvmTypeInfoPtr
|
||||||
internal val ContextUtils.kTypeInfo: LLVMTypeRef
|
internal val RuntimeAware.kTypeInfo: LLVMTypeRef
|
||||||
get() = LLVMGetTypeByName(context.llvmModule, "struct.TypeInfo")!!
|
get() = runtime.typeInfoType
|
||||||
internal val ContextUtils.kObjHeader: LLVMTypeRef
|
internal val RuntimeAware.kObjHeader: LLVMTypeRef
|
||||||
get() = LLVMGetTypeByName(context.llvmModule, "struct.ObjHeader")!!
|
get() = runtime.objHeaderType
|
||||||
internal val ContextUtils.kObjHeaderPtr: LLVMTypeRef
|
internal val RuntimeAware.kContainerHeader: LLVMTypeRef
|
||||||
|
get() = runtime.containerHeaderType
|
||||||
|
internal val RuntimeAware.kObjHeaderPtr: LLVMTypeRef
|
||||||
get() = pointerType(kObjHeader)
|
get() = pointerType(kObjHeader)
|
||||||
internal val ContextUtils.kObjHeaderPtrPtr: LLVMTypeRef
|
internal val RuntimeAware.kObjHeaderPtrPtr: LLVMTypeRef
|
||||||
get() = pointerType(kObjHeaderPtr)
|
get() = pointerType(kObjHeaderPtr)
|
||||||
internal val ContextUtils.kArrayHeader: LLVMTypeRef
|
internal val RuntimeAware.kArrayHeader: LLVMTypeRef
|
||||||
get() = LLVMGetTypeByName(context.llvmModule, "struct.ArrayHeader")!!
|
get() = runtime.arrayHeaderType
|
||||||
internal val ContextUtils.kArrayHeaderPtr: LLVMTypeRef
|
internal val RuntimeAware.kArrayHeaderPtr: LLVMTypeRef
|
||||||
get() = pointerType(kArrayHeader)
|
get() = pointerType(kArrayHeader)
|
||||||
internal val ContextUtils.kTypeInfoPtr: LLVMTypeRef
|
internal val RuntimeAware.kTypeInfoPtr: LLVMTypeRef
|
||||||
get() = pointerType(kTypeInfo)
|
get() = pointerType(kTypeInfo)
|
||||||
internal val kInt1 = LLVMInt1Type()!!
|
internal val kInt1 = LLVMInt1Type()!!
|
||||||
internal val kBoolean = kInt1
|
internal val kBoolean = kInt1
|
||||||
internal val kInt8Ptr = pointerType(int8Type)
|
internal val kInt8Ptr = pointerType(int8Type)
|
||||||
internal val kInt8PtrPtr = pointerType(kInt8Ptr)
|
internal val kInt8PtrPtr = pointerType(kInt8Ptr)
|
||||||
internal val kNullInt8Ptr = LLVMConstNull(kInt8Ptr)
|
internal val kNullInt8Ptr = LLVMConstNull(kInt8Ptr)!!
|
||||||
internal val kImmInt32One = Int32(1).llvm
|
internal val kImmInt32One = Int32(1).llvm
|
||||||
internal val kImmInt64One = Int64(1).llvm
|
internal val kImmInt64One = Int64(1).llvm
|
||||||
internal val ContextUtils.kNullObjHeaderPtr: LLVMValueRef
|
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)!!
|
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 {
|
internal fun ContextUtils.numParameters(functionType: LLVMTypeRef) : Int {
|
||||||
// Note that type is usually function pointer, so we have to dereference it.
|
// Note that type is usually function pointer, so we have to dereference it.
|
||||||
return LLVMCountParamTypes(LLVMGetElementType(functionType))!!
|
return LLVMCountParamTypes(LLVMGetElementType(functionType))!!
|
||||||
@@ -176,7 +166,7 @@ internal fun ContextUtils.isObjectRef(value: LLVMValueRef): Boolean {
|
|||||||
return isObjectType(value.type)
|
return isObjectType(value.type)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun ContextUtils.isObjectType(type: LLVMTypeRef): Boolean {
|
internal fun RuntimeAware.isObjectType(type: LLVMTypeRef): Boolean {
|
||||||
return type == kObjHeaderPtr || type == kArrayHeaderPtr
|
return type == kObjHeaderPtr || type == kArrayHeaderPtr
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,6 +188,7 @@ internal fun ContextUtils.externalGlobal(name: String, type: LLVMTypeRef): LLVMV
|
|||||||
val found = LLVMGetNamedGlobal(context.llvmModule, name)
|
val found = LLVMGetNamedGlobal(context.llvmModule, name)
|
||||||
if (found != null) {
|
if (found != null) {
|
||||||
assert (getGlobalType(found) == type)
|
assert (getGlobalType(found) == type)
|
||||||
|
assert (LLVMGetInitializer(found) == null)
|
||||||
return found
|
return found
|
||||||
} else {
|
} else {
|
||||||
return LLVMAddGlobal(context.llvmModule, type, name)!!
|
return LLVMAddGlobal(context.llvmModule, type, name)!!
|
||||||
@@ -225,3 +216,9 @@ internal operator fun LLVMAttributeSet.contains(attribute: LLVMAttribute): Boole
|
|||||||
return (this and attribute.value) != 0
|
return (this and attribute.value) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getStructElements(type: LLVMTypeRef): List<LLVMTypeRef> {
|
||||||
|
val count = LLVMCountStructElementTypes(type)
|
||||||
|
return (0 until count).map {
|
||||||
|
LLVMStructGetTypeAtIndex(type, it)!!
|
||||||
|
}
|
||||||
|
}
|
||||||
-99
@@ -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()}"
|
|
||||||
+28
-42
@@ -1,7 +1,5 @@
|
|||||||
package org.jetbrains.kotlin.backend.konan.llvm
|
package org.jetbrains.kotlin.backend.konan.llvm
|
||||||
|
|
||||||
|
|
||||||
import kotlinx.cinterop.*
|
|
||||||
import llvm.*
|
import llvm.*
|
||||||
import org.jetbrains.kotlin.backend.konan.Context
|
import org.jetbrains.kotlin.backend.konan.Context
|
||||||
import org.jetbrains.kotlin.backend.konan.descriptors.*
|
import org.jetbrains.kotlin.backend.konan.descriptors.*
|
||||||
@@ -52,18 +50,6 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
|||||||
Int32(fieldsCount)
|
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?) {
|
private fun exportTypeInfoIfRequired(classDesc: ClassDescriptor, typeInfoGlobal: LLVMValueRef?) {
|
||||||
val annot = classDesc.annotations.findAnnotation(FqName("konan.ExportTypeInfo"))
|
val annot = classDesc.annotations.findAnnotation(FqName("konan.ExportTypeInfo"))
|
||||||
if (annot != null) {
|
if (annot != null) {
|
||||||
@@ -97,11 +83,13 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
|||||||
|
|
||||||
val className = classDesc.fqNameSafe
|
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 name = className.globalHash
|
||||||
|
|
||||||
val size = getInstanceSize(classType, className)
|
val size = getInstanceSize(bodyType, className)
|
||||||
|
|
||||||
val superTypeOrAny = classDesc.getSuperClassOrAny()
|
val superTypeOrAny = classDesc.getSuperClassOrAny()
|
||||||
val superType = if (KotlinBuiltIns.isAny(classDesc)) NullPointer(runtime.typeInfoType)
|
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",
|
val interfacesPtr = staticData.placeGlobalConstArray("kintf:$className",
|
||||||
pointerType(runtime.typeInfoType), interfaces)
|
pointerType(runtime.typeInfoType), interfaces)
|
||||||
|
|
||||||
val refFieldIndices = classDesc.fields.mapIndexedNotNull { index, field ->
|
// TODO: reuse offsets obtained for 'fields' below
|
||||||
val type = field.returnType!!
|
val objOffsets = getStructElements(bodyType).mapIndexedNotNull { index, type ->
|
||||||
if (isObjectType(getLLVMType(type))) {
|
if (isObjectType(type)) {
|
||||||
index
|
LLVMOffsetOfElement(llvmTargetData, bodyType, index)
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO: reuse offsets obtained for 'fields' below
|
|
||||||
val objOffsets = refFieldIndices.map { LLVMOffsetOfElement(llvmTargetData, classType, it) }
|
|
||||||
val objOffsetsPtr = staticData.placeGlobalConstArray("krefs:$className", int32Type,
|
val objOffsetsPtr = staticData.placeGlobalConstArray("krefs:$className", int32Type,
|
||||||
objOffsets.map { Int32(it.toInt()) })
|
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
|
// 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 nameSignature = field.fqNameSafe.localHash // FIXME: add signature
|
||||||
val fieldOffset = LLVMOffsetOfElement(llvmTargetData, classType, index)
|
val fieldOffset = LLVMOffsetOfElement(llvmTargetData, bodyType, index)
|
||||||
FieldTableRecord(nameSignature, fieldOffset.toInt())
|
FieldTableRecord(nameSignature, fieldOffset.toInt())
|
||||||
}.sortedBy { it.nameSignature.value }
|
}.sortedBy { it.nameSignature.value }
|
||||||
|
|
||||||
val fieldsPtr = staticData.placeGlobalConstArray("kfields:$className",
|
val fieldsPtr = staticData.placeGlobalConstArray("kfields:$className",
|
||||||
runtime.fieldTableRecordType, fields)
|
runtime.fieldTableRecordType, fields)
|
||||||
|
|
||||||
val vtableEntries: List<ConstValue>
|
val methods = if (!classDesc.isAbstract()) {
|
||||||
val methods: List<ConstValue>
|
classDesc.methodTableEntries.map {
|
||||||
|
|
||||||
if (!classDesc.isAbstract()) {
|
|
||||||
// TODO: compile-time resolution limits binary compatibility
|
|
||||||
vtableEntries = classDesc.vtableEntries.map { it.implementation.entryPointAddress }
|
|
||||||
|
|
||||||
methods = classDesc.methodTableEntries.map {
|
|
||||||
val nameSignature = it.functionName.localHash
|
val nameSignature = it.functionName.localHash
|
||||||
// TODO: compile-time resolution limits binary compatibility
|
// TODO: compile-time resolution limits binary compatibility
|
||||||
val methodEntryPoint = it.implementation.entryPointAddress
|
val methodEntryPoint = it.resolveFakeOverride().original.entryPointAddress
|
||||||
MethodTableRecord(nameSignature, methodEntryPoint)
|
MethodTableRecord(nameSignature, methodEntryPoint)
|
||||||
}.sortedBy { it.nameSignature.value }
|
}.sortedBy { it.nameSignature.value }
|
||||||
} else {
|
} else {
|
||||||
vtableEntries = emptyList()
|
emptyList()
|
||||||
methods = emptyList()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
assert (vtableEntries.size == classDesc.vtableSize)
|
|
||||||
|
|
||||||
val vtable = ConstArray(int8TypePtr, vtableEntries)
|
|
||||||
|
|
||||||
val methodsPtr = staticData.placeGlobalConstArray("kmethods:$className",
|
val methodsPtr = staticData.placeGlobalConstArray("kmethods:$className",
|
||||||
runtime.methodTableRecordType, methods)
|
runtime.methodTableRecordType, methods)
|
||||||
|
|
||||||
@@ -166,9 +142,19 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
|||||||
methodsPtr, methods.size,
|
methodsPtr, methods.size,
|
||||||
fieldsPtr, if (classDesc.isInterface) -1 else fields.size)
|
fieldsPtr, if (classDesc.isInterface) -1 else fields.size)
|
||||||
|
|
||||||
val typeInfoGlobal = classDesc.typeInfoWithVtable.llvm // TODO: it is a hack
|
val typeInfoGlobal = llvmDeclarations.typeInfoGlobal
|
||||||
LLVMSetInitializer(typeInfoGlobal, Struct(typeInfo, vtable).llvm)
|
|
||||||
LLVMSetGlobalConstant(typeInfoGlobal, 1)
|
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)
|
exportTypeInfoIfRequired(classDesc, classDesc.llvmTypeInfoPtr)
|
||||||
}
|
}
|
||||||
|
|||||||
+5
@@ -3,6 +3,10 @@ package org.jetbrains.kotlin.backend.konan.llvm
|
|||||||
import kotlinx.cinterop.*
|
import kotlinx.cinterop.*
|
||||||
import llvm.*
|
import llvm.*
|
||||||
|
|
||||||
|
interface RuntimeAware {
|
||||||
|
val runtime: Runtime
|
||||||
|
}
|
||||||
|
|
||||||
class Runtime(private val bitcodeFile: String) {
|
class Runtime(private val bitcodeFile: String) {
|
||||||
val llvmModule: LLVMModuleRef
|
val llvmModule: LLVMModuleRef
|
||||||
|
|
||||||
@@ -11,6 +15,7 @@ class Runtime(private val bitcodeFile: String) {
|
|||||||
|
|
||||||
val bufRef = alloc<LLVMMemoryBufferRefVar>()
|
val bufRef = alloc<LLVMMemoryBufferRefVar>()
|
||||||
val errorRef = allocPointerTo<CInt8Var>()
|
val errorRef = allocPointerTo<CInt8Var>()
|
||||||
|
|
||||||
val res = LLVMCreateMemoryBufferWithContentsOfFile(bitcodeFile, bufRef.ptr, errorRef.ptr)
|
val res = LLVMCreateMemoryBufferWithContentsOfFile(bitcodeFile, bufRef.ptr, errorRef.ptr)
|
||||||
if (res != 0) {
|
if (res != 0) {
|
||||||
throw Error(errorRef.value?.asCString()?.toString())
|
throw Error(errorRef.value?.asCString()?.toString())
|
||||||
|
|||||||
+20
-20
@@ -15,34 +15,34 @@ internal class StaticData(override val context: Context): ContextUtils {
|
|||||||
class Global private constructor(val staticData: StaticData, val llvmGlobal: LLVMValueRef) {
|
class Global private constructor(val staticData: StaticData, val llvmGlobal: LLVMValueRef) {
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
private fun createLlvmGlobal(module: LLVMModuleRef, type: LLVMTypeRef, name: String): LLVMValueRef {
|
private fun createLlvmGlobal(module: LLVMModuleRef,
|
||||||
val isUnnamed = (name == "") // LLVM will select the unique index and represent the global as `@idx`.
|
type: LLVMTypeRef,
|
||||||
|
name: String,
|
||||||
|
isExported: Boolean
|
||||||
|
): LLVMValueRef {
|
||||||
|
|
||||||
if (!isUnnamed) {
|
if (isExported && LLVMGetNamedGlobal(module, name) != null) {
|
||||||
LLVMGetNamedGlobal(module, name)?.let { existingGlobal ->
|
throw IllegalArgumentException("Global '$name' already exists")
|
||||||
if (getGlobalType(existingGlobal) != type || LLVMGetInitializer(existingGlobal) != null) {
|
|
||||||
throw IllegalArgumentException("Global '$name' already exists")
|
|
||||||
} else {
|
|
||||||
return existingGlobal
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val llvmGlobal = LLVMAddGlobal(module, type, name)!!
|
val llvmGlobal = LLVMAddGlobal(module, type, name)!!
|
||||||
|
|
||||||
if (isUnnamed) {
|
if (!isExported) {
|
||||||
// Currently using unnamed globals may result in link errors due to symbol redefinition;
|
LLVMSetLinkage(llvmGlobal, LLVMLinkage.LLVMInternalLinkage)
|
||||||
// apply the workaround:
|
|
||||||
LLVMSetLinkage(llvmGlobal, LLVMLinkage.LLVMPrivateLinkage)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return llvmGlobal
|
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 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)
|
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].
|
* It is external until explicitly initialized with [Global.setInitializer].
|
||||||
*/
|
*/
|
||||||
fun createGlobal(type: LLVMTypeRef, name: String): Global {
|
fun createGlobal(type: LLVMTypeRef, name: String, isExported: Boolean = false): Global {
|
||||||
return Global.create(this, type, name)
|
return Global.create(this, type, name, isExported)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates [Global] with given name and value.
|
* Creates [Global] with given name and value.
|
||||||
*/
|
*/
|
||||||
fun placeGlobal(name: String, initializer: ConstValue): Global {
|
fun placeGlobal(name: String, initializer: ConstValue, isExported: Boolean = false): Global {
|
||||||
val global = createGlobal(initializer.llvmType, name)
|
val global = createGlobal(initializer.llvmType, name, isExported)
|
||||||
global.setInitializer(initializer)
|
global.setInitializer(initializer)
|
||||||
return global
|
return global
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-7
@@ -117,16 +117,23 @@ internal fun StaticData.createArrayList(elementType: TypeProjection, array: Cons
|
|||||||
return createKotlinObject(type, body)
|
return createKotlinObject(type, body)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val theUnitInstanceName = "kobj:kotlin.Unit"
|
internal fun StaticData.createUnitInstance(descriptor: ClassDescriptor,
|
||||||
|
bodyType: LLVMTypeRef,
|
||||||
internal fun StaticData.createUnitInstance(descriptor: ClassDescriptor): ConstPointer {
|
typeInfo: ConstPointer
|
||||||
|
): ConstPointer {
|
||||||
assert (descriptor.isUnit())
|
assert (descriptor.isUnit())
|
||||||
assert (descriptor.fields.isEmpty())
|
assert (getStructElements(bodyType).isEmpty())
|
||||||
val typeInfo = descriptor.defaultType.typeInfoPtr!!
|
|
||||||
val objHeader = objHeader(typeInfo)
|
val objHeader = objHeader(typeInfo)
|
||||||
val global = this.placeGlobal(theUnitInstanceName, objHeader)
|
val global = this.placeGlobal(theUnitInstanceName, objHeader, isExported = true)
|
||||||
return global.pointer
|
return global.pointer
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val ContextUtils.theUnitInstanceRef: ConstPointer
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
+23
-14
@@ -2,6 +2,7 @@ package org.jetbrains.kotlin.backend.konan.llvm
|
|||||||
|
|
||||||
import llvm.*
|
import llvm.*
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.types.*
|
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}"
|
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 load() : LLVMValueRef = value
|
||||||
override fun store(value: LLVMValueRef) = throw Error("writing to immutable: ${descriptor}")
|
override fun store(value: LLVMValueRef) = throw Error("writing to immutable: ${name}")
|
||||||
override fun address() : LLVMValueRef = throw Error("no address for: ${descriptor}")
|
override fun address() : LLVMValueRef = throw Error("no address for: ${name}")
|
||||||
override fun toString() = "value of ${value} from ${descriptor}"
|
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 variables: ArrayList<Record> = arrayListOf()
|
||||||
val descriptors: HashMap<Pair<ValueDescriptor, CodeContext>, Int> = hashMapOf()
|
val contextVariablesToIndex: HashMap<ScopedVariable, Int> = hashMapOf()
|
||||||
|
|
||||||
// Clears inner state of variable manager.
|
// Clears inner state of variable manager.
|
||||||
fun clear() {
|
fun clear() {
|
||||||
variables.clear()
|
variables.clear()
|
||||||
descriptors.clear()
|
contextVariablesToIndex.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createVariable(scoped: Pair<VariableDescriptor, CodeContext>, value: LLVMValueRef? = null) : Int {
|
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>,
|
fun createMutable(scoped: Pair<VariableDescriptor, CodeContext>,
|
||||||
isVar: Boolean, value: LLVMValueRef? = null) : Int {
|
isVar: Boolean, value: LLVMValueRef? = null) : Int {
|
||||||
val descriptor = scoped.first
|
val descriptor = scoped.first
|
||||||
assert(descriptors.get(scoped) == null)
|
val scopedVariable = ScopedVariable(scoped)
|
||||||
|
assert(!contextVariablesToIndex.contains(scopedVariable))
|
||||||
val index = variables.size
|
val index = variables.size
|
||||||
val type = codegen.getLLVMType(descriptor.type)
|
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)
|
if (value != null)
|
||||||
codegen.storeAnyLocal(value, slot)
|
codegen.storeAnyLocal(value, slot)
|
||||||
variables.add(SlotRecord(slot, codegen.isObjectType(type), isVar))
|
variables.add(SlotRecord(slot, codegen.isObjectType(type), isVar))
|
||||||
descriptors[scoped] = index
|
contextVariablesToIndex[scopedVariable] = index
|
||||||
return index
|
return index
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,16 +90,18 @@ internal class VariableManager(val codegen: CodeGenerator) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun createImmutable(scoped: Pair<ValueDescriptor, CodeContext>, value: LLVMValueRef) : Int {
|
fun createImmutable(scoped: Pair<ValueDescriptor, CodeContext>, value: LLVMValueRef) : Int {
|
||||||
val descriptor = scoped.first
|
val scopedVariable = ScopedVariable(scoped)
|
||||||
assert(descriptors.get(scoped) == null)
|
if(contextVariablesToIndex.containsKey(scopedVariable))
|
||||||
|
throw Error("${scopedVariable.descriptor} is already defined")
|
||||||
|
assert(!contextVariablesToIndex.containsKey(scopedVariable))
|
||||||
val index = variables.size
|
val index = variables.size
|
||||||
variables.add(ValueRecord(value, descriptor))
|
variables.add(ValueRecord(value, scopedVariable.descriptor.name))
|
||||||
descriptors[scoped] = index
|
contextVariablesToIndex[scopedVariable] = index
|
||||||
return index
|
return index
|
||||||
}
|
}
|
||||||
|
|
||||||
fun indexOf(scoped: Pair<ValueDescriptor, CodeContext>) : Int {
|
fun indexOf(scoped: Pair<ValueDescriptor, CodeContext>) : Int {
|
||||||
return descriptors.getOrElse(scoped) { -1 }
|
return contextVariablesToIndex.getOrElse(ScopedVariable(scoped)) { -1 }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addressOf(index: Int): LLVMValueRef {
|
fun addressOf(index: Int): LLVMValueRef {
|
||||||
|
|||||||
+13
-3
@@ -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.getKonanInternalClass
|
||||||
import org.jetbrains.kotlin.backend.konan.descriptors.getKonanInternalFunctions
|
import org.jetbrains.kotlin.backend.konan.descriptors.getKonanInternalFunctions
|
||||||
import org.jetbrains.kotlin.backend.konan.descriptors.unboundCallableReferenceTypeOrNull
|
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.PropertyDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||||
import org.jetbrains.kotlin.ir.expressions.*
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
||||||
@@ -128,8 +129,17 @@ private class AutoboxingTransformer(val context: Context) : AbstractValueUsageTr
|
|||||||
return this.adaptIfNecessary(actualType, type)
|
return this.adaptIfNecessary(actualType, type)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun IrExpression.useAsArgument(parameter: ParameterDescriptor): IrExpression {
|
override fun IrExpression.useAsDispatchReceiver(function: CallableDescriptor): IrExpression {
|
||||||
return this.useAsValue(parameter.original)
|
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 {
|
override fun IrExpression.useForField(field: PropertyDescriptor): IrExpression {
|
||||||
|
|||||||
+3
-3
@@ -1,7 +1,7 @@
|
|||||||
package org.jetbrains.kotlin.backend.konan.lower
|
package org.jetbrains.kotlin.backend.konan.lower
|
||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.DeclarationContainerLoweringPass
|
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.*
|
||||||
import org.jetbrains.kotlin.backend.konan.descriptors.*
|
import org.jetbrains.kotlin.backend.konan.descriptors.*
|
||||||
import org.jetbrains.kotlin.backend.konan.ir.*
|
import org.jetbrains.kotlin.backend.konan.ir.*
|
||||||
@@ -136,8 +136,8 @@ private class CallableReferencesUnbinder(val lower: CallableReferenceLowering,
|
|||||||
// Create new function descriptor:
|
// Create new function descriptor:
|
||||||
val newDescriptor = createSimpleFunctionImplTargetDescriptor(descriptor, unboundParams)
|
val newDescriptor = createSimpleFunctionImplTargetDescriptor(descriptor, unboundParams)
|
||||||
|
|
||||||
val generator = lower.context.createFunctionIrGenerator(newDescriptor)
|
val builder = lower.context.createFunctionIrBuilder(newDescriptor)
|
||||||
val blockBody = generator.irBlockBody(startOffset, endOffset) {
|
val blockBody = builder.irBlockBody(startOffset, endOffset) {
|
||||||
val boundArgsGet = irCall(simpleFunctionImplBoundArgsGetter).apply {
|
val boundArgsGet = irCall(simpleFunctionImplBoundArgsGetter).apply {
|
||||||
dispatchReceiver = irGet(newDescriptor.valueParameters[0])
|
dispatchReceiver = irGet(newDescriptor.valueParameters[0])
|
||||||
}
|
}
|
||||||
|
|||||||
+203
-93
@@ -2,14 +2,15 @@ package org.jetbrains.kotlin.backend.konan.lower
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
|
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
|
||||||
import org.jetbrains.kotlin.backend.common.DeclarationContainerLoweringPass
|
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.common.lower.createIrBuilder
|
import org.jetbrains.kotlin.backend.common.lower.irBlockBody
|
||||||
import org.jetbrains.kotlin.backend.konan.Context
|
import org.jetbrains.kotlin.backend.konan.Context
|
||||||
import org.jetbrains.kotlin.backend.konan.KonanPlatform
|
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.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
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.SimpleFunctionDescriptorImpl
|
||||||
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
|
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
|
||||||
import org.jetbrains.kotlin.ir.IrElement
|
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.IrDeclarationOriginImpl
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
|
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.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
|
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.util.transformFlat
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
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> {
|
private fun lower(irFunction: IrFunction): List<IrFunction> {
|
||||||
val bodies = mutableListOf<IrExpressionBody>()
|
val bodies = mutableListOf<IrExpressionBody>()
|
||||||
|
val functionDescriptor = irFunction.descriptor
|
||||||
|
|
||||||
|
if (hasNotDefaultParameters(functionDescriptor))
|
||||||
|
return irFunction.singletonList()
|
||||||
|
|
||||||
irFunction.acceptChildrenVoid(object:IrElementVisitorVoid{
|
irFunction.acceptChildrenVoid(object:IrElementVisitorVoid{
|
||||||
override fun visitExpressionBody(body: IrExpressionBody) {
|
override fun visitExpressionBody(body: IrExpressionBody) {
|
||||||
bodies.add(body)
|
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()) {
|
if (bodies.isNotEmpty()) {
|
||||||
val (descriptor, mask, extension, dispatch) = functionDescriptor.generateDefaultsDescriptor()
|
val description = functionDescriptor.getOrCreateDefaultsDescription(context)
|
||||||
val generator = context.createFunctionIrGenerator(descriptor)
|
val builder = context.createFunctionIrBuilder(description.function)
|
||||||
val builder = generator.createIrBuilder()
|
val body = builder.irBlockBody(irFunction) {
|
||||||
builder.apply {
|
|
||||||
startOffset = irFunction.startOffset
|
|
||||||
endOffset = irFunction.endOffset
|
|
||||||
}
|
|
||||||
val body = generator.irBlockBody {
|
|
||||||
val params = mutableListOf<VariableDescriptor>()
|
val params = mutableListOf<VariableDescriptor>()
|
||||||
val variables = mutableMapOf<VariableDescriptor, 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) {
|
for (valueParameter in functionDescriptor.valueParameters) {
|
||||||
val parameterDescriptor = descriptor.valueParameters[valueParameter.index]
|
val parameterDescriptor = description.function.valueParameters[valueParameter.index]
|
||||||
if (valueParameter.hasDefaultValue()) {
|
if (valueParameter.hasDefaultValue()) {
|
||||||
val variable = scope.createTemporaryVariable(
|
val variable = scope.createTemporaryVariable(
|
||||||
irExpression = nullConst(valueParameter.type)!!,
|
irExpression = nullConst(valueParameter.type)!!,
|
||||||
@@ -82,13 +97,15 @@ class DefaultParameterStubGenerator internal constructor(val context: Context):
|
|||||||
params.add(variableDescriptor)
|
params.add(variableDescriptor)
|
||||||
+variable
|
+variable
|
||||||
val condition = irNotEquals(irCall(intAnd).apply {
|
val condition = irNotEquals(irCall(intAnd).apply {
|
||||||
dispatchReceiver = irGet(mask)
|
dispatchReceiver = irGet(description.mask)
|
||||||
putValueArgument(0, irInt(1 shl valueParameter.index))
|
putValueArgument(0, irInt(1 shl valueParameter.index))
|
||||||
}, irInt(0))
|
}, irInt(0))
|
||||||
val exprBody = getDefaultParameterExpressionBody(irFunction, valueParameter)
|
val exprBody = getDefaultParameterExpressionBody(irFunction, valueParameter)
|
||||||
/* Use previously calculated values in next expression. */
|
/* Use previously calculated values in next expression. */
|
||||||
exprBody.expression.transformChildrenVoid(object:IrElementTransformerVoid() {
|
exprBody.expression.transformChildrenVoid(object:IrElementTransformerVoid() {
|
||||||
override fun visitGetValue(expression: IrGetValue): IrExpression {
|
override fun visitGetValue(expression: IrGetValue): IrExpression {
|
||||||
|
if (expression.descriptor == functionDescriptor.extensionReceiverParameter)
|
||||||
|
return newExtensionReceiver!!
|
||||||
if (!variables.containsKey(expression.descriptor))
|
if (!variables.containsKey(expression.descriptor))
|
||||||
return expression
|
return expression
|
||||||
return irGet(variables[expression.descriptor] as VariableDescriptor)
|
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. */
|
/* Mapping calculated values with its origin variables. */
|
||||||
variables.put(valueParameter, variableDescriptor)
|
variables.put(valueParameter, variableDescriptor)
|
||||||
+irIfThenElse(
|
+irIfThenElse(
|
||||||
type = KonanPlatform.builtIns.unitType,
|
type = KonanPlatform.builtIns.unitType,
|
||||||
condition = condition,
|
condition = condition,
|
||||||
thenPart = irSetVar(variableDescriptor, exprBody.expression),
|
thenPart = irSetVar(variableDescriptor, exprBody.expression),
|
||||||
elsePart = irSetVar(variableDescriptor, irGet(parameterDescriptor)))
|
elsePart = irSetVar(variableDescriptor, irGet(parameterDescriptor)))
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
params.add(parameterDescriptor)
|
params.add(parameterDescriptor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+ irReturn(irCall(functionDescriptor).apply {
|
if (functionDescriptor !is ClassConstructorDescriptor) {
|
||||||
if (functionDescriptor.dispatchReceiverParameter != null) {
|
+irReturn(irCall(functionDescriptor).apply {
|
||||||
dispatchReceiver = irGet(dispatch!!)
|
if (functionDescriptor.dispatchReceiverParameter != null) {
|
||||||
}
|
dispatchReceiver = irThis()
|
||||||
if (functionDescriptor.extensionReceiverParameter != null) {
|
}
|
||||||
extensionReceiver = irGet(extension!!)
|
if (functionDescriptor.extensionReceiverParameter != null) {
|
||||||
}
|
extensionReceiver = newExtensionReceiver
|
||||||
params.forEachIndexed { i, variable ->
|
}
|
||||||
putValueArgument(i, irGet(variable))
|
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.
|
// TODO: replace irFunction with new one without expression bodies.
|
||||||
return listOf(irFunction, IrFunctionImpl(
|
return listOf(irFunction, IrFunctionImpl(
|
||||||
irFunction.startOffset ,
|
irFunction.startOffset,
|
||||||
irFunction.endOffset,
|
irFunction.endOffset,
|
||||||
DECLARATION_ORIGIN_FUNCTION_FOR_DEFAULT_PARAMETER,
|
DECLARATION_ORIGIN_FUNCTION_FOR_DEFAULT_PARAMETER,
|
||||||
descriptor, body))
|
description.function, body))
|
||||||
}
|
}
|
||||||
return irFunction.singletonList()
|
return irFunction.singletonList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun log(msg:String) = context.log("DEFAULT-REPLACER: $msg")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getDefaultParameterExpressionBody(irFunction: IrFunction, valueParameter: ValueParameterDescriptor):IrExpressionBody {
|
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 {
|
class DefaultParameterInjector internal constructor(val context: Context): BodyLoweringPass {
|
||||||
override fun lower(irBody: IrBody) {
|
override fun lower(irBody: IrBody) {
|
||||||
irBody.transformChildrenVoid(object : IrElementTransformerVoid() {
|
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 {
|
override fun visitCall(expression: IrCall): IrExpression {
|
||||||
super.visitCall(expression)
|
super.visitCall(expression)
|
||||||
val descriptor = expression.descriptor
|
val functionDescriptor = expression.descriptor as FunctionDescriptor
|
||||||
if (descriptor.valueParameters.none{it.hasDefaultValue()})
|
|
||||||
|
if (hasNotDefaultParameters(functionDescriptor))
|
||||||
return expression
|
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
|
var argumentsCount = 0
|
||||||
expression.acceptChildrenVoid(object:IrElementVisitorVoid{
|
expression.acceptChildrenVoid(object : IrElementVisitorVoid {
|
||||||
override fun visitElement(element: IrElement) {
|
override fun visitElement(element: IrElement) {
|
||||||
argumentsCount++
|
argumentsCount++
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if (descriptor.dispatchReceiverParameter != null || descriptor.extensionReceiverParameter != null)
|
val descriptor = expression.descriptor
|
||||||
|
if (descriptor !is ConstructorDescriptor && (descriptor.dispatchReceiverParameter != null || descriptor.extensionReceiverParameter != null))
|
||||||
argumentsCount--
|
argumentsCount--
|
||||||
if (argumentsCount == descriptor.valueParameters.size)
|
return argumentsCount
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 intDesctiptor = DescriptorUtils.getClassDescriptorForType(KonanPlatform.builtIns.intType)
|
||||||
val intAnd = DescriptorUtils.getFunctionByName(intDesctiptor.unsubstitutedMemberScope, Name.identifier("and"))
|
val intAnd = DescriptorUtils.getFunctionByName(intDesctiptor.unsubstitutedMemberScope, Name.identifier("and"))
|
||||||
|
|
||||||
data class DefaultParameterDescriptor(val function: FunctionDescriptor, val mask:ValueParameterDescriptor,
|
data class DefaultParameterDescription(val function: FunctionDescriptor, val mask:ValueParameterDescriptor,
|
||||||
val extensionReceiver:ValueParameterDescriptor?, val dispatchReceiver: ValueParameterDescriptor?)
|
val hasExtensionReceiver:Boolean, val hasDispatchReceiver: Boolean)
|
||||||
|
|
||||||
fun FunctionDescriptor.generateDefaultsDescriptor():DefaultParameterDescriptor {
|
private fun FunctionDescriptor.getOrCreateDefaultsDescription(context: Context): DefaultParameterDescription {
|
||||||
val name = Name.identifier("${this.name.asString()}\$default")
|
return context.ir.defaultParameterDescriptions.getOrPut(this) {
|
||||||
val descriptor = SimpleFunctionDescriptorImpl.create(this.containingDeclaration, Annotations.EMPTY,
|
this.generateDefaultsDescription(context)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
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{
|
val parameterList = mutableListOf<ValueParameterDescriptor>()
|
||||||
if (it.hasDefaultValue()) ValueParameterDescriptorImpl(
|
|
||||||
|
val maskVariable = valueParameter(descriptor, valueParameters.size , "__\$mask\$__", KonanPlatform.builtIns.intType)
|
||||||
|
|
||||||
|
parameterList += valueParameters.map{
|
||||||
|
ValueParameterDescriptorImpl(
|
||||||
containingDeclaration = it.containingDeclaration,
|
containingDeclaration = it.containingDeclaration,
|
||||||
original = it.original,
|
original = it.original,
|
||||||
index = it.index,
|
index = it.index,
|
||||||
@@ -235,21 +327,22 @@ fun FunctionDescriptor.generateDefaultsDescriptor():DefaultParameterDescriptor {
|
|||||||
isNoinline = it.isNoinline,
|
isNoinline = it.isNoinline,
|
||||||
varargElementType = it.varargElementType,
|
varargElementType = it.varargElementType,
|
||||||
source = it.source)
|
source = it.source)
|
||||||
else it
|
}
|
||||||
}.toTypedArray())
|
|
||||||
|
|
||||||
parameterList.add(maskVariable)
|
parameterList.add(maskVariable)
|
||||||
if (extensionReceiver != null) parameterList.add(extensionReceiver)
|
|
||||||
if (dispatchReceiver != null) parameterList.add(dispatchReceiver)
|
|
||||||
descriptor.initialize(
|
descriptor.initialize(
|
||||||
/* receiverParameterType = */ null,
|
/* receiverParameterType = */ extensionReceiverParameter?.type,
|
||||||
/* dispatchReceiverParameterType = */ null,
|
/* dispatchReceiverParameter = */ dispatchReceiverParameter,
|
||||||
/* typeParameters = */ typeParameters,
|
/* typeParameters = */ typeParameters,
|
||||||
/* unsubstitutedValueParameters = */ parameterList,
|
/* unsubstitutedValueParameters = */ parameterList,
|
||||||
/* unsubstitutedReturnType = */ returnType,
|
/* unsubstitutedReturnType = */ returnType,
|
||||||
/* modality = */ this.modality,
|
/* modality = */ Modality.FINAL,
|
||||||
/* visibility = */ this.visibility)
|
/* 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 {
|
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,
|
varargElementType = null,
|
||||||
source = SourceElement.NO_SOURCE
|
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")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+600
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
-6
@@ -74,13 +74,13 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoid(
|
|||||||
return parametersNew
|
return parametersNew
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
//-------------------------------------------------------------------------//
|
||||||
|
|
||||||
fun inlineFunction(irCall: IrCall): IrBlock {
|
fun inlineFunction(irCall: IrCall): IrBlock {
|
||||||
|
|
||||||
val functionDescriptor = irCall.descriptor as FunctionDescriptor
|
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 copyFuncDeclaration = functionDeclaration!!.accept(DeepCopyIrTree(), null) as IrFunction // Create copy of the function.
|
||||||
|
|
||||||
val startOffset = copyFuncDeclaration.startOffset
|
val startOffset = copyFuncDeclaration.startOffset
|
||||||
@@ -111,11 +111,11 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoid(
|
|||||||
|
|
||||||
override fun visitCall(expression: IrCall): IrExpression {
|
override fun visitCall(expression: IrCall): IrExpression {
|
||||||
val functionDescriptor = expression.descriptor as FunctionDescriptor
|
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.
|
val functionDeclaration = context.ir.originalModuleIndex.functions[functionDescriptor] // Get FunctionDeclaration by FunctionDescriptor.
|
||||||
if (functionDeclaration == null) return super.visitCall(expression) // Function is declared in another module.
|
if (functionDeclaration == null) return super.visitCall(expression) // Function is declared in another module.
|
||||||
return inlineFunction(expression) // Return newly created IrBlock instead of IrCall.
|
return inlineFunction(expression) // Return newly created IrBlock instead of IrCall.
|
||||||
}
|
}
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
//-------------------------------------------------------------------------//
|
||||||
|
|||||||
+141
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
+78
-6
@@ -1,10 +1,12 @@
|
|||||||
package org.jetbrains.kotlin.backend.konan.lower
|
package org.jetbrains.kotlin.backend.konan.lower
|
||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.FunctionLoweringPass
|
import org.jetbrains.kotlin.backend.common.FunctionLoweringPass
|
||||||
import org.jetbrains.kotlin.backend.common.lower.createFunctionIrGenerator
|
import org.jetbrains.kotlin.backend.common.lower.*
|
||||||
import org.jetbrains.kotlin.backend.common.lower.irBlock
|
|
||||||
import org.jetbrains.kotlin.backend.konan.Context
|
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.FunctionDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||||
import org.jetbrains.kotlin.ir.IrStatement
|
import org.jetbrains.kotlin.ir.IrStatement
|
||||||
import org.jetbrains.kotlin.ir.builders.*
|
import org.jetbrains.kotlin.ir.builders.*
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
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.expressions.IrTypeOperatorCall
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
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.
|
* 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 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 {
|
override fun visitFunction(declaration: IrFunction): IrStatement {
|
||||||
// ignore inner functions during this pass
|
// ignore inner functions during this pass
|
||||||
return declaration
|
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 {
|
private fun lowerSafeCast(expression: IrTypeOperatorCall): IrExpression {
|
||||||
return generator.irBlock(expression) {
|
val typeOperand = expression.typeOperand.erasure()
|
||||||
|
|
||||||
|
return builder.irBlock(expression) {
|
||||||
+irLet(expression.argument) { variable ->
|
+irLet(expression.argument) { variable ->
|
||||||
irIfThenElse(expression.type,
|
irIfThenElse(expression.type,
|
||||||
condition = irIs(irGet(variable), expression.typeOperand),
|
condition = irIs(irGet(variable), typeOperand),
|
||||||
thenPart = irImplicitCast(irGet(variable), expression.typeOperand),
|
thenPart = irImplicitCast(irGet(variable), typeOperand),
|
||||||
elsePart = irNull())
|
elsePart = irNull())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -50,6 +121,7 @@ private class TypeOperatorTransformer(val context: Context, val function: Functi
|
|||||||
|
|
||||||
return when (expression.operator) {
|
return when (expression.operator) {
|
||||||
IrTypeOperator.SAFE_CAST -> lowerSafeCast(expression)
|
IrTypeOperator.SAFE_CAST -> lowerSafeCast(expression)
|
||||||
|
IrTypeOperator.CAST -> lowerCast(expression)
|
||||||
else -> expression
|
else -> expression
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+218
@@ -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)
|
||||||
|
}
|
||||||
@@ -1,19 +1,42 @@
|
|||||||
|
|
||||||
// TODO: utilize substitution mechanism from interop.
|
// TODO: utilize substitution mechanism from interop.
|
||||||
|
|
||||||
|
// macbook
|
||||||
|
arch.osx = x86_64
|
||||||
sysRoot.osx = target-sysroot-1-darwin-macos
|
sysRoot.osx = target-sysroot-1-darwin-macos
|
||||||
llvmHome.osx = clang+llvm-3.8.0-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
|
llvmLtoFlags.osx = -O3 -function-sections -exported-symbol=_main
|
||||||
llvmLlcFlags.osx = -mtriple=x86_64-apple-macosx10.10.0
|
llvmLlcFlags.osx = -mtriple=x86_64-apple-macosx10.10.0
|
||||||
linkerKonanFlags.osx = -lc++
|
linkerKonanFlags.osx = -lc++
|
||||||
linkerOptimizationFlags.osx = -dead_strip
|
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
|
llvmLtoFlags.linux = -O3 -function-sections -exported-symbol=main
|
||||||
llvmLlcFlags.linux = -march=x86-64
|
llvmLlcFlags.linux = -march=x86-64
|
||||||
linkerKonanFlags.linux = -Bstatic -lstdc++ -Bdynamic -ldl -lm -lpthread
|
linkerKonanFlags.linux = -Bstatic -lstdc++ -Bdynamic -ldl -lm -lpthread
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
import groovy.json.JsonOutput
|
||||||
|
import org.jetbrains.kotlin.*
|
||||||
|
|
||||||
configurations {
|
configurations {
|
||||||
cli_bc
|
cli_bc
|
||||||
}
|
}
|
||||||
@@ -6,107 +9,76 @@ dependencies {
|
|||||||
cli_bc project(path: ':backend.native', configuration: 'cli_bc')
|
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 {
|
// backend.native/tests/external
|
||||||
protected String source
|
testOutputExternal {
|
||||||
def backendNative = project.project(":backend.native")
|
output.dir(rootProject.file("$testOutputRoot/external"))
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@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 {
|
task clean {
|
||||||
void compileTest(String source, String exe) {
|
doLast {
|
||||||
runCompiler(source, exe, [])
|
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() {
|
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) {
|
task sum (type:RunKonanTest) {
|
||||||
@@ -127,7 +99,6 @@ task fields1(type: RunKonanTest) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
task fields2(type: RunKonanTest) {
|
task fields2(type: RunKonanTest) {
|
||||||
disabled = true
|
|
||||||
goldValue =
|
goldValue =
|
||||||
"Set global = 1\n" +
|
"Set global = 1\n" +
|
||||||
"Set member = 42\n" +
|
"Set member = 42\n" +
|
||||||
@@ -166,6 +137,11 @@ task safe_cast(type: RunKonanTest) {
|
|||||||
source = "codegen/basics/safe_cast.kt"
|
source = "codegen/basics/safe_cast.kt"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
task typealias1(type: RunKonanTest) {
|
||||||
|
goldValue = "42\n"
|
||||||
|
source = "codegen/basics/typealias1.kt"
|
||||||
|
}
|
||||||
|
|
||||||
task aritmetic(type: RunKonanTest) {
|
task aritmetic(type: RunKonanTest) {
|
||||||
source = "codegen/function/arithmetic.kt"
|
source = "codegen/function/arithmetic.kt"
|
||||||
}
|
}
|
||||||
@@ -202,13 +178,11 @@ task defaults4(type: RunKonanTest) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
task defaults5(type: RunKonanTest) {
|
task defaults5(type: RunKonanTest) {
|
||||||
disabled = true
|
|
||||||
goldValue = "5\n6\n"
|
goldValue = "5\n6\n"
|
||||||
source = "codegen/function/defaults5.kt"
|
source = "codegen/function/defaults5.kt"
|
||||||
}
|
}
|
||||||
|
|
||||||
task defaults6(type: RunKonanTest) {
|
task defaults6(type: RunKonanTest) {
|
||||||
disabled = true
|
|
||||||
goldValue = "42\n"
|
goldValue = "42\n"
|
||||||
source = "codegen/function/defaults6.kt"
|
source = "codegen/function/defaults6.kt"
|
||||||
}
|
}
|
||||||
@@ -231,6 +205,27 @@ task cast_simple(type: RunKonanTest) {
|
|||||||
source = "codegen/basics/cast_simple.kt"
|
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) {
|
task null_check(type: RunKonanTest) {
|
||||||
source = "codegen/basics/null_check.kt"
|
source = "codegen/basics/null_check.kt"
|
||||||
}
|
}
|
||||||
@@ -297,6 +292,76 @@ task empty_substring(type: RunKonanTest) {
|
|||||||
source = "runtime/basic/empty_substring.kt"
|
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) {
|
task array0(type: RunKonanTest) {
|
||||||
goldValue = "5\n6\n7\n8\n9\n10\n11\n12\n13\n"
|
goldValue = "5\n6\n7\n8\n9\n10\n11\n12\n13\n"
|
||||||
source = "runtime/collections/array0.kt"
|
source = "runtime/collections/array0.kt"
|
||||||
@@ -337,6 +402,11 @@ task when8(type: RunKonanTest) {
|
|||||||
goldValue = "true\n"
|
goldValue = "true\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
task when9(type: RunKonanTest) {
|
||||||
|
goldValue = "Ok\n"
|
||||||
|
source = "codegen/branching/when9.kt"
|
||||||
|
}
|
||||||
|
|
||||||
task when_through(type: RunKonanTest) {
|
task when_through(type: RunKonanTest) {
|
||||||
source = "codegen/branching/when_through.kt"
|
source = "codegen/branching/when_through.kt"
|
||||||
}
|
}
|
||||||
@@ -505,6 +575,17 @@ task boxing13(type: RunKonanTest) {
|
|||||||
source = "codegen/boxing/boxing13.kt"
|
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) {
|
task interface0(type: RunKonanTest) {
|
||||||
goldValue = "PASSED\n"
|
goldValue = "PASSED\n"
|
||||||
source = "runtime/basic/interface0.kt"
|
source = "runtime/basic/interface0.kt"
|
||||||
@@ -572,6 +653,11 @@ task catch7(type: RunKonanTest) {
|
|||||||
source = "runtime/exceptions/catch7.kt"
|
source = "runtime/exceptions/catch7.kt"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
task extend_exception(type: RunKonanTest) {
|
||||||
|
goldValue = "OK\n"
|
||||||
|
source = "runtime/exceptions/extend0.kt"
|
||||||
|
}
|
||||||
|
|
||||||
task catch3(type: RunKonanTest) {
|
task catch3(type: RunKonanTest) {
|
||||||
goldValue = "Before\nCaught Throwable\nDone\n"
|
goldValue = "Before\nCaught Throwable\nDone\n"
|
||||||
source = "codegen/try/catch3.kt"
|
source = "codegen/try/catch3.kt"
|
||||||
@@ -814,6 +900,21 @@ task lambda13(type: RunKonanTest) {
|
|||||||
source = "codegen/lambda/lambda13.kt"
|
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) {
|
task initializers2(type: RunKonanTest) {
|
||||||
goldValue = "init globalValue2\n" +
|
goldValue = "init globalValue2\n" +
|
||||||
"init globalValue3\n" +
|
"init globalValue3\n" +
|
||||||
@@ -829,11 +930,15 @@ task initializers3(type: RunKonanTest) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
task initializers4(type: RunKonanTest) {
|
task initializers4(type: RunKonanTest) {
|
||||||
disabled = true
|
goldValue = "1073741824\ntrue\n"
|
||||||
goldValue = "fixme\n"
|
|
||||||
source = "runtime/basic/initializers4.kt"
|
source = "runtime/basic/initializers4.kt"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
task initializers5(type: RunKonanTest) {
|
||||||
|
goldValue = "42\n"
|
||||||
|
source = "runtime/basic/initializers5.kt"
|
||||||
|
}
|
||||||
|
|
||||||
task expression_as_statement(type: RunKonanTest) {
|
task expression_as_statement(type: RunKonanTest) {
|
||||||
goldValue = "Ok\n"
|
goldValue = "Ok\n"
|
||||||
source = "codegen/basics/expression_as_statement.kt"
|
source = "codegen/basics/expression_as_statement.kt"
|
||||||
@@ -905,6 +1010,7 @@ task inline3(type: RunKonanTest) {
|
|||||||
source = "codegen/inline/inline3.kt"
|
source = "codegen/inline/inline3.kt"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
task inline4(type: RunKonanTest) {
|
task inline4(type: RunKonanTest) {
|
||||||
goldValue = "3\n"
|
goldValue = "3\n"
|
||||||
source = "codegen/inline/inline4.kt"
|
source = "codegen/inline/inline4.kt"
|
||||||
@@ -918,4 +1024,8 @@ task inline5(type: RunKonanTest) {
|
|||||||
task inline9(type: RunKonanTest) {
|
task inline9(type: RunKonanTest) {
|
||||||
goldValue = "hello\n6\n"
|
goldValue = "hello\n6\n"
|
||||||
source = "codegen/inline/inline9.kt"
|
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)
|
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)
|
override fun foo(x: Int) = println(x + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun main(args: Array<String>) {
|
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())
|
||||||
+35
@@ -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
|
||||||
|
}
|
||||||
Vendored
+33
@@ -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"
|
||||||
|
}
|
||||||
+35
@@ -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"
|
||||||
|
}
|
||||||
Vendored
+49
@@ -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"
|
||||||
|
}
|
||||||
+40
@@ -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"
|
||||||
|
}
|
||||||
+19
@@ -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
|
||||||
|
}
|
||||||
+36
@@ -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"
|
||||||
|
}
|
||||||
+35
@@ -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"
|
||||||
|
}
|
||||||
+40
@@ -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"
|
||||||
|
}
|
||||||
+28
@@ -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"
|
||||||
|
}
|
||||||
+41
@@ -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
|
||||||
+32
@@ -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
|
||||||
|
}
|
||||||
+14
@@ -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"
|
||||||
+34
@@ -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"
|
||||||
|
}
|
||||||
+48
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+44
@@ -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"
|
||||||
|
}
|
||||||
Vendored
+24
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+32
@@ -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
|
||||||
+19
@@ -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
|
||||||
Vendored
+19
@@ -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"
|
||||||
|
}
|
||||||
+53
@@ -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()}"} ()
|
||||||
|
}
|
||||||
+33
@@ -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()}"
|
||||||
|
}
|
||||||
+40
@@ -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
Reference in New Issue
Block a user