Merge
This commit is contained in:
@@ -21,6 +21,7 @@ kotstd/kotstd.iml
|
||||
*.kt.S
|
||||
*.kt.exe
|
||||
*.log
|
||||
test.output
|
||||
|
||||
# Ignore Gradle GUI config
|
||||
gradle-app.setting
|
||||
|
||||
+40
-29
@@ -143,7 +143,7 @@ kotlinNativeInterop {
|
||||
compilerOpts '-fPIC'
|
||||
linkerOpts '-fPIC'
|
||||
linker 'clang++'
|
||||
linkOutputs ':common:compileHash'
|
||||
linkOutputs ':common:hostHash'
|
||||
|
||||
headers fileTree('../common/src/hash/headers') {
|
||||
include '**/*.h'
|
||||
@@ -182,47 +182,58 @@ dependencies {
|
||||
|
||||
build.dependsOn 'compilerClasses','cli_bcClasses','bc_frontendClasses'
|
||||
|
||||
|
||||
// These are just a couple of aliases
|
||||
task stdlib(dependsOn: 'hostStdlib')
|
||||
task start(dependsOn: 'hostStart')
|
||||
|
||||
// These files are built before the 'dist' is complete,
|
||||
// so we provide custom values for
|
||||
// -runtime, -properties, -library and -Djava.library.path
|
||||
|
||||
task stdlib(type: JavaExec) {
|
||||
main = 'org.jetbrains.kotlin.cli.bc.K2NativeKt'
|
||||
classpath = project.configurations.cli_bc
|
||||
jvmArgs "-ea",
|
||||
"-Dkonan.home=${project.parent.file('dist')}",
|
||||
"-Djava.library.path=${project.buildDir}/nativelibs"
|
||||
args('-output', project(':runtime').file('build/stdlib.kt.bc'),
|
||||
'-nolink', '-nostdlib',
|
||||
'-runtime', project(':runtime').file('build/runtime.bc'),
|
||||
'-properties', project(':backend.native').file('konan.properties'),
|
||||
targetList.each { target ->
|
||||
task("${target}Stdlib", type: JavaExec) {
|
||||
main = 'org.jetbrains.kotlin.cli.bc.K2NativeKt'
|
||||
classpath = project.configurations.cli_bc
|
||||
jvmArgs "-ea",
|
||||
"-Dkonan.home=${project.parent.file('dist')}",
|
||||
"-Djava.library.path=${project.buildDir}/nativelibs"
|
||||
args('-output', project(':runtime').file("build/${target}/stdlib.kt.bc"),
|
||||
'-nolink', '-nostdlib',
|
||||
'-target', target,
|
||||
'-runtime', project(':runtime').file("build/${target}/runtime.bc"),
|
||||
'-properties', project(':backend.native').file('konan.properties'),
|
||||
project(':runtime').file('src/main/kotlin'),
|
||||
*project.globalArgs)
|
||||
|
||||
inputs.dir(project(':runtime').file('src/main/kotlin'))
|
||||
outputs.file(project(':runtime').file('build/stdlib.kt.bc'))
|
||||
inputs.dir(project(':runtime').file('src/main/kotlin'))
|
||||
outputs.file(project(':runtime').file("build/${target}/stdlib.kt.bc"))
|
||||
|
||||
dependsOn ':runtime:build'
|
||||
dependsOn ":runtime:${target}Runtime"
|
||||
}
|
||||
}
|
||||
|
||||
task start(type: JavaExec) {
|
||||
main = 'org.jetbrains.kotlin.cli.bc.K2NativeKt'
|
||||
classpath = project.configurations.cli_bc
|
||||
jvmArgs "-ea",
|
||||
"-Dkonan.home=${project.parent.file('dist')}",
|
||||
"-Djava.library.path=${project.buildDir}/nativelibs"
|
||||
args('-output', project(':runtime').file('build/start.kt.bc'),
|
||||
'-nolink', '-nostdlib',
|
||||
'-library', project(':runtime').file('build/stdlib.kt.bc'),
|
||||
'-runtime', project(':runtime').file('build/runtime.bc'),
|
||||
'-properties', project(':backend.native').file('konan.properties'),
|
||||
targetList.each { target ->
|
||||
task("${target}Start", type: JavaExec) {
|
||||
main = 'org.jetbrains.kotlin.cli.bc.K2NativeKt'
|
||||
classpath = project.configurations.cli_bc
|
||||
jvmArgs "-ea",
|
||||
"-Dkonan.home=${project.parent.file('dist')}",
|
||||
"-Djava.library.path=${project.buildDir}/nativelibs"
|
||||
args('-output', project(':runtime').file("build/${target}/start.kt.bc"),
|
||||
'-nolink', '-nostdlib',
|
||||
'-target', target,
|
||||
'-library', project(':runtime').file("build/${target}/stdlib.kt.bc"),
|
||||
'-runtime', project(':runtime').file("build/${target}/runtime.bc"),
|
||||
'-properties', project(':backend.native').file('konan.properties'),
|
||||
project(':runtime').file('src/launcher/kotlin'),
|
||||
*project.globalArgs)
|
||||
|
||||
inputs.dir(project(':runtime').file('src/launcher/kotlin'))
|
||||
outputs.file(project(':runtime').file('build/start.kt.bc'))
|
||||
inputs.dir(project(':runtime').file('src/launcher/kotlin'))
|
||||
outputs.file(project(':runtime').file("build/${target}/start.kt.bc"))
|
||||
|
||||
dependsOn ':runtime:build', 'stdlib'
|
||||
dependsOn ":runtime:${target}Runtime", "${target}Stdlib"
|
||||
}
|
||||
}
|
||||
|
||||
task run {
|
||||
@@ -237,7 +248,7 @@ task jars(type: Jar) {
|
||||
'build/classes/hashInteropStubs',
|
||||
'build/classes/llvmInteropStubs'
|
||||
|
||||
dependsOn 'build', ':runtime:build', 'external_jars'
|
||||
dependsOn 'build', ':runtime:hostRuntime', 'external_jars'
|
||||
}
|
||||
|
||||
task external_jars(type: Copy) {
|
||||
|
||||
@@ -77,11 +77,13 @@ class K2Native : CLICompiler<K2NativeCompilerArguments>() {
|
||||
|
||||
put(EXECUTABLE_FILE,
|
||||
arguments.outputFile ?: "program.kexe")
|
||||
put(RUNTIME_FILE,
|
||||
arguments.runtimeFile ?: Distribution.runtime)
|
||||
put(PROPERTY_FILE,
|
||||
arguments.propertyFile ?: Distribution.propertyFile)
|
||||
|
||||
if (arguments.runtimeFile != null)
|
||||
put(RUNTIME_FILE, arguments.runtimeFile)
|
||||
if (arguments.propertyFile != null)
|
||||
put(PROPERTY_FILE, arguments.propertyFile)
|
||||
if (arguments.target != null)
|
||||
put(TARGET, arguments.target)
|
||||
put(LIST_TARGETS, arguments.listTargets)
|
||||
put(OPTIMIZATION, arguments.optimization)
|
||||
|
||||
put(PRINT_IR, arguments.printIr)
|
||||
|
||||
@@ -30,6 +30,13 @@ public class K2NativeCompilerArguments extends CommonCompilerArguments {
|
||||
@Argument(value = "opt", description = "Enable optimizations during compilation")
|
||||
public boolean optimization;
|
||||
|
||||
@Argument(value = "target", description = "Set hardware target")
|
||||
@ValueDescription("<target>")
|
||||
public String target;
|
||||
|
||||
@Argument(value = "list_targets", description = "List available hardware targets")
|
||||
public boolean listTargets;
|
||||
|
||||
@Argument(value = "print_ir", description = "Print IR")
|
||||
public boolean printIr;
|
||||
|
||||
|
||||
+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 =
|
||||
this.useAsValue(parameter)
|
||||
|
||||
protected open fun IrExpression.useAsDispatchReceiver(function: CallableDescriptor): IrExpression =
|
||||
this.useAsArgument(function.dispatchReceiverParameter!!)
|
||||
|
||||
protected open fun IrExpression.useAsExtensionReceiver(function: CallableDescriptor): IrExpression =
|
||||
this.useAsArgument(function.extensionReceiverParameter!!)
|
||||
|
||||
protected open fun IrExpression.useAsValueArgument(parameter: ValueParameterDescriptor): IrExpression =
|
||||
this.useAsArgument(parameter)
|
||||
|
||||
protected open fun IrExpression.useForVariable(variable: VariableDescriptor): IrExpression =
|
||||
this.useAsValue(variable)
|
||||
|
||||
@@ -55,12 +64,12 @@ abstract class AbstractValueUsageTransformer(val builtIns: KotlinBuiltIns): IrEl
|
||||
expression.transformChildrenVoid(this)
|
||||
|
||||
with(expression) {
|
||||
dispatchReceiver = dispatchReceiver?.useAsArgument(descriptor.dispatchReceiverParameter!!)
|
||||
extensionReceiver = extensionReceiver?.useAsArgument(descriptor.extensionReceiverParameter!!)
|
||||
dispatchReceiver = dispatchReceiver?.useAsDispatchReceiver(descriptor)
|
||||
extensionReceiver = extensionReceiver?.useAsExtensionReceiver(descriptor)
|
||||
for (index in descriptor.valueParameters.indices) {
|
||||
val argument = getValueArgument(index) ?: continue
|
||||
val parameter = descriptor.valueParameters[index]
|
||||
putValueArgument(index, argument.useAsArgument(parameter))
|
||||
putValueArgument(index, argument.useAsValueArgument(parameter))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,7 +223,7 @@ abstract class AbstractValueUsageTransformer(val builtIns: KotlinBuiltIns): IrEl
|
||||
declaration.descriptor.valueParameters.forEach { parameter ->
|
||||
val defaultValue = declaration.getDefault(parameter)
|
||||
if (defaultValue is IrExpressionBody) {
|
||||
defaultValue.expression = defaultValue.expression.useAsArgument(parameter)
|
||||
defaultValue.expression = defaultValue.expression.useAsValueArgument(parameter)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+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
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.BackendContext
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.incremental.components.LookupLocation
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.NonReportingOverrideStrategy
|
||||
import org.jetbrains.kotlin.resolve.OverridingUtil
|
||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScopeImpl
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.utils.Printer
|
||||
|
||||
class IrLoweringContext(backendContext: BackendContext) : IrGeneratorContext(backendContext.irBuiltIns)
|
||||
|
||||
class FunctionIrGenerator(backendContext: BackendContext, functionDescriptor: FunctionDescriptor) : IrGeneratorWithScope {
|
||||
override val context = IrLoweringContext(backendContext)
|
||||
override val scope = Scope(functionDescriptor)
|
||||
}
|
||||
class FunctionIrBuilder(backendContext: BackendContext, functionDescriptor: FunctionDescriptor) :
|
||||
IrBuilderWithScope(
|
||||
IrLoweringContext(backendContext),
|
||||
Scope(functionDescriptor),
|
||||
UNDEFINED_OFFSET,
|
||||
UNDEFINED_OFFSET
|
||||
)
|
||||
|
||||
fun BackendContext.createFunctionIrGenerator(functionDescriptor: FunctionDescriptor) =
|
||||
FunctionIrGenerator(this, functionDescriptor)
|
||||
fun BackendContext.createFunctionIrBuilder(functionDescriptor: FunctionDescriptor) =
|
||||
FunctionIrBuilder(this, functionDescriptor)
|
||||
|
||||
class FunctionIrBuilder(context: IrLoweringContext, scope: Scope, startOffset: Int, endOffset: Int) :
|
||||
IrBuilderWithScope(context, scope, startOffset, endOffset)
|
||||
|
||||
fun FunctionIrGenerator.createIrBuilder() = FunctionIrBuilder(context, scope, UNDEFINED_OFFSET, UNDEFINED_OFFSET)
|
||||
fun <T : IrBuilder> T.at(element: IrElement) = this.at(element.startOffset, element.endOffset)
|
||||
|
||||
/**
|
||||
* Builds [IrBlock] to be used instead of given expression.
|
||||
*/
|
||||
inline fun FunctionIrGenerator.irBlock(expression: IrExpression, origin: IrStatementOrigin? = null,
|
||||
resultType: KotlinType? = expression.type,
|
||||
body: IrBlockBuilder.() -> Unit) =
|
||||
inline fun IrGeneratorWithScope.irBlock(expression: IrExpression, origin: IrStatementOrigin? = null,
|
||||
resultType: KotlinType? = expression.type,
|
||||
body: IrBlockBuilder.() -> Unit) =
|
||||
this.irBlock(expression.startOffset, expression.endOffset, origin, resultType, body)
|
||||
|
||||
inline fun FunctionIrGenerator.irBlockBody(irElement: IrElement, body: IrBlockBodyBuilder.() -> Unit) =
|
||||
this.irBlockBody(irElement.startOffset, irElement.endOffset, body)
|
||||
inline fun IrGeneratorWithScope.irBlockBody(irElement: IrElement, body: IrBlockBodyBuilder.() -> Unit) =
|
||||
this.irBlockBody(irElement.startOffset, irElement.endOffset, body)
|
||||
|
||||
fun computeOverrides(current: ClassDescriptor, functionsFromCurrent: List<CallableMemberDescriptor>): List<DeclarationDescriptor> {
|
||||
|
||||
val result = mutableListOf<DeclarationDescriptor>()
|
||||
|
||||
val allSuperDescriptors = current.typeConstructor.supertypes
|
||||
.flatMap { it.memberScope.getContributedDescriptors() }
|
||||
.filterIsInstance<CallableMemberDescriptor>()
|
||||
|
||||
for ((name, group) in allSuperDescriptors.groupBy { it.name }) {
|
||||
OverridingUtil.generateOverridesInFunctionGroup(
|
||||
name,
|
||||
/* membersFromSupertypes = */ group,
|
||||
/* membersFromCurrent = */ functionsFromCurrent.filter { it.name == name },
|
||||
current,
|
||||
object : NonReportingOverrideStrategy() {
|
||||
override fun addFakeOverride(fakeOverride: CallableMemberDescriptor) {
|
||||
result.add(fakeOverride)
|
||||
}
|
||||
|
||||
override fun conflict(fromSuper: CallableMemberDescriptor, fromCurrent: CallableMemberDescriptor) {
|
||||
error("Conflict in scope of $current: $fromSuper vs $fromCurrent")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
class SimpleMemberScope(val members: List<DeclarationDescriptor>) : MemberScopeImpl() {
|
||||
|
||||
override fun getContributedClassifier(name: Name, location: LookupLocation): ClassifierDescriptor? = TODO()
|
||||
|
||||
override fun getContributedVariables(name: Name, location: LookupLocation): Collection<PropertyDescriptor> = TODO()
|
||||
|
||||
override fun getContributedFunctions(name: Name, location: LookupLocation): Collection<SimpleFunctionDescriptor> = TODO()
|
||||
|
||||
override fun getContributedDescriptors(kindFilter: DescriptorKindFilter,
|
||||
nameFilter: (Name) -> Boolean): Collection<DeclarationDescriptor> =
|
||||
members.filter { kindFilter.accepts(it) && nameFilter(it.name) }
|
||||
|
||||
override fun printScopeStructure(p: Printer) = TODO("not implemented")
|
||||
|
||||
}
|
||||
+34
-14
@@ -1,28 +1,47 @@
|
||||
package org.jetbrains.kotlin.backend.konan
|
||||
|
||||
import llvm.*
|
||||
import org.jetbrains.kotlin.backend.konan.ir.Ir
|
||||
import org.jetbrains.kotlin.backend.konan.ir.ModuleIndex
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.Runtime
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.getFunctionType
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.StaticData
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.Llvm
|
||||
import org.jetbrains.kotlin.backend.konan.KonanBackendContext
|
||||
import org.jetbrains.kotlin.backend.konan.KonanPhase
|
||||
import org.jetbrains.kotlin.backend.konan.KonanConfig
|
||||
import org.jetbrains.kotlin.backend.konan.KonanConfigKeys
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import llvm.LLVMDumpModule
|
||||
import llvm.LLVMModuleRef
|
||||
import org.jetbrains.kotlin.backend.jvm.descriptors.initialize
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.deepPrint
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName
|
||||
import org.jetbrains.kotlin.backend.konan.ir.Ir
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.Llvm
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.LlvmDeclarations
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.verifyModule
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl
|
||||
import org.jetbrains.kotlin.descriptors.impl.ReceiverParameterDescriptorImpl
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
import org.jetbrains.kotlin.ir.util.DumpIrTreeVisitor
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver
|
||||
import java.lang.System.out
|
||||
|
||||
internal class SpecialDescriptorsFactory {
|
||||
private val outerThisDescriptors = mutableMapOf<ClassDescriptor, PropertyDescriptor>()
|
||||
|
||||
fun getOuterThisFieldDescriptor(innerClassDescriptor: ClassDescriptor): PropertyDescriptor =
|
||||
if (!innerClassDescriptor.isInner) throw AssertionError("Class is not inner: $innerClassDescriptor")
|
||||
else outerThisDescriptors.getOrPut(innerClassDescriptor) {
|
||||
val outerClassDescriptor = DescriptorUtils.getContainingClass(innerClassDescriptor) ?:
|
||||
throw AssertionError("No containing class for inner class $innerClassDescriptor")
|
||||
|
||||
val receiver = ReceiverParameterDescriptorImpl(innerClassDescriptor, ImplicitClassReceiver(innerClassDescriptor))
|
||||
PropertyDescriptorImpl.create(innerClassDescriptor, Annotations.EMPTY, Modality.FINAL, Visibilities.PRIVATE,
|
||||
false, "this$0".synthesizedName, CallableMemberDescriptor.Kind.SYNTHESIZED, SourceElement.NO_SOURCE,
|
||||
false, false, false, false, false, false).initialize(outerClassDescriptor.defaultType, dispatchReceiverParameter = receiver)
|
||||
}
|
||||
}
|
||||
|
||||
internal final class Context(val config: KonanConfig) : KonanBackendContext() {
|
||||
|
||||
var moduleDescriptor: ModuleDescriptor? = null
|
||||
|
||||
val specialDescriptorsFactory = SpecialDescriptorsFactory()
|
||||
|
||||
// TODO: make lateinit?
|
||||
var irModule: IrModuleFragment? = null
|
||||
set(module: IrModuleFragment?) {
|
||||
if (field != null) {
|
||||
@@ -49,6 +68,7 @@ internal final class Context(val config: KonanConfig) : KonanBackendContext() {
|
||||
}
|
||||
|
||||
lateinit var llvm: Llvm
|
||||
lateinit var llvmDeclarations: LlvmDeclarations
|
||||
|
||||
var phase: KonanPhase? = null
|
||||
var depth: Int = 0
|
||||
|
||||
+34
-28
@@ -1,36 +1,42 @@
|
||||
package org.jetbrains.kotlin.backend.konan
|
||||
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import java.io.File
|
||||
import java.util.Properties
|
||||
|
||||
public class Distribution(val properties: KonanProperties,
|
||||
val os: String, val target: String) {
|
||||
public class Distribution(val config: CompilerConfiguration) {
|
||||
|
||||
companion object {
|
||||
val targetManager = TargetManager(config)
|
||||
val target =
|
||||
if (!targetManager.crossCompile) "host"
|
||||
else targetManager.current.name.toLowerCase()
|
||||
val suffix = targetManager.currentSuffix()
|
||||
|
||||
private fun findKonanHome(): String {
|
||||
val value = System.getProperty("konan.home", "dist")
|
||||
val path = File(value).absolutePath
|
||||
return path
|
||||
}
|
||||
|
||||
val konanHome = findKonanHome()
|
||||
val propertyFile = "$konanHome/konan/konan.properties"
|
||||
|
||||
val lib = "$konanHome/lib"
|
||||
|
||||
// TODO: Pack all needed things to dist.
|
||||
val dependencies = "$konanHome/../dependencies/all"
|
||||
|
||||
val start = "$lib/start.kt.bc"
|
||||
val stdlib = "$lib/stdlib.kt.bc"
|
||||
val runtime = "$lib/runtime.bc"
|
||||
val launcher = "$lib/launcher.bc"
|
||||
private fun findKonanHome(): String {
|
||||
val value = System.getProperty("konan.home", "dist")
|
||||
val path = File(value).absolutePath
|
||||
return path
|
||||
}
|
||||
|
||||
val llvmHome = "$dependencies/${properties.propertyString("llvmHome.$os")}"
|
||||
val sysRoot = "$dependencies/${properties.propertyString("sysRoot.$os")}"
|
||||
val libGcc = "$dependencies/${properties.propertyString("libGcc.$os")}"
|
||||
val konanHome = findKonanHome()
|
||||
val propertyFile = config.get(KonanConfigKeys.PROPERTY_FILE)
|
||||
?: "$konanHome/konan/konan.properties"
|
||||
val properties = KonanProperties(propertyFile)
|
||||
|
||||
val lib = "$konanHome/lib/$target"
|
||||
|
||||
// TODO: Pack all needed things to dist.
|
||||
val dependencies = "$konanHome/../dependencies/all"
|
||||
|
||||
val stdlib = "$lib/stdlib.kt.bc"
|
||||
val start = "$lib/start.kt.bc"
|
||||
val launcher = "$lib/launcher.bc"
|
||||
val runtime = config.get(KonanConfigKeys.RUNTIME_FILE)
|
||||
?: "$lib/runtime.bc"
|
||||
|
||||
val llvmHome = "$dependencies/${properties.propertyString("llvmHome.$suffix")}"
|
||||
val sysRoot = "$dependencies/${properties.propertyString("sysRoot.$suffix")}"
|
||||
val libGcc = "$dependencies/${properties.propertyString("libGcc.$suffix")}"
|
||||
|
||||
val llvmBin = "$llvmHome/bin"
|
||||
val llvmLib = "$llvmHome/lib"
|
||||
@@ -40,9 +46,9 @@ public class Distribution(val properties: KonanProperties,
|
||||
val llvmLto = "$llvmBin/llvm-lto"
|
||||
val llvmLink = "$llvmBin/llvm-link"
|
||||
val libCppAbi = "$llvmLib/libc++abi.a"
|
||||
val libLTO = when (os) {
|
||||
"osx" -> "$llvmLib/libLTO.dylib"
|
||||
"linux" -> "$llvmLib/libLTO.so"
|
||||
else -> error("Don't know libLTO location for the platform.")
|
||||
val libLTO = when (TargetManager.host) {
|
||||
KonanTarget.MACBOOK -> "$llvmLib/libLTO.dylib"
|
||||
KonanTarget.LINUX -> "$llvmLib/libLTO.so"
|
||||
else -> error("Don't know libLTO location for this platform.")
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -16,13 +16,15 @@ import java.io.File
|
||||
|
||||
class KonanConfig(val project: Project, val configuration: CompilerConfiguration) {
|
||||
|
||||
internal val distribution = Distribution(configuration)
|
||||
|
||||
internal val libraries: List<String>
|
||||
get() {
|
||||
val fromCommandLine = configuration.getList(KonanConfigKeys.LIBRARY_FILES)
|
||||
if (configuration.get(KonanConfigKeys.NOSTDLIB) ?: false) {
|
||||
return fromCommandLine
|
||||
}
|
||||
return fromCommandLine + Distribution.stdlib
|
||||
return fromCommandLine + distribution.stdlib
|
||||
}
|
||||
|
||||
private val loadedDescriptors = loadLibMetadata(libraries)
|
||||
|
||||
+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.serialization.js.ModuleKind;
|
||||
import org.jetbrains.kotlin.config.CompilerConfigurationKey
|
||||
import org.jetbrains.kotlin.serialization.js.ModuleKind
|
||||
|
||||
class KonanConfigKeys {
|
||||
companion object {
|
||||
val LIBRARY_FILES: CompilerConfigurationKey<List<String>>
|
||||
= CompilerConfigurationKey.create("library file paths");
|
||||
= CompilerConfigurationKey.create("library file paths")
|
||||
val BITCODE_FILE: CompilerConfigurationKey<String>
|
||||
= CompilerConfigurationKey.create("emitted bitcode file path");
|
||||
= CompilerConfigurationKey.create("emitted bitcode file path")
|
||||
val EXECUTABLE_FILE: CompilerConfigurationKey<String>
|
||||
= CompilerConfigurationKey.create("final executable file path");
|
||||
= CompilerConfigurationKey.create("final executable file path")
|
||||
val RUNTIME_FILE: CompilerConfigurationKey<String?>
|
||||
= CompilerConfigurationKey.create("override default runtime file path");
|
||||
= CompilerConfigurationKey.create("override default runtime file path")
|
||||
val PROPERTY_FILE: CompilerConfigurationKey<String?>
|
||||
= CompilerConfigurationKey.create("override default property file path");
|
||||
= CompilerConfigurationKey.create("override default property file path")
|
||||
val ABI_VERSION: CompilerConfigurationKey<Int>
|
||||
= CompilerConfigurationKey.create("current abi version");
|
||||
= CompilerConfigurationKey.create("current abi version")
|
||||
val OPTIMIZATION: CompilerConfigurationKey<Boolean>
|
||||
= CompilerConfigurationKey.create("optimized compilation");
|
||||
= CompilerConfigurationKey.create("optimized compilation")
|
||||
val NOSTDLIB: CompilerConfigurationKey<Boolean>
|
||||
= CompilerConfigurationKey.create("don't link with stdlib");
|
||||
= CompilerConfigurationKey.create("don't link with stdlib")
|
||||
val NOLINK: CompilerConfigurationKey<Boolean>
|
||||
= CompilerConfigurationKey.create("don't link, only produce a bitcode file ");
|
||||
= CompilerConfigurationKey.create("don't link, only produce a bitcode file ")
|
||||
val TARGET: CompilerConfigurationKey<String?>
|
||||
= CompilerConfigurationKey.create("target we compile for")
|
||||
val LIST_TARGETS: CompilerConfigurationKey<Boolean>
|
||||
= CompilerConfigurationKey.create("list available targets")
|
||||
|
||||
val SOURCE_MAP: CompilerConfigurationKey<List<String>>
|
||||
= CompilerConfigurationKey.create("generate source map");
|
||||
= CompilerConfigurationKey.create("generate source map")
|
||||
val META_INFO: CompilerConfigurationKey<List<String>>
|
||||
= CompilerConfigurationKey.create("generate metadata");
|
||||
= CompilerConfigurationKey.create("generate metadata")
|
||||
val MODULE_KIND: CompilerConfigurationKey<ModuleKind>
|
||||
= CompilerConfigurationKey.create("module kind");
|
||||
= CompilerConfigurationKey.create("module kind")
|
||||
|
||||
val VERIFY_IR: CompilerConfigurationKey<Boolean>
|
||||
= CompilerConfigurationKey.create("verify ir");
|
||||
= CompilerConfigurationKey.create("verify ir")
|
||||
val VERIFY_DESCRIPTORS: CompilerConfigurationKey<Boolean>
|
||||
= CompilerConfigurationKey.create("verify descriptors");
|
||||
= CompilerConfigurationKey.create("verify descriptors")
|
||||
val VERIFY_BITCODE: CompilerConfigurationKey<Boolean>
|
||||
= CompilerConfigurationKey.create("verify bitcode");
|
||||
= CompilerConfigurationKey.create("verify bitcode")
|
||||
|
||||
val PRINT_IR: CompilerConfigurationKey<Boolean>
|
||||
= CompilerConfigurationKey.create("print ir");
|
||||
= CompilerConfigurationKey.create("print ir")
|
||||
val PRINT_DESCRIPTORS: CompilerConfigurationKey<Boolean>
|
||||
= CompilerConfigurationKey.create("print descriptors");
|
||||
= CompilerConfigurationKey.create("print descriptors")
|
||||
val PRINT_BITCODE: CompilerConfigurationKey<Boolean>
|
||||
= CompilerConfigurationKey.create("print bitcode");
|
||||
= CompilerConfigurationKey.create("print bitcode")
|
||||
|
||||
val ENABLED_PHASES: CompilerConfigurationKey<List<String>>
|
||||
= CompilerConfigurationKey.create("enable backend phases");
|
||||
= CompilerConfigurationKey.create("enable backend phases")
|
||||
val DISABLED_PHASES: CompilerConfigurationKey<List<String>>
|
||||
= CompilerConfigurationKey.create("disable backend phases");
|
||||
= CompilerConfigurationKey.create("disable backend phases")
|
||||
val VERBOSE_PHASES: CompilerConfigurationKey<List<String>>
|
||||
= CompilerConfigurationKey.create("verbose backend phases");
|
||||
= CompilerConfigurationKey.create("verbose backend phases")
|
||||
val LIST_PHASES: CompilerConfigurationKey<Boolean>
|
||||
= CompilerConfigurationKey.create("list backend phases");
|
||||
= CompilerConfigurationKey.create("list backend phases")
|
||||
val TIME_PHASES: CompilerConfigurationKey<Boolean>
|
||||
= CompilerConfigurationKey.create("time backend phases");
|
||||
= CompilerConfigurationKey.create("time backend phases")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+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.util.profile
|
||||
import org.jetbrains.kotlin.backend.konan.Context
|
||||
import org.jetbrains.kotlin.backend.konan.ir.ModuleIndex
|
||||
import org.jetbrains.kotlin.cli.common.CLICompiler
|
||||
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.config.kotlinSourceRoots
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.psi2ir.Psi2IrConfiguration
|
||||
@@ -34,11 +36,18 @@ public fun runTopLevelPhases(konanConfig: KonanConfig, environment: KotlinCoreEn
|
||||
|
||||
val config = konanConfig.configuration
|
||||
|
||||
val targets = TargetManager(config)
|
||||
if (config.get(KonanConfigKeys.LIST_TARGETS) ?: false) {
|
||||
targets.list()
|
||||
}
|
||||
|
||||
KonanPhases.config(konanConfig)
|
||||
if (config.get(KonanConfigKeys.LIST_PHASES) ?: false) {
|
||||
KonanPhases.list()
|
||||
}
|
||||
|
||||
if (config.kotlinSourceRoots.isEmpty()) return
|
||||
|
||||
val collector = config.getNotNull(
|
||||
CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
|
||||
|
||||
@@ -67,6 +76,7 @@ public fun runTopLevelPhases(konanConfig: KonanConfig, environment: KotlinCoreEn
|
||||
phaser.phase(KonanPhase.BACKEND) {
|
||||
phaser.phase(KonanPhase.LOWER) {
|
||||
KonanLower(context).lower()
|
||||
context.ir.moduleIndexForCodegen = ModuleIndex(context.ir.irModule)
|
||||
}
|
||||
phaser.phase(KonanPhase.BITCODE) {
|
||||
emitLLVM(context)
|
||||
|
||||
+10
-1
@@ -20,6 +20,15 @@ internal class KonanLower(val context: Context) {
|
||||
phaser.phase(KonanPhase.LOWER_INLINE) {
|
||||
FunctionInlining(context).inline(irFile)
|
||||
}
|
||||
phaser.phase(KonanPhase.LOWER_ENUMS) {
|
||||
EnumClassLowering(context).run(irFile)
|
||||
}
|
||||
phaser.phase(KonanPhase.LOWER_INNER_CLASSES) {
|
||||
InnerClassLowering(context).runOnFilePostfix(irFile)
|
||||
}
|
||||
phaser.phase(KonanPhase.LOWER_VARARG) {
|
||||
VarargInjectionLowering(context).runOnFilePostfix(irFile)
|
||||
}
|
||||
phaser.phase(KonanPhase.LOWER_DEFAULT_PARAMETER_EXTENT) {
|
||||
DefaultParameterStubGenerator(context).runOnFilePostfix(irFile)
|
||||
DefaultParameterInjector(context).runOnFilePostfix(irFile)
|
||||
@@ -34,7 +43,7 @@ internal class KonanLower(val context: Context) {
|
||||
SharedVariablesLowering(context).runOnFilePostfix(irFile)
|
||||
}
|
||||
phaser.phase(KonanPhase.LOWER_LOCAL_FUNCTIONS) {
|
||||
LocalFunctionsLowering(context).runOnFilePostfix(irFile)
|
||||
LocalDeclarationsLowering(context).runOnFilePostfix(irFile)
|
||||
}
|
||||
phaser.phase(KonanPhase.LOWER_CALLABLES) {
|
||||
CallableReferenceLowering(context).runOnFilePostfix(irFile)
|
||||
|
||||
+3
@@ -11,6 +11,7 @@ enum class KonanPhase(val description: String,
|
||||
/* */ BACKEND("All backend"),
|
||||
/* ... */ LOWER("IR Lowering"),
|
||||
/* ... ... */ LOWER_BUILTIN_OPERATORS("BuiltIn Operators Lowering"),
|
||||
/* ... ... */ LOWER_VARARG("Vararg lowering"),
|
||||
/* ... ... */ LOWER_DEFAULT_PARAMETER_EXTENT("Default Parameter Extent Lowering"),
|
||||
/* ... ... */ LOWER_TYPE_OPERATORS("Type operators lowering"),
|
||||
/* ... ... */ LOWER_SHARED_VARIABLES("Shared Variable Lowering"),
|
||||
@@ -18,6 +19,8 @@ enum class KonanPhase(val description: String,
|
||||
/* ... ... */ LOWER_CALLABLES("Callable references Lowering"),
|
||||
/* ... ... */ LOWER_INLINE("Functions inlining"),
|
||||
/* ... ... */ AUTOBOX("Autoboxing of primitive types"),
|
||||
/* ... ... */ LOWER_ENUMS("Enum classes lowering"),
|
||||
/* ... ... */ LOWER_INNER_CLASSES("Inner classes lowering"),
|
||||
/* ... */ BITCODE("LLVM BitCode Generation"),
|
||||
/* ... ... */ RTTI("RTTI Generation"),
|
||||
/* ... ... */ CODEGEN("Code Generation"),
|
||||
|
||||
+2
-3
@@ -1,15 +1,14 @@
|
||||
package org.jetbrains.kotlin.backend.konan
|
||||
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import java.io.File
|
||||
import java.util.Properties
|
||||
|
||||
public class KonanProperties(config: CompilerConfiguration) {
|
||||
public class KonanProperties(val propertyFile: String) {
|
||||
|
||||
val properties = Properties()
|
||||
|
||||
init {
|
||||
val file = File(config.get(KonanConfigKeys.PROPERTY_FILE))
|
||||
val file = File(propertyFile)
|
||||
file.bufferedReader()?.use { reader ->
|
||||
properties.load(reader)
|
||||
}
|
||||
|
||||
+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
|
||||
// for another implementation of this class.
|
||||
internal abstract class PlatformFlags(val distrib: Distribution,
|
||||
val properties: KonanProperties) {
|
||||
internal abstract class PlatformFlags(val distribution: Distribution) {
|
||||
val properties = distribution.properties
|
||||
|
||||
abstract val llvmLtoFlags: List<String>
|
||||
abstract val llvmLlcFlags: List<String>
|
||||
@@ -25,8 +25,8 @@ internal abstract class PlatformFlags(val distrib: Distribution,
|
||||
}
|
||||
|
||||
|
||||
internal class MacOSPlatform(distrib: Distribution,
|
||||
properties: KonanProperties) : PlatformFlags(distrib, properties) {
|
||||
internal open class MacOSPlatform(distribution: Distribution)
|
||||
: PlatformFlags(distribution) {
|
||||
|
||||
override val llvmLtoFlags = properties.propertyList("llvmLtoFlags.osx")
|
||||
override val llvmLlcFlags = properties.propertyList("llvmLlcFlags.osx")
|
||||
@@ -34,28 +34,49 @@ internal class MacOSPlatform(distrib: Distribution,
|
||||
override val linkerOptimizationFlags =
|
||||
properties.propertyList("linkerOptimizationFlags.osx")
|
||||
override val linkerKonanFlags = properties.propertyList("linkerKonanFlags.osx")
|
||||
override val linker = "${distrib.sysRoot}/usr/bin/ld"
|
||||
override val linker = "${distribution.sysRoot}/usr/bin/ld"
|
||||
|
||||
open val arch = properties.propertyString("arch.osx")!!
|
||||
open val osVersionMin = properties.propertyList("osVersionMin.osx")
|
||||
|
||||
open val sysRoot = distribution.sysRoot
|
||||
open val targetSysRoot = sysRoot
|
||||
open val llvmLib = distribution.llvmLib
|
||||
|
||||
override fun linkCommand(objectFiles: List<String>, executable: String, optimize: Boolean): List<String> {
|
||||
|
||||
val sysRoot = distrib.sysRoot
|
||||
val llvmLib = distrib.llvmLib
|
||||
|
||||
return mutableListOf<String>(linker, "-demangle") +
|
||||
if (optimize) listOf("-object_path_lto", "temporary.o", "-lto_library", distrib.libLTO) else {listOf<String>()} +
|
||||
listOf( "-dynamic", "-arch", "x86_64", "-macosx_version_min") +
|
||||
properties.propertyList("macosVersionMin.osx") +
|
||||
listOf("-syslibroot", "$sysRoot",
|
||||
if (optimize) listOf("-object_path_lto", "temporary.o", "-lto_library", distribution.libLTO) else {listOf<String>()} +
|
||||
listOf( "-dynamic", "-arch", arch) +
|
||||
osVersionMin +
|
||||
listOf("-syslibroot", "$targetSysRoot",
|
||||
"-o", executable) +
|
||||
objectFiles +
|
||||
if (optimize) linkerOptimizationFlags else {listOf<String>()} +
|
||||
linkerKonanFlags +
|
||||
listOf("-lSystem", "$llvmLib/clang/3.8.0/lib/darwin/libclang_rt.osx.a")
|
||||
listOf("-lSystem")
|
||||
}
|
||||
}
|
||||
|
||||
internal class LinuxPlatform(distrib: Distribution,
|
||||
properties: KonanProperties) : PlatformFlags(distrib, properties) {
|
||||
internal class IPhoneOSfromMacOSPlatform(distribution: Distribution)
|
||||
: MacOSPlatform(distribution) {
|
||||
|
||||
override val arch = properties.propertyString("arch.osx-ios")!!
|
||||
override val osVersionMin = properties.propertyList("osVersionMin.osx-ios")
|
||||
override val llvmLlcFlags = properties.propertyList("llvmLlcFlags.osx-ios")
|
||||
override val targetSysRoot = "${distribution.dependencies}/${properties.propertyString("targetSysRoot.osx-ios")!!}"
|
||||
}
|
||||
|
||||
internal class IPhoneSimulatorFromMacOSPlatform(distribution: Distribution)
|
||||
: MacOSPlatform(distribution) {
|
||||
|
||||
override val arch = properties.propertyString("arch.osx-ios-sim")!!
|
||||
override val osVersionMin = properties.propertyList("osVersionMin.osx-ios-sim")
|
||||
override val llvmLlcFlags = properties.propertyList("llvmLlcFlags.osx-ios-sim")
|
||||
override val targetSysRoot = "${distribution.dependencies}/${properties.propertyString("targetSysRoot.osx-ios-sim")!!}"
|
||||
}
|
||||
internal class LinuxPlatform(distribution: Distribution)
|
||||
: PlatformFlags(distribution) {
|
||||
|
||||
override val llvmLtoFlags = properties.propertyList("llvmLtoFlags.linux")
|
||||
override val llvmLlcFlags = properties.propertyList("llvmLlcFlags.linux")
|
||||
@@ -63,16 +84,16 @@ internal class LinuxPlatform(distrib: Distribution,
|
||||
override val linkerOptimizationFlags =
|
||||
properties.propertyList("linkerOptimizationFlags.linux")
|
||||
override val linkerKonanFlags = properties.propertyList("linkerKonanFlags.linux")
|
||||
override val linker = "${distrib.sysRoot}/../bin/ld.gold"
|
||||
override val linker = "${distribution.sysRoot}/../bin/ld.gold"
|
||||
|
||||
val pluginOptimizationFlags =
|
||||
properties.propertyList("pluginOptimizationFlags.linux")
|
||||
|
||||
override fun linkCommand(objectFiles: List<String>, executable: String, optimize: Boolean): List<String> {
|
||||
|
||||
val sysRoot = distrib.sysRoot
|
||||
val llvmLib = distrib.llvmLib
|
||||
val libGcc = distrib.libGcc
|
||||
val sysRoot = distribution.sysRoot
|
||||
val llvmLib = distribution.llvmLib
|
||||
val libGcc = distribution.libGcc
|
||||
|
||||
// TODO: Can we extract more to the konan.properties?
|
||||
return mutableListOf<String>("$linker",
|
||||
@@ -97,41 +118,40 @@ internal class LinuxPlatform(distrib: Distribution,
|
||||
|
||||
|
||||
internal class LinkStage(val context: Context) {
|
||||
|
||||
private val javaOsName = System.getProperty("os.name")
|
||||
private val javaArch = System.getProperty("os.arch")
|
||||
|
||||
val os = when (javaOsName) {
|
||||
"Mac OS X" -> "osx"
|
||||
"Linux" -> "linux"
|
||||
else -> error("Unknown operating system: ${javaOsName}")
|
||||
}
|
||||
val arch = when (javaArch) {
|
||||
"x86_64" -> "x86_64"
|
||||
"amd64" -> "x86_64"
|
||||
else -> error("Unknown hardware platform: ${javaArch}")
|
||||
}
|
||||
|
||||
private val properties = KonanProperties(context.config.configuration)
|
||||
private val distrib = Distribution(properties, os, arch)
|
||||
|
||||
val platform: PlatformFlags = when (os) {
|
||||
"linux" -> LinuxPlatform(distrib, properties)
|
||||
"osx" -> MacOSPlatform(distrib, properties)
|
||||
else -> error("Could not tell the current platform")
|
||||
}
|
||||
|
||||
val config = context.config.configuration
|
||||
|
||||
val targetManager = TargetManager(config)
|
||||
private val distribution =
|
||||
Distribution(context.config.configuration)
|
||||
private val properties = distribution.properties
|
||||
|
||||
val platform: PlatformFlags = when (TargetManager.host) {
|
||||
KonanTarget.LINUX -> LinuxPlatform(distribution)
|
||||
KonanTarget.MACBOOK -> when (targetManager.current) {
|
||||
KonanTarget.IPHONE_SIM
|
||||
-> IPhoneSimulatorFromMacOSPlatform(distribution)
|
||||
KonanTarget.IPHONE
|
||||
-> IPhoneOSfromMacOSPlatform(distribution)
|
||||
KonanTarget.MACBOOK
|
||||
-> MacOSPlatform(distribution)
|
||||
else -> TODO("Target not implemented yet.")
|
||||
}
|
||||
else -> TODO("Target not implemented yet")
|
||||
}
|
||||
val suffix = targetManager.currentSuffix()
|
||||
|
||||
val optimize = config.get(KonanConfigKeys.OPTIMIZATION) ?: false
|
||||
val emitted = config.get(KonanConfigKeys.BITCODE_FILE)!!
|
||||
val nostdlib = config.get(KonanConfigKeys.NOSTDLIB) ?: false
|
||||
val libraries = context.config.libraries
|
||||
|
||||
fun llvmLto(files: List<BitcodeFile>): ObjectFile {
|
||||
// TODO: Make it a temporary file.
|
||||
val combined = "combined.o"
|
||||
val tmpCombined = File.createTempFile("combined", ".o")
|
||||
tmpCombined.deleteOnExit()
|
||||
val combined = tmpCombined.absolutePath
|
||||
|
||||
val tool = distrib.llvmLto
|
||||
val tool = distribution.llvmLto
|
||||
val command = mutableListOf(tool, "-o", combined)
|
||||
if (optimize) {
|
||||
command.addAll(platform.llvmLtoFlags)
|
||||
@@ -143,12 +163,13 @@ internal class LinkStage(val context: Context) {
|
||||
}
|
||||
|
||||
fun llvmLlc(file: BitcodeFile): ObjectFile {
|
||||
// TODO: Make it a temporary file.
|
||||
val objectFile = "$file.o"
|
||||
val tmpObjectFile = File.createTempFile(File(file).name, ".o")
|
||||
tmpObjectFile.deleteOnExit()
|
||||
val objectFile = tmpObjectFile.absolutePath
|
||||
|
||||
val tool = distrib.llvmLlc
|
||||
val command = listOf(distrib.llvmLlc, "-o", objectFile, "-filetype=obj") +
|
||||
properties.propertyList("llvmLlcFlags.$os") + listOf(file)
|
||||
val tool = distribution.llvmLlc
|
||||
val command = listOf(distribution.llvmLlc, "-o", objectFile, "-filetype=obj") +
|
||||
properties.propertyList("llvmLlcFlags.$suffix") + listOf(file)
|
||||
runTool(*command.toTypedArray())
|
||||
|
||||
return objectFile
|
||||
@@ -186,10 +207,10 @@ internal class LinkStage(val context: Context) {
|
||||
}
|
||||
|
||||
fun linkStage() {
|
||||
context.log("# Compiler root: ${Distribution.konanHome}")
|
||||
context.log("# Compiler root: ${distribution.konanHome}")
|
||||
|
||||
val bitcodeFiles = listOf<BitcodeFile>(emitted, Distribution.start, Distribution.runtime,
|
||||
Distribution.launcher) + libraries
|
||||
val bitcodeFiles = listOf<BitcodeFile>(emitted, distribution.start, distribution.runtime,
|
||||
distribution.launcher) + libraries
|
||||
|
||||
val objectFiles = if (optimize) {
|
||||
listOf( llvmLto(bitcodeFiles ) )
|
||||
|
||||
+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.isFunctionType
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl
|
||||
import org.jetbrains.kotlin.descriptors.impl.PropertyGetterDescriptorImpl
|
||||
import org.jetbrains.kotlin.descriptors.impl.PropertySetterDescriptorImpl
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.OverridingUtil
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.*
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeSubstitution
|
||||
import org.jetbrains.kotlin.types.TypeSubstitutor
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
@@ -35,16 +42,16 @@ internal val ClassDescriptor.implementedInterfaces: List<ClassDescriptor>
|
||||
*
|
||||
* TODO: this method is actually a part of resolve and probably duplicates another one
|
||||
*/
|
||||
internal val FunctionDescriptor.implementation: FunctionDescriptor
|
||||
get() {
|
||||
if (this.kind.isReal) {
|
||||
return this
|
||||
} else {
|
||||
val overridden = OverridingUtil.getOverriddenDeclarations(this)
|
||||
val filtered = OverridingUtil.filterOutOverridden(overridden)
|
||||
return filtered.first { it.modality != Modality.ABSTRACT } as FunctionDescriptor
|
||||
}
|
||||
internal fun FunctionDescriptor.resolveFakeOverride(): FunctionDescriptor {
|
||||
if (this.kind.isReal) {
|
||||
return this
|
||||
} else {
|
||||
val overridden = OverridingUtil.getOverriddenDeclarations(this)
|
||||
val filtered = OverridingUtil.filterOutOverridden(overridden)
|
||||
// TODO: is it correct to take first?
|
||||
return filtered.first { it.modality != Modality.ABSTRACT } as FunctionDescriptor
|
||||
}
|
||||
}
|
||||
|
||||
private val intrinsicAnnotation = FqName("konan.internal.Intrinsic")
|
||||
|
||||
@@ -128,16 +135,18 @@ internal fun KotlinType.isUnboundCallableReference(): Boolean {
|
||||
|
||||
internal val CallableDescriptor.allValueParameters: List<ParameterDescriptor>
|
||||
get() {
|
||||
val constructorReceiver = if (this is ConstructorDescriptor) {
|
||||
this.constructedClass.thisAsReceiverParameter
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val receivers = mutableListOf<ParameterDescriptor>()
|
||||
|
||||
val receivers = listOf(
|
||||
constructorReceiver,
|
||||
this.dispatchReceiverParameter,
|
||||
this.extensionReceiverParameter).filterNotNull()
|
||||
if (this is ConstructorDescriptor)
|
||||
receivers.add(this.constructedClass.thisAsReceiverParameter)
|
||||
|
||||
val dispatchReceiverParameter = this.dispatchReceiverParameter
|
||||
if (dispatchReceiverParameter != null)
|
||||
receivers.add(dispatchReceiverParameter)
|
||||
|
||||
val extensionReceiverParameter = this.extensionReceiverParameter
|
||||
if (extensionReceiverParameter != null)
|
||||
receivers.add(extensionReceiverParameter)
|
||||
|
||||
return receivers + this.valueParameters
|
||||
}
|
||||
@@ -152,7 +161,7 @@ internal val FunctionDescriptor.isFunctionInvoke: Boolean
|
||||
|
||||
internal fun ClassDescriptor.isUnit() = this.defaultType.isUnit()
|
||||
|
||||
internal val ClassDescriptor.сontributedMethods: List<FunctionDescriptor>
|
||||
internal val ClassDescriptor.contributedMethods: List<FunctionDescriptor>
|
||||
get() {
|
||||
val contributedDescriptors = unsubstitutedMemberScope.getContributedDescriptors()
|
||||
// (includes declarations from supers)
|
||||
@@ -171,6 +180,7 @@ internal val ClassDescriptor.сontributedMethods: List<FunctionDescriptor>
|
||||
}
|
||||
|
||||
fun ClassDescriptor.isAbstract() = this.modality == Modality.SEALED || this.modality == Modality.ABSTRACT
|
||||
|| this.kind == ClassKind.ENUM_CLASS
|
||||
|
||||
// TODO: optimize
|
||||
val ClassDescriptor.vtableEntries: List<FunctionDescriptor>
|
||||
@@ -183,18 +193,15 @@ val ClassDescriptor.vtableEntries: List<FunctionDescriptor>
|
||||
this.getSuperClassOrAny().vtableEntries
|
||||
}
|
||||
|
||||
val methods = this.сontributedMethods
|
||||
val methods = this.contributedMethods
|
||||
|
||||
val inheritedVtableSlots = superVtableEntries.map { superMethod ->
|
||||
methods.single { OverridingUtil.overrides(it, superMethod) }
|
||||
methods.singleOrNull { OverridingUtil.overrides(it, superMethod) } ?: superMethod
|
||||
}
|
||||
|
||||
return inheritedVtableSlots + (methods - inheritedVtableSlots).filter { it.isOverridable }
|
||||
}
|
||||
|
||||
val ClassDescriptor.vtableSize: Int
|
||||
get() = if (this.isAbstract()) 0 else this.vtableEntries.size
|
||||
|
||||
fun ClassDescriptor.vtableIndex(function: FunctionDescriptor): Int {
|
||||
this.vtableEntries.forEachIndexed { index, functionDescriptor ->
|
||||
if (functionDescriptor == function.original) return index
|
||||
@@ -204,8 +211,11 @@ fun ClassDescriptor.vtableIndex(function: FunctionDescriptor): Int {
|
||||
|
||||
val ClassDescriptor.methodTableEntries: List<FunctionDescriptor>
|
||||
get() {
|
||||
assert (!this.isAbstract())
|
||||
|
||||
return this.сontributedMethods.filter { it.isOverridableOrOverrides }
|
||||
assert(!this.isAbstract())
|
||||
return this.contributedMethods.filter {
|
||||
// We check that either method is open, or one of declarations it overrides
|
||||
// is open.
|
||||
it.isOverridable || DescriptorUtils.getAllOverriddenDeclarations(it).any { it.isOverridable }
|
||||
}
|
||||
// TODO: probably method table should contain all accessible methods to improve binary compatibility
|
||||
}
|
||||
+3
-1
@@ -37,5 +37,7 @@ val PropertyDescriptor.backingField: PropertyDescriptor?
|
||||
}
|
||||
|
||||
fun DeclarationDescriptor.deepPrint() {
|
||||
this!!.accept(DeepPrintVisitor(PrintVisitor()), 0)
|
||||
this.accept(DeepPrintVisitor(PrintVisitor()), 0)
|
||||
}
|
||||
|
||||
internal val String.synthesizedName get() = Name.identifier("\$" + this)
|
||||
|
||||
+7
-1
@@ -1,6 +1,8 @@
|
||||
package org.jetbrains.kotlin.backend.konan.ir
|
||||
|
||||
import org.jetbrains.kotlin.backend.konan.Context
|
||||
import org.jetbrains.kotlin.backend.konan.lower.DefaultParameterDescription
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
|
||||
@@ -8,5 +10,9 @@ import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
internal class Ir(val context: Context, val irModule: IrModuleFragment) {
|
||||
val propertiesWithBackingFields = mutableSetOf<PropertyDescriptor>()
|
||||
|
||||
val moduleIndex = ModuleIndex(irModule)
|
||||
val defaultParameterDescriptions = mutableMapOf<FunctionDescriptor, DefaultParameterDescription>()
|
||||
|
||||
val originalModuleIndex = ModuleIndex(irModule)
|
||||
|
||||
lateinit var moduleIndexForCodegen: ModuleIndex
|
||||
}
|
||||
|
||||
+4
-8
@@ -1,5 +1,6 @@
|
||||
package org.jetbrains.kotlin.backend.konan.ir
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
@@ -8,15 +9,13 @@ import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
|
||||
|
||||
class ModuleIndex(val module: IrModuleFragment) {
|
||||
|
||||
/**
|
||||
* Contains all classes declared in [module]
|
||||
*/
|
||||
val classes: Map<ClassId, IrClass>
|
||||
val classes: Map<ClassDescriptor, IrClass>
|
||||
|
||||
/**
|
||||
* Contains all functions declared in [module]
|
||||
@@ -24,7 +23,7 @@ class ModuleIndex(val module: IrModuleFragment) {
|
||||
val functions = mutableMapOf<FunctionDescriptor, IrFunction>()
|
||||
|
||||
init {
|
||||
val map = mutableMapOf<ClassId, IrClass>()
|
||||
val map = mutableMapOf<ClassDescriptor, IrClass>()
|
||||
|
||||
module.acceptVoid(object : IrElementVisitorVoid {
|
||||
override fun visitElement(element: IrElement) {
|
||||
@@ -34,10 +33,7 @@ class ModuleIndex(val module: IrModuleFragment) {
|
||||
override fun visitClass(declaration: IrClass) {
|
||||
super.visitClass(declaration)
|
||||
|
||||
val classId = declaration.descriptor.classId
|
||||
if (classId != null) {
|
||||
map[classId] = declaration
|
||||
}
|
||||
map[declaration.descriptor] = declaration
|
||||
}
|
||||
|
||||
override fun visitFunction(declaration: IrFunction) {
|
||||
|
||||
+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.
|
||||
var constructedClass: ClassDescriptor? = null
|
||||
val vars = VariableManager(this)
|
||||
var returnSlot: LLVMValueRef? = null
|
||||
var slotsPhi: LLVMValueRef? = null
|
||||
var slotCount = 0
|
||||
var functionDescriptor: FunctionDescriptor? = null
|
||||
private var returnSlot: LLVMValueRef? = null
|
||||
private var slotsPhi: LLVMValueRef? = null
|
||||
private var slotCount = 0
|
||||
private var localAllocs = 0
|
||||
private var arenaSlot: LLVMValueRef? = null
|
||||
|
||||
private val intPtrType = LLVMIntPtrType(llvmTargetData)!!
|
||||
private val immOneIntPtrType = LLVMConstInt(intPtrType, 1, 1)!!
|
||||
|
||||
fun prologue(descriptor: FunctionDescriptor) {
|
||||
prologue(llvmFunction(descriptor),
|
||||
val llvmFunction = llvmFunction(descriptor)
|
||||
|
||||
prologue(llvmFunction,
|
||||
LLVMGetReturnType(getLlvmFunctionType(descriptor))!!)
|
||||
|
||||
if (!descriptor.isExported()) {
|
||||
LLVMSetLinkage(llvmFunction, LLVMLinkage.LLVMInternalLinkage)
|
||||
// (Cannot do this before the function body is created).
|
||||
}
|
||||
|
||||
if (descriptor is ConstructorDescriptor) {
|
||||
constructedClass = descriptor.constructedClass
|
||||
}
|
||||
@@ -47,16 +60,21 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
|
||||
cleanupLandingpad = LLVMAppendBasicBlock(function, "cleanup_landingpad")!!
|
||||
positionAtEnd(entryBb!!)
|
||||
slotsPhi = phi(kObjHeaderPtrPtr)
|
||||
slotCount = 0
|
||||
// First slot can be assigned to keep pointer to frame local arena.
|
||||
slotCount = 1
|
||||
localAllocs = 0
|
||||
// Is removed by DCE trivially, if not needed.
|
||||
arenaSlot = intToPtr(
|
||||
or(ptrToInt(slotsPhi, intPtrType), immOneIntPtrType), kObjHeaderPtrPtr)
|
||||
}
|
||||
|
||||
fun epilogue() {
|
||||
appendingTo(prologueBb!!) {
|
||||
val slots = if (slotCount > 0)
|
||||
val slots = if (needSlots)
|
||||
LLVMBuildArrayAlloca(builder, kObjHeaderPtr, Int32(slotCount).llvm, "")!!
|
||||
else
|
||||
kNullObjHeaderPtrPtr
|
||||
if (slotCount > 0) {
|
||||
if (needSlots) {
|
||||
// Zero-init slots.
|
||||
val slotsMem = bitcast(kInt8Ptr, slots)
|
||||
val pointerSize = LLVMABISizeOfType(llvmTargetData, kObjHeaderPtr).toInt()
|
||||
@@ -81,7 +99,7 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
|
||||
val returnPhi = phi(returnType!!)
|
||||
addPhiIncoming(returnPhi, *returns.toList().toTypedArray())
|
||||
if (returnSlot != null) {
|
||||
updateLocalRef(returnPhi, returnSlot!!)
|
||||
updateReturnRef(returnPhi, returnSlot!!)
|
||||
}
|
||||
releaseVars()
|
||||
LLVMBuildRet(builder, returnPhi)
|
||||
@@ -104,9 +122,14 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
|
||||
slotsPhi = null
|
||||
}
|
||||
|
||||
fun releaseVars() {
|
||||
if (slotCount > 0) {
|
||||
call(context.llvm.releaseLocalRefsFunction,
|
||||
private val needSlots: Boolean
|
||||
get() {
|
||||
return slotCount > 1 || localAllocs > 0
|
||||
}
|
||||
|
||||
private fun releaseVars() {
|
||||
if (needSlots) {
|
||||
call(context.llvm.leaveFrameFunction,
|
||||
listOf(slotsPhi!!, Int32(slotCount).llvm))
|
||||
}
|
||||
}
|
||||
@@ -125,6 +148,8 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
|
||||
fun div (arg0: LLVMValueRef, arg1: LLVMValueRef, name: String = ""): LLVMValueRef = LLVMBuildSDiv(builder, arg0, arg1, name)!!
|
||||
fun srem (arg0: LLVMValueRef, arg1: LLVMValueRef, name: String = ""): LLVMValueRef = LLVMBuildSRem(builder, arg0, arg1, name)!!
|
||||
|
||||
fun or (arg0: LLVMValueRef, arg1: LLVMValueRef, name: String = ""): LLVMValueRef = LLVMBuildOr (builder, arg0, arg1, name)!!
|
||||
|
||||
/* integers comparisons */
|
||||
fun icmpEq(arg0: LLVMValueRef, arg1: LLVMValueRef, name: String = ""): LLVMValueRef = LLVMBuildICmp(builder, LLVMIntPredicate.LLVMIntEQ, arg0, arg1, name)!!
|
||||
fun icmpGt(arg0: LLVMValueRef, arg1: LLVMValueRef, name: String = ""): LLVMValueRef = LLVMBuildICmp(builder, LLVMIntPredicate.LLVMIntSGT, arg0, arg1, name)!!
|
||||
@@ -140,7 +165,8 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
|
||||
|
||||
fun bitcast(type: LLVMTypeRef?, value: LLVMValueRef, name: String = "") = LLVMBuildBitCast(builder, value, type, name)!!
|
||||
|
||||
fun intToPtr(imm: LLVMValueRef?, DestTy: LLVMTypeRef, Name: String = "") = LLVMBuildIntToPtr(builder, imm, DestTy, Name)!!
|
||||
fun intToPtr(value: LLVMValueRef?, DestTy: LLVMTypeRef, Name: String = "") = LLVMBuildIntToPtr(builder, value, DestTy, Name)!!
|
||||
fun ptrToInt(value: LLVMValueRef?, DestTy: LLVMTypeRef, Name: String = "") = LLVMBuildPtrToInt(builder, value, DestTy, Name)!!
|
||||
|
||||
fun alloca(type: LLVMTypeRef?, name: String = ""): LLVMValueRef {
|
||||
if (isObjectType(type!!)) {
|
||||
@@ -150,6 +176,16 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
|
||||
return LLVMBuildAlloca(builder, type, name)!!
|
||||
}
|
||||
}
|
||||
|
||||
fun allocInstance(typeInfo: LLVMValueRef, lifetime: Lifetime) : LLVMValueRef {
|
||||
return call(context.llvm.allocInstanceFunction, listOf(typeInfo), lifetime)
|
||||
}
|
||||
|
||||
fun allocArray(
|
||||
typeInfo: LLVMValueRef, count: LLVMValueRef, lifetime: Lifetime) : LLVMValueRef {
|
||||
return call(context.llvm.allocArrayFunction, listOf(typeInfo, count), lifetime)
|
||||
}
|
||||
|
||||
fun load(value: LLVMValueRef, name: String = ""): LLVMValueRef {
|
||||
val result = LLVMBuildLoad(builder, value, name)!!
|
||||
// Use loadSlot() API for that.
|
||||
@@ -191,6 +227,10 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
|
||||
}
|
||||
}
|
||||
|
||||
fun updateReturnRef(value: LLVMValueRef, address: LLVMValueRef) {
|
||||
call(context.llvm.updateReturnRefFunction, listOf(address, value))
|
||||
}
|
||||
|
||||
// Only use ignoreOld, when sure that memory is freshly inited and have no value.
|
||||
fun updateLocalRef(value: LLVMValueRef, address: LLVMValueRef, ignoreOld: Boolean = false) {
|
||||
call(if (ignoreOld) context.llvm.setLocalRefFunction else context.llvm.updateLocalRefFunction,
|
||||
@@ -206,19 +246,44 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
fun callAtFunctionScope(llvmFunction: LLVMValueRef, args: List<LLVMValueRef>, name: String = "") =
|
||||
call(llvmFunction, args, this::cleanupLandingpad, name)
|
||||
fun callAtFunctionScope(llvmFunction: LLVMValueRef, args: List<LLVMValueRef>,
|
||||
lifetime: Lifetime) =
|
||||
call(llvmFunction, args, lifetime, this::cleanupLandingpad)
|
||||
|
||||
fun call(llvmFunction: LLVMValueRef, args: List<LLVMValueRef>,
|
||||
lazyLandingpad: () -> LLVMBasicBlockRef? = { null }, name: String = ""): LLVMValueRef {
|
||||
resultLifetime: Lifetime = Lifetime.IRRELEVANT,
|
||||
lazyLandingpad: () -> LLVMBasicBlockRef? = { null }): LLVMValueRef {
|
||||
var callArgs = if (isObjectReturn(llvmFunction.type)) {
|
||||
// If function returns an object - create slot for the returned value or give local arena.
|
||||
// This allows appropriate rootset accounting by just looking at the stack slots,
|
||||
// along with ability to allocate in appropriate arena.
|
||||
val resultSlot = when (resultLifetime.slotType) {
|
||||
SlotType.ARENA -> {
|
||||
localAllocs++
|
||||
arenaSlot!!
|
||||
}
|
||||
SlotType.RETURN -> returnSlot!!
|
||||
// TODO: for RETURN_IF_ARENA choose between created slot and arenaSlot
|
||||
// dynamically.
|
||||
SlotType.ANONYMOUS, SlotType.RETURN_IF_ARENA -> vars.createAnonymousSlot()
|
||||
else -> throw Error("Incorrect slot type")
|
||||
}
|
||||
args + resultSlot
|
||||
} else {
|
||||
args
|
||||
}
|
||||
return callRaw(llvmFunction, callArgs, lazyLandingpad)
|
||||
}
|
||||
|
||||
private fun callRaw(llvmFunction: LLVMValueRef, args: List<LLVMValueRef>,
|
||||
lazyLandingpad: () -> LLVMBasicBlockRef?): LLVMValueRef {
|
||||
memScoped {
|
||||
val rargs = allocArrayOf(args)[0].ptr
|
||||
|
||||
if (LLVMIsAFunction(llvmFunction) != null /* the function declaration */ &&
|
||||
LLVMAttribute.LLVMNoUnwindAttribute in LLVMGetFunctionAttrSet(llvmFunction)) {
|
||||
|
||||
return LLVMBuildCall(builder, llvmFunction, rargs, args.size, name)!!
|
||||
return LLVMBuildCall(builder, llvmFunction, rargs, args.size, "")!!
|
||||
} else {
|
||||
val landingpad = lazyLandingpad()
|
||||
|
||||
@@ -232,7 +297,7 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
|
||||
}
|
||||
|
||||
val success = basicBlock()
|
||||
val result = LLVMBuildInvoke(builder, llvmFunction, rargs, args.size, success, landingpad, name)!!
|
||||
val result = LLVMBuildInvoke(builder, llvmFunction, rargs, args.size, success, landingpad, "")!!
|
||||
positionAtEnd(success)
|
||||
return result
|
||||
}
|
||||
@@ -264,7 +329,6 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
/* to class descriptor */
|
||||
fun classType(descriptor: ClassDescriptor): LLVMTypeRef = LLVMGetTypeByName(context.llvmModule, descriptor.symbolName)!!
|
||||
fun typeInfoValue(descriptor: ClassDescriptor): LLVMValueRef = descriptor.llvmTypeInfoPtr
|
||||
|
||||
/**
|
||||
@@ -278,9 +342,6 @@ internal class CodeGenerator(override val context: Context) : ContextUtils {
|
||||
}
|
||||
fun countParams(fn: FunctionDescriptor) = LLVMCountParams(fn.llvmFunction)
|
||||
|
||||
fun indexInClass(p:PropertyDescriptor):Int = (p.containingDeclaration as ClassDescriptor).fields.indexOf(p)
|
||||
|
||||
|
||||
fun basicBlock(name: String = "label_"): LLVMBasicBlockRef {
|
||||
val currentBlock = this.currentBlock
|
||||
val result = LLVMInsertBasicBlock(currentBlock, name)!!
|
||||
|
||||
+65
-81
@@ -3,32 +3,61 @@ package org.jetbrains.kotlin.backend.konan.llvm
|
||||
import kotlinx.cinterop.*
|
||||
import llvm.*
|
||||
import org.jetbrains.kotlin.backend.konan.Context
|
||||
import org.jetbrains.kotlin.backend.konan.Distribution
|
||||
import org.jetbrains.kotlin.backend.konan.hash.GlobalHash
|
||||
import org.jetbrains.kotlin.backend.konan.KonanConfigKeys
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.backingField
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.vtableSize
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.IrProperty
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
|
||||
internal enum class SlotType {
|
||||
// Frame local arena slot can be used.
|
||||
ARENA,
|
||||
// Return slot can be used.
|
||||
RETURN,
|
||||
// Return slot, if it is an arena, can be used.
|
||||
RETURN_IF_ARENA,
|
||||
// Anonymous slot.
|
||||
ANONYMOUS,
|
||||
// Unknown slot type.
|
||||
UNKNOWN
|
||||
}
|
||||
|
||||
// Lifetimes class of reference, computed by escape analysis.
|
||||
internal enum class Lifetime(val slotType: SlotType) {
|
||||
// If reference is frame-local (only obtained from some call and never leaves).
|
||||
LOCAL(SlotType.ARENA),
|
||||
// If reference is only returned.
|
||||
RETURN_VALUE(SlotType.RETURN),
|
||||
// If reference is set as field of references of class RETURN_VALUE or INDIRECT_RETURN_VALUE.
|
||||
INDIRECT_RETURN_VALUE(SlotType.RETURN_IF_ARENA),
|
||||
// If reference is stored to the field of an incoming parameters.
|
||||
PARAMETER_FIELD(SlotType.ANONYMOUS),
|
||||
// If reference refers to the global (either global object or global variable).
|
||||
GLOBAL(SlotType.ANONYMOUS),
|
||||
// If reference used to throw.
|
||||
THROW(SlotType.ANONYMOUS),
|
||||
// If reference used as an argument of outgoing function. Class can be improved by escape analysis
|
||||
// of called function.
|
||||
ARGUMENT(SlotType.ANONYMOUS),
|
||||
// If reference class is unknown.
|
||||
UNKNOWN(SlotType.UNKNOWN),
|
||||
// If reference class is irrelevant.
|
||||
IRRELEVANT(SlotType.UNKNOWN)
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides utility methods to the implementer.
|
||||
*/
|
||||
internal interface ContextUtils {
|
||||
internal interface ContextUtils : RuntimeAware {
|
||||
val context: Context
|
||||
|
||||
val runtime: Runtime
|
||||
override val runtime: Runtime
|
||||
get() = context.llvm.runtime
|
||||
|
||||
/**
|
||||
@@ -42,53 +71,7 @@ internal interface ContextUtils {
|
||||
val staticData: StaticData
|
||||
get() = context.llvm.staticData
|
||||
|
||||
/**
|
||||
* All fields of the class instance.
|
||||
* The order respects the class hierarchy, i.e. a class [fields] contains superclass [fields] as a prefix.
|
||||
*/
|
||||
val ClassDescriptor.fields: List<PropertyDescriptor>
|
||||
get() {
|
||||
val superClass = this.getSuperClassNotAny() // TODO: what if Any has fields?
|
||||
val superFields = if (superClass != null) superClass.fields else emptyList()
|
||||
|
||||
return superFields + this.declaredFields
|
||||
}
|
||||
|
||||
/**
|
||||
* Fields declared in the class.
|
||||
*/
|
||||
val ClassDescriptor.declaredFields: List<PropertyDescriptor>
|
||||
get() {
|
||||
// TODO: Here's what is going on here:
|
||||
// The existence of a backing field for a property is only described in the IR,
|
||||
// but not in the property descriptor.
|
||||
// That works, while we process the IR, but not for deserialized descriptors.
|
||||
//
|
||||
// So to have something in deserialized descriptors,
|
||||
// while we still see the IR, we mark the property with an annotation.
|
||||
//
|
||||
// We could apply the annotation during IR rewite, but we still are not
|
||||
// that far in the rewriting infrastructure. So we postpone
|
||||
// the annotation until the serializer.
|
||||
//
|
||||
// In this function we check the presence of the backing filed
|
||||
// two ways: first we check IR, then we check the annotation.
|
||||
|
||||
val irClass = context.ir.moduleIndex.classes[this.classId]
|
||||
if (irClass != null) {
|
||||
val irProperties = irClass.declarations
|
||||
|
||||
return irProperties.mapNotNull {
|
||||
(it as? IrProperty)?.backingField?.descriptor
|
||||
}
|
||||
} else {
|
||||
val properties = this.unsubstitutedMemberScope.
|
||||
getContributedDescriptors().
|
||||
filterIsInstance<PropertyDescriptor>()
|
||||
|
||||
return properties.mapNotNull{ it.backingField }
|
||||
}
|
||||
}
|
||||
fun isExternal(descriptor: DeclarationDescriptor) = descriptor.module != context.ir.irModule.descriptor
|
||||
|
||||
/**
|
||||
* LLVM function generated from the Kotlin function.
|
||||
@@ -97,13 +80,15 @@ internal interface ContextUtils {
|
||||
val FunctionDescriptor.llvmFunction: LLVMValueRef
|
||||
get() {
|
||||
assert (this.kind.isReal)
|
||||
val globalName = this.symbolName
|
||||
val module = context.llvmModule
|
||||
if (this is TypeAliasConstructorDescriptor) {
|
||||
return this.underlyingConstructorDescriptor.llvmFunction
|
||||
}
|
||||
|
||||
val functionType = getLlvmFunctionType(this)
|
||||
|
||||
return LLVMGetNamedFunction(module, globalName) ?:
|
||||
LLVMAddFunction(module, globalName, functionType)!!
|
||||
return if (isExternal(this)) {
|
||||
context.llvm.externalFunction(this.symbolName, getLlvmFunctionType(this))
|
||||
} else {
|
||||
context.llvmDeclarations.forFunction(this).llvmFunction
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -115,17 +100,14 @@ internal interface ContextUtils {
|
||||
return constValue(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Pointer to struct { TypeInfo, vtable }.
|
||||
*/
|
||||
val ClassDescriptor.typeInfoWithVtable: ConstPointer
|
||||
get() {
|
||||
val type = structType(runtime.typeInfoType, LLVMArrayType(int8TypePtr, this.vtableSize)!!)
|
||||
return constPointer(externalGlobal(this.typeInfoSymbolName, type))
|
||||
}
|
||||
|
||||
val ClassDescriptor.typeInfoPtr: ConstPointer
|
||||
get() = typeInfoWithVtable.getElementPtr(0)
|
||||
get() {
|
||||
return if (isExternal(this)) {
|
||||
constPointer(externalGlobal(this.typeInfoSymbolName, runtime.typeInfoType))
|
||||
} else {
|
||||
context.llvmDeclarations.forClass(this).typeInfo
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pointer to type info for given class.
|
||||
@@ -223,13 +205,14 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun externalFunction(name: String, type: LLVMTypeRef): LLVMValueRef {
|
||||
val found = LLVMGetNamedFunction(context.llvmModule, name)
|
||||
internal fun externalFunction(name: String, type: LLVMTypeRef): LLVMValueRef {
|
||||
val found = LLVMGetNamedFunction(llvmModule, name)
|
||||
if (found != null) {
|
||||
assert (getFunctionType(found) == type)
|
||||
assert (LLVMGetLinkage(found) == LLVMLinkage.LLVMExternalLinkage)
|
||||
return found
|
||||
} else {
|
||||
return LLVMAddFunction(context.llvmModule, name, type)!!
|
||||
return LLVMAddFunction(llvmModule, name, type)!!
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,7 +224,7 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
|
||||
|
||||
val staticData = StaticData(context)
|
||||
|
||||
val runtimeFile = context.config.configuration.get(KonanConfigKeys.RUNTIME_FILE)!!
|
||||
val runtimeFile = context.config.distribution.runtime
|
||||
val runtime = Runtime(runtimeFile) // TODO: dispose
|
||||
|
||||
init {
|
||||
@@ -254,13 +237,14 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
|
||||
var globalInitIndex:Int = 0
|
||||
|
||||
val allocInstanceFunction = importRtFunction("AllocInstance")
|
||||
val initInstanceFunction = importRtFunction("InitInstance")
|
||||
val allocArrayFunction = importRtFunction("AllocArrayInstance")
|
||||
val initInstanceFunction = importRtFunction("InitInstance")
|
||||
val updateReturnRefFunction = importRtFunction("UpdateReturnRef")
|
||||
val setLocalRefFunction = importRtFunction("SetLocalRef")
|
||||
val setGlobalRefFunction = importRtFunction("SetGlobalRef")
|
||||
val updateLocalRefFunction = importRtFunction("UpdateLocalRef")
|
||||
val updateGlobalRefFunction = importRtFunction("UpdateGlobalRef")
|
||||
val releaseLocalRefsFunction = importRtFunction("ReleaseLocalRefs")
|
||||
val leaveFrameFunction = importRtFunction("LeaveFrame")
|
||||
val setArrayFunction = importRtFunction("Kotlin_Array_set")
|
||||
val copyImplArrayFunction = importRtFunction("Kotlin_Array_copyImpl")
|
||||
val lookupFieldOffset = importRtFunction("LookupFieldOffset")
|
||||
|
||||
+2
-2
@@ -6,7 +6,7 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
||||
|
||||
internal fun ContextUtils.getLLVMType(type: KotlinType): LLVMTypeRef {
|
||||
internal fun RuntimeAware.getLLVMType(type: KotlinType): LLVMTypeRef {
|
||||
return when {
|
||||
// Nullable types must be represented as objects for boxing.
|
||||
type.isMarkedNullable -> this.kObjHeaderPtr
|
||||
@@ -23,7 +23,7 @@ internal fun ContextUtils.getLLVMType(type: KotlinType): LLVMTypeRef {
|
||||
}!!
|
||||
}
|
||||
|
||||
internal fun ContextUtils.getLLVMReturnType(type: KotlinType): LLVMTypeRef {
|
||||
internal fun RuntimeAware.getLLVMReturnType(type: KotlinType): LLVMTypeRef {
|
||||
return when {
|
||||
type.isUnit() -> LLVMVoidType()!!
|
||||
// TODO: stdlib have methods taking Nothing, such as kotlin.collections.EmptySet.contains().
|
||||
|
||||
+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.Name
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
@@ -33,9 +32,16 @@ import org.jetbrains.kotlin.utils.addToStdlib.singletonList
|
||||
internal fun emitLLVM(context: Context) {
|
||||
|
||||
val irModule = context.irModule!!
|
||||
// Note that we don't set module target explicitly.
|
||||
// It is determined by the target of runtime.bc
|
||||
// (see Llvm class in ContextUtils)
|
||||
// Which in turn is determined by the clang flags
|
||||
// used to compile runtime.bc.
|
||||
val llvmModule = LLVMModuleCreateWithName("out")!! // TODO: dispose
|
||||
context.llvmModule = llvmModule
|
||||
|
||||
context.llvmDeclarations = createLlvmDeclarations(context)
|
||||
|
||||
val phaser = PhaseManager(context)
|
||||
|
||||
phaser.phase(KonanPhase.RTTI) {
|
||||
@@ -78,11 +84,6 @@ internal class RTTIGeneratorVisitor(context: Context) : IrElementVisitorVoid {
|
||||
override fun visitClass(declaration: IrClass) {
|
||||
super.visitClass(declaration)
|
||||
|
||||
if (declaration.descriptor.kind == ClassKind.ANNOTATION_CLASS) {
|
||||
// do not generate any RTTI for annotation classes as a workaround for link errors
|
||||
return
|
||||
}
|
||||
|
||||
if (declaration.descriptor.isIntrinsic) {
|
||||
// do not generate any code for intrinsic classes as they require special handling
|
||||
return
|
||||
@@ -118,7 +119,7 @@ internal class MetadatorVisitor(val context: Context) : IrElementVisitorVoid {
|
||||
/**
|
||||
* Defines how to generate context-dependent operations.
|
||||
*/
|
||||
interface CodeContext {
|
||||
internal interface CodeContext {
|
||||
|
||||
/**
|
||||
* Generates `return` [value] operation.
|
||||
@@ -131,7 +132,7 @@ interface CodeContext {
|
||||
|
||||
fun genContinue(destination: IrContinue)
|
||||
|
||||
fun genCall(function: LLVMValueRef, args: List<LLVMValueRef>): LLVMValueRef
|
||||
fun genCall(function: LLVMValueRef, args: List<LLVMValueRef>, resultLifetime: Lifetime): LLVMValueRef
|
||||
|
||||
fun genThrow(exception: LLVMValueRef)
|
||||
|
||||
@@ -167,6 +168,7 @@ interface CodeContext {
|
||||
internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid {
|
||||
|
||||
val codegen = CodeGenerator(context)
|
||||
val resultLifetimes = mutableMapOf<IrMemberAccessExpression, Lifetime>()
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
@@ -188,7 +190,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
|
||||
override fun genContinue(destination: IrContinue) = unsupported()
|
||||
|
||||
override fun genCall(function: LLVMValueRef, args: List<LLVMValueRef>) = unsupported(function)
|
||||
override fun genCall(function: LLVMValueRef, args: List<LLVMValueRef>, resultLifetime: Lifetime) = unsupported(function)
|
||||
|
||||
override fun genThrow(exception: LLVMValueRef) = unsupported()
|
||||
|
||||
@@ -235,6 +237,8 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
override fun visitModuleFragment(module: IrModuleFragment) {
|
||||
context.log("visitModule : ${ir2string(module)}")
|
||||
|
||||
computeLifetimes(module, resultLifetimes)
|
||||
|
||||
module.acceptChildrenVoid(this)
|
||||
appendLlvmUsed(context.llvm.usedFunctions)
|
||||
appendStaticInitializers(context.llvm.staticInitializers)
|
||||
@@ -246,7 +250,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
val kNodeInitType = LLVMGetTypeByName(context.llvmModule, "struct.InitNode")!!
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
fun createInitBody(initName: String) {
|
||||
fun createInitBody(initName: String): LLVMValueRef {
|
||||
val initFunction = LLVMAddFunction(context.llvmModule, initName, kVoidFuncType)!! // create LLVM function
|
||||
codegen.prologue(initFunction, voidType)
|
||||
using(FunctionScope(initFunction)) {
|
||||
@@ -254,33 +258,33 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
val irField = it as IrField
|
||||
val descriptor = irField.descriptor
|
||||
val initialization = evaluateExpression(irField.initializer!!.expression)
|
||||
val globalPtr = LLVMGetNamedGlobal(context.llvmModule, descriptor.symbolName)
|
||||
codegen.storeAnyGlobal(initialization, globalPtr!!)
|
||||
val globalPtr = context.llvmDeclarations.forStaticField(descriptor).storage
|
||||
codegen.storeAnyGlobal(initialization, globalPtr)
|
||||
}
|
||||
codegen.ret(null)
|
||||
}
|
||||
codegen.epilogue()
|
||||
|
||||
return initFunction
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
// Creates static struct InitNode $nodeName = {$initName, NULL};
|
||||
|
||||
fun createInitNode(initName: String, nodeName: String) {
|
||||
fun createInitNode(initFunction: LLVMValueRef, nodeName: String): LLVMValueRef {
|
||||
memScoped {
|
||||
val initFunction = LLVMGetNamedFunction(context.llvmModule, initName)!! // Get initialization function.
|
||||
val nextInitNode = LLVMConstNull(pointerType(kNodeInitType)) // Set InitNode.next = NULL.
|
||||
val argList = allocArrayOf(initFunction, nextInitNode)[0].ptr // Allocate array of args.
|
||||
val initNode = LLVMConstNamedStruct(kNodeInitType, argList, 2)!! // Create static object of class InitNode.
|
||||
context.llvm.staticData.placeGlobal(nodeName, constPointer(initNode)).llvmGlobal // Put the object in global var with name "nodeName".
|
||||
return context.llvm.staticData.placeGlobal(nodeName, constPointer(initNode)).llvmGlobal // Put the object in global var with name "nodeName".
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
fun createInitCtor(ctorName: String, nodeName: String) {
|
||||
fun createInitCtor(ctorName: String, initNodePtr: LLVMValueRef) {
|
||||
val ctorFunction = LLVMAddFunction(context.llvmModule, ctorName, kVoidFuncType)!! // Create constructor function.
|
||||
codegen.prologue(ctorFunction, voidType)
|
||||
val initNodePtr = LLVMGetNamedGlobal(context.llvmModule, nodeName)!! // Get LLVM function initializing globals of current file.
|
||||
codegen.call(context.llvm.appendToInitalizersTail, initNodePtr.singletonList()) // Add node to the tail of initializers list.
|
||||
codegen.ret(null)
|
||||
codegen.epilogue()
|
||||
@@ -305,9 +309,9 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
val nodeName = "${fileName}_node_${context.llvm.globalInitIndex}"
|
||||
val ctorName = "${fileName}_ctor_${context.llvm.globalInitIndex++}"
|
||||
|
||||
createInitBody(initName)
|
||||
createInitNode(initName, nodeName)
|
||||
createInitCtor(ctorName, nodeName)
|
||||
val initFunction = createInitBody(initName)
|
||||
val initNode = createInitNode(initFunction, nodeName)
|
||||
createInitCtor(ctorName, initNode)
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
@@ -380,15 +384,13 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
|
||||
if (constructorDescriptor.isPrimary) {
|
||||
if (DescriptorUtils.isObject(classDescriptor)) {
|
||||
if (classDescriptor.isUnit()) {
|
||||
context.llvm.staticData.createUnitInstance(classDescriptor)
|
||||
} else {
|
||||
if (!classDescriptor.isUnit()) {
|
||||
val objectPtr = objectPtrByName(classDescriptor)
|
||||
|
||||
LLVMSetInitializer(objectPtr, codegen.kNullObjHeaderPtr)
|
||||
}
|
||||
}
|
||||
val irOfCurrentClass = context.ir.moduleIndex.classes[classDescriptor.classId]
|
||||
val irOfCurrentClass = context.ir.moduleIndexForCodegen.classes[classDescriptor]
|
||||
irOfCurrentClass!!.acceptChildrenVoid(object : IrElementVisitorVoid {
|
||||
override fun visitElement(element: IrElement) {
|
||||
element.acceptChildrenVoid(this)
|
||||
@@ -527,14 +529,14 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
}
|
||||
}
|
||||
|
||||
override fun genCall(function: LLVMValueRef, args: List<LLVMValueRef>) =
|
||||
codegen.callAtFunctionScope(function, args)
|
||||
override fun genCall(function: LLVMValueRef, args: List<LLVMValueRef>, resultLifetime: Lifetime) =
|
||||
codegen.callAtFunctionScope(function, args, resultLifetime)
|
||||
|
||||
override fun genThrow(exception: LLVMValueRef) {
|
||||
val objHeaderPtr = codegen.bitcast(codegen.kObjHeaderPtr, exception)
|
||||
val args = listOf(objHeaderPtr)
|
||||
|
||||
this.genCall(context.llvm.throwExceptionFunction, args)
|
||||
this.genCall(context.llvm.throwExceptionFunction, args, Lifetime.IRRELEVANT)
|
||||
codegen.unreachable()
|
||||
}
|
||||
|
||||
@@ -587,6 +589,12 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
override fun visitTypeAlias(declaration: IrTypeAlias) {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
override fun visitProperty(declaration: IrProperty) {
|
||||
declaration.acceptChildrenVoid(this)
|
||||
}
|
||||
@@ -597,13 +605,18 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
context.log("visitField : ${ir2string(expression)}")
|
||||
val descriptor = expression.descriptor
|
||||
if (descriptor.containingDeclaration is PackageFragmentDescriptor) {
|
||||
val globalProperty = LLVMAddGlobal(context.llvmModule, codegen.getLLVMType(descriptor.type), descriptor.symbolName)
|
||||
val type = codegen.getLLVMType(descriptor.type)
|
||||
val globalProperty = context.llvmDeclarations.forStaticField(descriptor).storage
|
||||
if (expression.initializer!!.expression is IrConst<*>) {
|
||||
LLVMSetInitializer(globalProperty, evaluateExpression(expression.initializer!!.expression))
|
||||
} else {
|
||||
LLVMSetInitializer(globalProperty, codegen.kNullObjHeaderPtr)
|
||||
LLVMSetInitializer(globalProperty, LLVMConstNull(type))
|
||||
context.llvm.fileInitializers.add(expression)
|
||||
}
|
||||
|
||||
LLVMSetLinkage(globalProperty, LLVMLinkage.LLVMInternalLinkage)
|
||||
// (Cannot do this before the global is initialized).
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -679,11 +692,10 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
|
||||
codegen.positionAtEnd(bbInit)
|
||||
val typeInfo = codegen.typeInfoValue(value.descriptor)
|
||||
val allocHint = Int32(1).llvm
|
||||
val initFunction = value.descriptor.constructors.first { it.valueParameters.size == 0 }
|
||||
val ctor = codegen.llvmFunction(initFunction)
|
||||
val args = listOf(objectPtr, typeInfo, allocHint, ctor)
|
||||
val newValue = call(context.llvm.initInstanceFunction, args)
|
||||
val args = listOf(objectPtr, typeInfo, ctor)
|
||||
val newValue = call(context.llvm.initInstanceFunction, args, Lifetime.GLOBAL)
|
||||
val bbInitResult = codegen.currentBlock
|
||||
codegen.br(bbExit)
|
||||
|
||||
@@ -790,9 +802,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
codegen.plus(sum, size!!)
|
||||
}
|
||||
|
||||
val typeInfo = codegen.typeInfoValue(value.type)!!
|
||||
val arrayCreationArgs = listOf(typeInfo, kImmInt32One, finalLength)
|
||||
val array = call(context.llvm.allocArrayFunction, arrayCreationArgs)
|
||||
val array = codegen.allocArray(codegen.typeInfoValue(value.type)!!, finalLength, Lifetime.GLOBAL)
|
||||
elements.fold(kImmZero) { sum, (exp, size, isArray) ->
|
||||
if (!isArray) {
|
||||
call(context.llvm.setArrayFunction, listOf(array, sum, exp))
|
||||
@@ -820,6 +830,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
val kStringLength = KonanPlatform.builtIns.string.getter2Descriptor(kNameLength)
|
||||
val kStringBuilderToString = kStringBuilder.signature2Descriptor(kNameToString)
|
||||
|
||||
//TODO: make it lowering pass.
|
||||
private fun evaluateStringConcatenation(value: IrStringConcatenation): LLVMValueRef {
|
||||
data class Element(val string: LLVMValueRef, val llvmLenght: LLVMValueRef?, val length: Int)
|
||||
|
||||
@@ -830,8 +841,10 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
return@map Element(codegen.staticData.kotlinStringLiteral(it as IrConst<String>).llvm, null, string.length)
|
||||
} else {
|
||||
val toStringDescriptor = getToString(it.type)
|
||||
val string = if (KotlinBuiltIns.isString(it.type)) evaluationResult
|
||||
else evaluateSimpleFunctionCall(toStringDescriptor, listOf(evaluationResult))
|
||||
val string =
|
||||
if (KotlinBuiltIns.isString(it.type)) evaluationResult
|
||||
else evaluateSimpleFunctionCall(
|
||||
toStringDescriptor, listOf(evaluationResult), Lifetime.LOCAL)
|
||||
val length = call(codegen.llvmFunction(kStringLength!!), listOf(string))
|
||||
return@map Element(string, length, -1)
|
||||
}
|
||||
@@ -845,8 +858,8 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
|
||||
val constructor = kStringBuilder!!.constructors
|
||||
.firstOrNull { it -> it.valueParameters.size == 1 && KotlinBuiltIns.isInt(it.valueParameters[0].type) }!!
|
||||
val stringBuilderObj = call(context.llvm.allocInstanceFunction,
|
||||
listOf(codegen.typeInfoValue(kStringBuilder.defaultType)!!, kImmOne))
|
||||
val stringBuilderObj = codegen.allocInstance(codegen.typeInfoValue(kStringBuilder), Lifetime.LOCAL)
|
||||
|
||||
call(codegen.llvmFunction(constructor), listOf(stringBuilderObj, totalLength))
|
||||
|
||||
stringsWithLengths.fold(stringBuilderObj) { sum, (string, _, _) ->
|
||||
@@ -854,7 +867,8 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
return@fold sum
|
||||
}
|
||||
|
||||
return evaluateSimpleFunctionCall(kStringBuilderToString!!, listOf(stringBuilderObj))
|
||||
return evaluateSimpleFunctionCall(kStringBuilderToString!!, listOf(stringBuilderObj),
|
||||
Lifetime.GLOBAL /* TODO: fix */)
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
@@ -946,8 +960,8 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
}
|
||||
|
||||
// The call inside [CatchingScope] must be configured to dispatch exception to the scope's handler.
|
||||
override fun genCall(function: LLVMValueRef, args: List<LLVMValueRef>): LLVMValueRef {
|
||||
val res = codegen.call(function, args, this::landingpad)
|
||||
override fun genCall(function: LLVMValueRef, args: List<LLVMValueRef>, lifetime: Lifetime): LLVMValueRef {
|
||||
val res = codegen.call(function, args, lifetime, this::landingpad)
|
||||
return res
|
||||
}
|
||||
|
||||
@@ -1144,9 +1158,13 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
if (!isNothing) // If "when" has "exit".
|
||||
bbExit = codegen.basicBlock() // Create basic block to process "exit".
|
||||
|
||||
val resultPhi = if (isUnit || isNothing) null else
|
||||
val hasNoValue = !isUnconditional(expression.branches.last())
|
||||
// (It is possible if IrWhen is used as statement).
|
||||
|
||||
val llvmType = codegen.getLLVMType(expression.type)
|
||||
val resultPhi = if (isUnit || isNothing || hasNoValue) null else
|
||||
codegen.appendingTo(bbExit!!) {
|
||||
codegen.phi(codegen.getLLVMType(expression.type))
|
||||
codegen.phi(llvmType)
|
||||
}
|
||||
|
||||
expression.branches.forEach { // Iterate through "when" branches (clauses).
|
||||
@@ -1160,6 +1178,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
// FIXME: remove the hacks.
|
||||
isUnit -> codegen.theUnitInstanceRef.llvm
|
||||
isNothing -> codegen.kNothingFakeValue
|
||||
hasNoValue -> LLVMGetUndef(llvmType)!!
|
||||
else -> resultPhi!!
|
||||
}
|
||||
}
|
||||
@@ -1279,7 +1298,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
val srcArg = evaluateExpression(value.argument) // Evaluate src expression.
|
||||
val srcObjInfoPtr = codegen.bitcast(codegen.kObjHeaderPtr, srcArg) // Cast src to ObjInfoPtr.
|
||||
val args = listOf(srcObjInfoPtr, dstTypeInfo) // Create arg list.
|
||||
call(context.llvm.checkInstanceFunction, args) // Check if dst is subclass of src.
|
||||
call(context.llvm.checkInstanceFunction, args) // Check if dst is subclass of src.
|
||||
return srcArg
|
||||
}
|
||||
|
||||
@@ -1317,6 +1336,8 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
|
||||
private fun genInstanceOf(obj: LLVMValueRef, type: KotlinType): LLVMValueRef {
|
||||
val dstDescriptor = TypeUtils.getClassDescriptor(type) // Get class descriptor for dst type.
|
||||
// Reified parameters are not yet supported.
|
||||
assert(dstDescriptor != null)
|
||||
val dstTypeInfo = codegen.typeInfoValue(dstDescriptor!!) // Get TypeInfo for dst type.
|
||||
val srcObjInfoPtr = codegen.bitcast(codegen.kObjHeaderPtr, obj) // Cast src to ObjInfoPtr.
|
||||
val args = listOf(srcObjInfoPtr, dstTypeInfo) // Create arg list.
|
||||
@@ -1343,7 +1364,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
}
|
||||
else {
|
||||
assert (value.receiver == null)
|
||||
val ptr = LLVMGetNamedGlobal(context.llvmModule, value.descriptor.symbolName)!!
|
||||
val ptr = context.llvmDeclarations.forStaticField(value.descriptor).storage
|
||||
return codegen.loadSlot(ptr, value.descriptor.isVar())
|
||||
}
|
||||
}
|
||||
@@ -1352,13 +1373,12 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
|
||||
private fun objectPtrByName(descriptor: ClassDescriptor): LLVMValueRef {
|
||||
assert (!descriptor.isUnit())
|
||||
val objName = descriptor.fqNameSafe.asString()
|
||||
var objectPtr = LLVMGetNamedGlobal(context.llvmModule, objName)
|
||||
if (objectPtr == null) {
|
||||
return if (codegen.isExternal(descriptor)) {
|
||||
val llvmType = codegen.getLLVMType(descriptor.defaultType)
|
||||
objectPtr = LLVMAddGlobal(context.llvmModule, llvmType, objName)
|
||||
codegen.externalGlobal(descriptor.objectInstanceFieldSymbolName, llvmType)
|
||||
} else {
|
||||
context.llvmDeclarations.forSingleton(descriptor).instanceFieldRef
|
||||
}
|
||||
return objectPtr!!
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
@@ -1372,8 +1392,8 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
}
|
||||
else {
|
||||
assert (value.receiver == null)
|
||||
val globalValue = LLVMGetNamedGlobal(context.llvmModule, value.descriptor.symbolName)
|
||||
codegen.storeAnyGlobal(valueToAssign, globalValue!!)
|
||||
val globalValue = context.llvmDeclarations.forStaticField(value.descriptor).storage
|
||||
codegen.storeAnyGlobal(valueToAssign, globalValue)
|
||||
}
|
||||
|
||||
assert (value.type.isUnit())
|
||||
@@ -1415,13 +1435,14 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
|
||||
*/
|
||||
private fun fieldPtrOfClass(thisPtr: LLVMValueRef, value: PropertyDescriptor): LLVMValueRef {
|
||||
val objHeaderPtr = codegen.bitcast(codegen.kObjHeaderPtr, thisPtr)
|
||||
val typePtr = pointerType(codegen.classType(value.containingDeclaration as ClassDescriptor))
|
||||
val fieldInfo = context.llvmDeclarations.forField(value)
|
||||
|
||||
val typePtr = pointerType(fieldInfo.classBodyType)
|
||||
memScoped {
|
||||
val args = allocArrayOf(kImmOne)
|
||||
val objectPtr = LLVMBuildGEP(codegen.builder, objHeaderPtr, args[0].ptr, 1, "")
|
||||
val objectPtr = LLVMBuildGEP(codegen.builder, thisPtr, args[0].ptr, 1, "")
|
||||
val typedObjPtr = codegen.bitcast(typePtr, objectPtr!!)
|
||||
val fieldPtr = LLVMBuildStructGEP(codegen.builder, typedObjPtr, codegen.indexInClass(value), "")
|
||||
val fieldPtr = LLVMBuildStructGEP(codegen.builder, typedObjPtr, fieldInfo.index, "")
|
||||
return fieldPtr!!
|
||||
}
|
||||
}
|
||||
@@ -1619,7 +1640,8 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
value is IrDelegatingConstructorCall ->
|
||||
return delegatingConstructorCall(value.descriptor, args)
|
||||
|
||||
value.descriptor is FunctionDescriptor -> return evaluateFunctionCall(value as IrCall, args)
|
||||
value.descriptor is FunctionDescriptor -> return evaluateFunctionCall(
|
||||
value as IrCall, args, resultLifetime(value))
|
||||
else -> {
|
||||
TODO("${ir2string(value)}")
|
||||
}
|
||||
@@ -1654,11 +1676,12 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
private fun evaluateFunctionCall(callee: IrCall, args: List<LLVMValueRef>): LLVMValueRef {
|
||||
private fun evaluateFunctionCall(callee: IrCall, args: List<LLVMValueRef>,
|
||||
resultLifetime: Lifetime): LLVMValueRef {
|
||||
val descriptor:FunctionDescriptor = callee.descriptor as FunctionDescriptor
|
||||
|
||||
if (descriptor.isFunctionInvoke) {
|
||||
return evaluateFunctionInvoke(descriptor, args)
|
||||
return evaluateFunctionInvoke(descriptor, args, resultLifetime)
|
||||
}
|
||||
|
||||
if (descriptor.isIntrinsic) {
|
||||
@@ -1667,8 +1690,9 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
|
||||
when (descriptor) {
|
||||
is IrBuiltinOperatorDescriptorBase -> return evaluateOperatorCall (callee, args)
|
||||
is ClassConstructorDescriptor -> return evaluateConstructorCall (callee, args)
|
||||
else -> return evaluateSimpleFunctionCall(descriptor, args)
|
||||
is ConstructorDescriptor -> return evaluateConstructorCall (callee, args)
|
||||
else -> return evaluateSimpleFunctionCall(
|
||||
descriptor, args, resultLifetime, callee.superQualifier)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1683,7 +1707,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
}
|
||||
|
||||
private fun evaluateFunctionInvoke(descriptor: FunctionDescriptor,
|
||||
args: List<LLVMValueRef>): LLVMValueRef {
|
||||
args: List<LLVMValueRef>, resultLifetime: Lifetime): LLVMValueRef {
|
||||
|
||||
// Note: the whole function code below is written in the assumption that
|
||||
// `invoke` method receiver is passed as first argument.
|
||||
@@ -1698,22 +1722,24 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
|
||||
// Get `functionImpl.unboundRef`:
|
||||
val unboundRef = evaluateSimpleFunctionCall(functionImplUnboundRefGetter,
|
||||
listOf(functionImpl))
|
||||
listOf(functionImpl), Lifetime.IRRELEVANT /* unboundRef isn't managed reference */)
|
||||
|
||||
// Cast `functionImpl.unboundRef` to pointer to function:
|
||||
val entryPtr = codegen.bitcast(pointerType(unboundRefType), unboundRef, "entry")
|
||||
|
||||
return call(descriptor, entryPtr, args)
|
||||
return call(descriptor, entryPtr, args, resultLifetime)
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
private fun evaluateSimpleFunctionCall(descriptor: FunctionDescriptor, args: List<LLVMValueRef>): LLVMValueRef {
|
||||
private fun evaluateSimpleFunctionCall(
|
||||
descriptor: FunctionDescriptor, args: List<LLVMValueRef>,
|
||||
resultLifetime: Lifetime, superClass: ClassDescriptor? = null): LLVMValueRef {
|
||||
//context.log("evaluateSimpleFunctionCall : $tmpVariableName = ${ir2string(value)}")
|
||||
if (descriptor.isOverridable)
|
||||
return callVirtual(descriptor, args)
|
||||
if (descriptor.isOverridable && superClass == null)
|
||||
return callVirtual(descriptor, args, resultLifetime)
|
||||
else
|
||||
return callDirect(descriptor, args)
|
||||
return callDirect(descriptor, args, resultLifetime)
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
@@ -1724,28 +1750,28 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
if (descriptor.dispatchReceiverParameter != null)
|
||||
args.add(evaluateExpression(value.dispatchReceiver!!)) //add this ptr
|
||||
args.add(evaluateExpression(value.getValueArgument(0)!!))
|
||||
return evaluateSimpleFunctionCall(descriptor, args)
|
||||
return evaluateSimpleFunctionCall(
|
||||
descriptor, args, Lifetime.IRRELEVANT, value.superQualifier)
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
private fun resultLifetime(callee: IrMemberAccessExpression): Lifetime {
|
||||
return resultLifetimes.getOrElse(callee) { Lifetime.GLOBAL }
|
||||
}
|
||||
|
||||
private fun evaluateConstructorCall(callee: IrCall, args: List<LLVMValueRef>): LLVMValueRef {
|
||||
context.log("evaluateConstructorCall : ${ir2string(callee)}")
|
||||
memScoped {
|
||||
val containingClass = (callee.descriptor as ClassConstructorDescriptor).containingDeclaration
|
||||
val typeInfo = codegen.typeInfoValue(containingClass)
|
||||
val allocHint = Int32(1).llvm
|
||||
val thisValue = if (containingClass.isArray) {
|
||||
val constructedClass = (callee.descriptor as ConstructorDescriptor).constructedClass
|
||||
val thisValue = if (constructedClass.isArray) {
|
||||
assert(args.size >= 1 && args[0].type == int32Type)
|
||||
val allocArrayInstanceArgs = listOf(typeInfo, allocHint, args[0])
|
||||
call(context.llvm.allocArrayFunction, allocArrayInstanceArgs)
|
||||
codegen.allocArray(codegen.typeInfoValue(constructedClass), args[0],
|
||||
resultLifetime(callee))
|
||||
} else {
|
||||
call(context.llvm.allocInstanceFunction, listOf(typeInfo, allocHint))
|
||||
codegen.allocInstance(codegen.typeInfoValue(constructedClass), resultLifetime(callee))
|
||||
}
|
||||
val constructorParams: MutableList<LLVMValueRef> = mutableListOf()
|
||||
constructorParams += thisValue
|
||||
constructorParams += args
|
||||
evaluateSimpleFunctionCall(callee.descriptor as FunctionDescriptor, constructorParams)
|
||||
evaluateSimpleFunctionCall(callee.descriptor as FunctionDescriptor,
|
||||
listOf(thisValue) + args, Lifetime.IRRELEVANT /* constructor doesn't return anything */)
|
||||
return thisValue
|
||||
}
|
||||
}
|
||||
@@ -1758,8 +1784,8 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
|
||||
return when (name) {
|
||||
"konan.internal.areEqualByValue" -> {
|
||||
val arg0 = args[0]!!
|
||||
val arg1 = args[1]!!
|
||||
val arg0 = args[0]
|
||||
val arg1 = args[1]
|
||||
assert (arg0.type == arg1.type)
|
||||
|
||||
when (LLVMGetTypeKind(arg0.type)) {
|
||||
@@ -1787,12 +1813,12 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
val descriptor = callee.descriptor
|
||||
val ib = context.irModule!!.irBuiltins
|
||||
when (descriptor) {
|
||||
ib.eqeqeq -> return codegen.icmpEq(args[0]!!, args[1]!!)
|
||||
ib.gt0 -> return codegen.icmpGt(args[0]!!, kImmZero)
|
||||
ib.gteq0 -> return codegen.icmpGe(args[0]!!, kImmZero)
|
||||
ib.lt0 -> return codegen.icmpLt(args[0]!!, kImmZero)
|
||||
ib.lteq0 -> return codegen.icmpLe(args[0]!!, kImmZero)
|
||||
ib.booleanNot -> return codegen.icmpNe(args[0]!!, kTrue)
|
||||
ib.eqeqeq -> return codegen.icmpEq(args[0], args[1])
|
||||
ib.gt0 -> return codegen.icmpGt(args[0], kImmZero)
|
||||
ib.gteq0 -> return codegen.icmpGe(args[0], kImmZero)
|
||||
ib.lt0 -> return codegen.icmpLt(args[0], kImmZero)
|
||||
ib.lteq0 -> return codegen.icmpLe(args[0], kImmZero)
|
||||
ib.booleanNot -> return codegen.icmpNe(args[0], kTrue)
|
||||
else -> {
|
||||
TODO(descriptor.name.toString())
|
||||
}
|
||||
@@ -1824,7 +1850,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
if (resultPhi != null && !isNothing)
|
||||
codegen.assignPhis(resultPhi to brResult)
|
||||
if (bbExit != null && !isNothing)
|
||||
codegen.br(bbExit!!)
|
||||
codegen.br(bbExit)
|
||||
if (bbNext != null) // Switch generation to next or exit.
|
||||
codegen.positionAtEnd(bbNext)
|
||||
else if (bbExit != null)
|
||||
@@ -1840,15 +1866,17 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
fun callDirect(descriptor: FunctionDescriptor, args: List<LLVMValueRef>): LLVMValueRef {
|
||||
val realDescriptor = DescriptorUtils.unwrapFakeOverride(descriptor)
|
||||
fun callDirect(descriptor: FunctionDescriptor, args: List<LLVMValueRef>,
|
||||
resultLifetime: Lifetime): LLVMValueRef {
|
||||
val realDescriptor = descriptor.resolveFakeOverride().original
|
||||
val llvmFunction = codegen.functionLlvmValue(realDescriptor)
|
||||
return call(descriptor, llvmFunction, args)
|
||||
return call(descriptor, llvmFunction, args, resultLifetime)
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
fun callVirtual(descriptor: FunctionDescriptor, args: List<LLVMValueRef>): LLVMValueRef {
|
||||
fun callVirtual(descriptor: FunctionDescriptor, args: List<LLVMValueRef>,
|
||||
resultLifetime: Lifetime): LLVMValueRef {
|
||||
val typeInfoPtrPtr = LLVMBuildStructGEP(codegen.builder, args[0], 0 /* type_info */, "")!!
|
||||
val typeInfoPtr = codegen.load(typeInfoPtrPtr)
|
||||
assert (typeInfoPtr.type == codegen.kTypeInfoPtr)
|
||||
@@ -1864,18 +1892,17 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
val slot = codegen.gep(vtable, Int32(index).llvm)
|
||||
codegen.load(slot)
|
||||
} else {
|
||||
// Otherwise, call via hashtable.
|
||||
// Otherwise, call by hash.
|
||||
// TODO: optimize by storing interface number in lower bits of 'this' pointer
|
||||
// when passing object as an interface. This way we can use those bits as index
|
||||
// for an additional per-interface vtable.
|
||||
val methodHash = codegen.functionHash(descriptor) // Calculate hash of the method to be invoked
|
||||
val lookupArgs = listOf(typeInfoPtr, methodHash) // Prepare args for lookup
|
||||
call(context.llvm.lookupOpenMethodFunction,
|
||||
lookupArgs)
|
||||
call(context.llvm.lookupOpenMethodFunction, lookupArgs)
|
||||
}
|
||||
val functionPtrType = pointerType(codegen.getLlvmFunctionType(descriptor)) // Construct type of the method to be invoked
|
||||
val function = codegen.bitcast(functionPtrType, llvmMethod) // Cast method address to the type
|
||||
return call(descriptor, function, args) // Invoke the method
|
||||
return call(descriptor, function, args, resultLifetime) // Invoke the method
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
@@ -1884,8 +1911,9 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
// instead of a plain list.
|
||||
// In such case it would be possible to check that all args are available and in the correct order.
|
||||
// However, it currently requires some refactoring to be performed.
|
||||
private fun call(descriptor: FunctionDescriptor, function: LLVMValueRef, args: List<LLVMValueRef>): LLVMValueRef {
|
||||
val result = call(function, args)
|
||||
private fun call(descriptor: FunctionDescriptor, function: LLVMValueRef, args: List<LLVMValueRef>,
|
||||
resultLifetime: Lifetime): LLVMValueRef {
|
||||
val result = call(function, args, resultLifetime)
|
||||
if (descriptor.returnType?.isNothing() == true) {
|
||||
codegen.unreachable()
|
||||
}
|
||||
@@ -1897,15 +1925,9 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
return result
|
||||
}
|
||||
|
||||
private fun call(function: LLVMValueRef, args: List<LLVMValueRef>): LLVMValueRef {
|
||||
if (codegen.isObjectReturn(function.type)) {
|
||||
// If function returns an object - create slot for the returned value.
|
||||
// This allows appropriate rootset accounting by just looking on stack slots.
|
||||
val resultSlot = codegen.vars.createAnonymousSlot()
|
||||
return currentCodeContext.genCall(function, args + resultSlot)
|
||||
} else {
|
||||
return currentCodeContext.genCall(function, args)
|
||||
}
|
||||
private fun call(function: LLVMValueRef, args: List<LLVMValueRef>,
|
||||
resultLifetime: Lifetime = Lifetime.IRRELEVANT): LLVMValueRef {
|
||||
return currentCodeContext.genCall(function, args, resultLifetime)
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
@@ -1923,7 +1945,8 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
|
||||
codegen.bitcast(thisPtrArgType, thisPtr)
|
||||
}
|
||||
|
||||
return callDirect(descriptor, listOf(thisPtrArg) + args)
|
||||
return callDirect(descriptor, listOf(thisPtrArg) + args,
|
||||
Lifetime.IRRELEVANT /* no value returned */)
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
+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
|
||||
internal val ContextUtils.kTheArrayTypeInfo: LLVMValueRef
|
||||
get() = KonanPlatform.builtIns.array.llvmTypeInfoPtr
|
||||
internal val ContextUtils.kTypeInfo: LLVMTypeRef
|
||||
get() = LLVMGetTypeByName(context.llvmModule, "struct.TypeInfo")!!
|
||||
internal val ContextUtils.kObjHeader: LLVMTypeRef
|
||||
get() = LLVMGetTypeByName(context.llvmModule, "struct.ObjHeader")!!
|
||||
internal val ContextUtils.kObjHeaderPtr: LLVMTypeRef
|
||||
internal val RuntimeAware.kTypeInfo: LLVMTypeRef
|
||||
get() = runtime.typeInfoType
|
||||
internal val RuntimeAware.kObjHeader: LLVMTypeRef
|
||||
get() = runtime.objHeaderType
|
||||
internal val RuntimeAware.kContainerHeader: LLVMTypeRef
|
||||
get() = runtime.containerHeaderType
|
||||
internal val RuntimeAware.kObjHeaderPtr: LLVMTypeRef
|
||||
get() = pointerType(kObjHeader)
|
||||
internal val ContextUtils.kObjHeaderPtrPtr: LLVMTypeRef
|
||||
internal val RuntimeAware.kObjHeaderPtrPtr: LLVMTypeRef
|
||||
get() = pointerType(kObjHeaderPtr)
|
||||
internal val ContextUtils.kArrayHeader: LLVMTypeRef
|
||||
get() = LLVMGetTypeByName(context.llvmModule, "struct.ArrayHeader")!!
|
||||
internal val ContextUtils.kArrayHeaderPtr: LLVMTypeRef
|
||||
internal val RuntimeAware.kArrayHeader: LLVMTypeRef
|
||||
get() = runtime.arrayHeaderType
|
||||
internal val RuntimeAware.kArrayHeaderPtr: LLVMTypeRef
|
||||
get() = pointerType(kArrayHeader)
|
||||
internal val ContextUtils.kTypeInfoPtr: LLVMTypeRef
|
||||
internal val RuntimeAware.kTypeInfoPtr: LLVMTypeRef
|
||||
get() = pointerType(kTypeInfo)
|
||||
internal val kInt1 = LLVMInt1Type()!!
|
||||
internal val kBoolean = kInt1
|
||||
internal val kInt8Ptr = pointerType(int8Type)
|
||||
internal val kInt8PtrPtr = pointerType(kInt8Ptr)
|
||||
internal val kNullInt8Ptr = LLVMConstNull(kInt8Ptr)
|
||||
internal val kNullInt8Ptr = LLVMConstNull(kInt8Ptr)!!
|
||||
internal val kImmInt32One = Int32(1).llvm
|
||||
internal val kImmInt64One = Int64(1).llvm
|
||||
internal val ContextUtils.kNullObjHeaderPtr: LLVMValueRef
|
||||
@@ -149,18 +151,6 @@ internal fun structType(types: List<LLVMTypeRef>): LLVMTypeRef = memScoped {
|
||||
LLVMStructType(allocArrayOf(types)[0].ptr, types.size, 0)!!
|
||||
}
|
||||
|
||||
internal fun ContextUtils.getLlvmFunctionType(function: FunctionDescriptor): LLVMTypeRef {
|
||||
val original = function.original
|
||||
val returnType = if (original is ConstructorDescriptor) voidType else getLLVMReturnType(original.returnType!!)
|
||||
val paramTypes = ArrayList(original.allValueParameters.map { getLLVMType(it.type) })
|
||||
if (isObjectType(returnType)) paramTypes.add(kObjHeaderPtrPtr)
|
||||
|
||||
memScoped {
|
||||
val paramTypesPtr = allocArrayOf(paramTypes)[0].ptr
|
||||
return LLVMFunctionType(returnType, paramTypesPtr, paramTypes.size, 0)!!
|
||||
}
|
||||
}
|
||||
|
||||
internal fun ContextUtils.numParameters(functionType: LLVMTypeRef) : Int {
|
||||
// Note that type is usually function pointer, so we have to dereference it.
|
||||
return LLVMCountParamTypes(LLVMGetElementType(functionType))!!
|
||||
@@ -176,7 +166,7 @@ internal fun ContextUtils.isObjectRef(value: LLVMValueRef): Boolean {
|
||||
return isObjectType(value.type)
|
||||
}
|
||||
|
||||
internal fun ContextUtils.isObjectType(type: LLVMTypeRef): Boolean {
|
||||
internal fun RuntimeAware.isObjectType(type: LLVMTypeRef): Boolean {
|
||||
return type == kObjHeaderPtr || type == kArrayHeaderPtr
|
||||
}
|
||||
|
||||
@@ -198,6 +188,7 @@ internal fun ContextUtils.externalGlobal(name: String, type: LLVMTypeRef): LLVMV
|
||||
val found = LLVMGetNamedGlobal(context.llvmModule, name)
|
||||
if (found != null) {
|
||||
assert (getGlobalType(found) == type)
|
||||
assert (LLVMGetInitializer(found) == null)
|
||||
return found
|
||||
} else {
|
||||
return LLVMAddGlobal(context.llvmModule, type, name)!!
|
||||
@@ -225,3 +216,9 @@ internal operator fun LLVMAttributeSet.contains(attribute: LLVMAttribute): Boole
|
||||
return (this and attribute.value) != 0
|
||||
}
|
||||
|
||||
fun getStructElements(type: LLVMTypeRef): List<LLVMTypeRef> {
|
||||
val count = LLVMCountStructElementTypes(type)
|
||||
return (0 until count).map {
|
||||
LLVMStructGetTypeAtIndex(type, it)!!
|
||||
}
|
||||
}
|
||||
-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
|
||||
|
||||
|
||||
import kotlinx.cinterop.*
|
||||
import llvm.*
|
||||
import org.jetbrains.kotlin.backend.konan.Context
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.*
|
||||
@@ -52,18 +50,6 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||
Int32(fieldsCount)
|
||||
)
|
||||
|
||||
// TODO: probably it should be moved out of this class and shared.
|
||||
private fun createStructFor(className: FqName, fields: List<PropertyDescriptor>): LLVMTypeRef? {
|
||||
val classType = LLVMStructCreateNamed(LLVMGetModuleContext(context.llvmModule), "kclass:" + className)
|
||||
val fieldTypes = fields.map { getLLVMType(it.returnType!!) }.toTypedArray()
|
||||
|
||||
memScoped {
|
||||
val fieldTypesNativeArrayPtr = allocArrayOf(*fieldTypes)[0].ptr
|
||||
LLVMStructSetBody(classType, fieldTypesNativeArrayPtr, fieldTypes.size, 0)
|
||||
}
|
||||
return classType
|
||||
}
|
||||
|
||||
private fun exportTypeInfoIfRequired(classDesc: ClassDescriptor, typeInfoGlobal: LLVMValueRef?) {
|
||||
val annot = classDesc.annotations.findAnnotation(FqName("konan.ExportTypeInfo"))
|
||||
if (annot != null) {
|
||||
@@ -97,11 +83,13 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||
|
||||
val className = classDesc.fqNameSafe
|
||||
|
||||
val classType = createStructFor(className, classDesc.fields)
|
||||
val llvmDeclarations = context.llvmDeclarations.forClass(classDesc)
|
||||
|
||||
val bodyType = llvmDeclarations.bodyType
|
||||
|
||||
val name = className.globalHash
|
||||
|
||||
val size = getInstanceSize(classType, className)
|
||||
val size = getInstanceSize(bodyType, className)
|
||||
|
||||
val superTypeOrAny = classDesc.getSuperClassOrAny()
|
||||
val superType = if (KotlinBuiltIns.isAny(classDesc)) NullPointer(runtime.typeInfoType)
|
||||
@@ -111,51 +99,39 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||
val interfacesPtr = staticData.placeGlobalConstArray("kintf:$className",
|
||||
pointerType(runtime.typeInfoType), interfaces)
|
||||
|
||||
val refFieldIndices = classDesc.fields.mapIndexedNotNull { index, field ->
|
||||
val type = field.returnType!!
|
||||
if (isObjectType(getLLVMType(type))) {
|
||||
index
|
||||
// TODO: reuse offsets obtained for 'fields' below
|
||||
val objOffsets = getStructElements(bodyType).mapIndexedNotNull { index, type ->
|
||||
if (isObjectType(type)) {
|
||||
LLVMOffsetOfElement(llvmTargetData, bodyType, index)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
// TODO: reuse offsets obtained for 'fields' below
|
||||
val objOffsets = refFieldIndices.map { LLVMOffsetOfElement(llvmTargetData, classType, it) }
|
||||
|
||||
val objOffsetsPtr = staticData.placeGlobalConstArray("krefs:$className", int32Type,
|
||||
objOffsets.map { Int32(it.toInt()) })
|
||||
|
||||
val fields = classDesc.fields.mapIndexed { index, field ->
|
||||
val fields = llvmDeclarations.fields.mapIndexed { index, field ->
|
||||
// Note: using FQ name because a class may have multiple fields with the same name due to property overriding
|
||||
val nameSignature = field.fqNameSafe.localHash // FIXME: add signature
|
||||
val fieldOffset = LLVMOffsetOfElement(llvmTargetData, classType, index)
|
||||
val fieldOffset = LLVMOffsetOfElement(llvmTargetData, bodyType, index)
|
||||
FieldTableRecord(nameSignature, fieldOffset.toInt())
|
||||
}.sortedBy { it.nameSignature.value }
|
||||
|
||||
val fieldsPtr = staticData.placeGlobalConstArray("kfields:$className",
|
||||
runtime.fieldTableRecordType, fields)
|
||||
|
||||
val vtableEntries: List<ConstValue>
|
||||
val methods: List<ConstValue>
|
||||
|
||||
if (!classDesc.isAbstract()) {
|
||||
// TODO: compile-time resolution limits binary compatibility
|
||||
vtableEntries = classDesc.vtableEntries.map { it.implementation.entryPointAddress }
|
||||
|
||||
methods = classDesc.methodTableEntries.map {
|
||||
val methods = if (!classDesc.isAbstract()) {
|
||||
classDesc.methodTableEntries.map {
|
||||
val nameSignature = it.functionName.localHash
|
||||
// TODO: compile-time resolution limits binary compatibility
|
||||
val methodEntryPoint = it.implementation.entryPointAddress
|
||||
val methodEntryPoint = it.resolveFakeOverride().original.entryPointAddress
|
||||
MethodTableRecord(nameSignature, methodEntryPoint)
|
||||
}.sortedBy { it.nameSignature.value }
|
||||
} else {
|
||||
vtableEntries = emptyList()
|
||||
methods = emptyList()
|
||||
emptyList()
|
||||
}
|
||||
|
||||
assert (vtableEntries.size == classDesc.vtableSize)
|
||||
|
||||
val vtable = ConstArray(int8TypePtr, vtableEntries)
|
||||
|
||||
val methodsPtr = staticData.placeGlobalConstArray("kmethods:$className",
|
||||
runtime.methodTableRecordType, methods)
|
||||
|
||||
@@ -166,9 +142,19 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||
methodsPtr, methods.size,
|
||||
fieldsPtr, if (classDesc.isInterface) -1 else fields.size)
|
||||
|
||||
val typeInfoGlobal = classDesc.typeInfoWithVtable.llvm // TODO: it is a hack
|
||||
LLVMSetInitializer(typeInfoGlobal, Struct(typeInfo, vtable).llvm)
|
||||
LLVMSetGlobalConstant(typeInfoGlobal, 1)
|
||||
val typeInfoGlobal = llvmDeclarations.typeInfoGlobal
|
||||
|
||||
val typeInfoGlobalValue = if (classDesc.isAbstract()) {
|
||||
typeInfo
|
||||
} else {
|
||||
// TODO: compile-time resolution limits binary compatibility
|
||||
val vtableEntries = classDesc.vtableEntries.map { it.resolveFakeOverride().original.entryPointAddress }
|
||||
val vtable = ConstArray(int8TypePtr, vtableEntries)
|
||||
Struct(typeInfo, vtable)
|
||||
}
|
||||
|
||||
typeInfoGlobal.setInitializer(typeInfoGlobalValue)
|
||||
typeInfoGlobal.setConstant(true)
|
||||
|
||||
exportTypeInfoIfRequired(classDesc, classDesc.llvmTypeInfoPtr)
|
||||
}
|
||||
|
||||
+5
@@ -3,6 +3,10 @@ package org.jetbrains.kotlin.backend.konan.llvm
|
||||
import kotlinx.cinterop.*
|
||||
import llvm.*
|
||||
|
||||
interface RuntimeAware {
|
||||
val runtime: Runtime
|
||||
}
|
||||
|
||||
class Runtime(private val bitcodeFile: String) {
|
||||
val llvmModule: LLVMModuleRef
|
||||
|
||||
@@ -11,6 +15,7 @@ class Runtime(private val bitcodeFile: String) {
|
||||
|
||||
val bufRef = alloc<LLVMMemoryBufferRefVar>()
|
||||
val errorRef = allocPointerTo<CInt8Var>()
|
||||
|
||||
val res = LLVMCreateMemoryBufferWithContentsOfFile(bitcodeFile, bufRef.ptr, errorRef.ptr)
|
||||
if (res != 0) {
|
||||
throw Error(errorRef.value?.asCString()?.toString())
|
||||
|
||||
+20
-20
@@ -15,34 +15,34 @@ internal class StaticData(override val context: Context): ContextUtils {
|
||||
class Global private constructor(val staticData: StaticData, val llvmGlobal: LLVMValueRef) {
|
||||
companion object {
|
||||
|
||||
private fun createLlvmGlobal(module: LLVMModuleRef, type: LLVMTypeRef, name: String): LLVMValueRef {
|
||||
val isUnnamed = (name == "") // LLVM will select the unique index and represent the global as `@idx`.
|
||||
private fun createLlvmGlobal(module: LLVMModuleRef,
|
||||
type: LLVMTypeRef,
|
||||
name: String,
|
||||
isExported: Boolean
|
||||
): LLVMValueRef {
|
||||
|
||||
if (!isUnnamed) {
|
||||
LLVMGetNamedGlobal(module, name)?.let { existingGlobal ->
|
||||
if (getGlobalType(existingGlobal) != type || LLVMGetInitializer(existingGlobal) != null) {
|
||||
throw IllegalArgumentException("Global '$name' already exists")
|
||||
} else {
|
||||
return existingGlobal
|
||||
}
|
||||
}
|
||||
if (isExported && LLVMGetNamedGlobal(module, name) != null) {
|
||||
throw IllegalArgumentException("Global '$name' already exists")
|
||||
}
|
||||
|
||||
val llvmGlobal = LLVMAddGlobal(module, type, name)!!
|
||||
|
||||
if (isUnnamed) {
|
||||
// Currently using unnamed globals may result in link errors due to symbol redefinition;
|
||||
// apply the workaround:
|
||||
LLVMSetLinkage(llvmGlobal, LLVMLinkage.LLVMPrivateLinkage)
|
||||
if (!isExported) {
|
||||
LLVMSetLinkage(llvmGlobal, LLVMLinkage.LLVMInternalLinkage)
|
||||
}
|
||||
|
||||
return llvmGlobal
|
||||
}
|
||||
|
||||
fun create(staticData: StaticData, type: LLVMTypeRef, name: String): Global {
|
||||
fun create(staticData: StaticData, type: LLVMTypeRef, name: String, isExported: Boolean): Global {
|
||||
val module = staticData.context.llvmModule
|
||||
|
||||
val llvmGlobal = createLlvmGlobal(module!!, type, name)
|
||||
val isUnnamed = (name == "") // LLVM will select the unique index and represent the global as `@idx`.
|
||||
if (isUnnamed && isExported) {
|
||||
throw IllegalArgumentException("unnamed global can't be exported")
|
||||
}
|
||||
|
||||
val llvmGlobal = createLlvmGlobal(module!!, type, name, isExported)
|
||||
return Global(staticData, llvmGlobal)
|
||||
}
|
||||
}
|
||||
@@ -110,15 +110,15 @@ internal class StaticData(override val context: Context): ContextUtils {
|
||||
*
|
||||
* It is external until explicitly initialized with [Global.setInitializer].
|
||||
*/
|
||||
fun createGlobal(type: LLVMTypeRef, name: String): Global {
|
||||
return Global.create(this, type, name)
|
||||
fun createGlobal(type: LLVMTypeRef, name: String, isExported: Boolean = false): Global {
|
||||
return Global.create(this, type, name, isExported)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates [Global] with given name and value.
|
||||
*/
|
||||
fun placeGlobal(name: String, initializer: ConstValue): Global {
|
||||
val global = createGlobal(initializer.llvmType, name)
|
||||
fun placeGlobal(name: String, initializer: ConstValue, isExported: Boolean = false): Global {
|
||||
val global = createGlobal(initializer.llvmType, name, isExported)
|
||||
global.setInitializer(initializer)
|
||||
return global
|
||||
}
|
||||
|
||||
+14
-7
@@ -117,16 +117,23 @@ internal fun StaticData.createArrayList(elementType: TypeProjection, array: Cons
|
||||
return createKotlinObject(type, body)
|
||||
}
|
||||
|
||||
private val theUnitInstanceName = "kobj:kotlin.Unit"
|
||||
|
||||
internal fun StaticData.createUnitInstance(descriptor: ClassDescriptor): ConstPointer {
|
||||
internal fun StaticData.createUnitInstance(descriptor: ClassDescriptor,
|
||||
bodyType: LLVMTypeRef,
|
||||
typeInfo: ConstPointer
|
||||
): ConstPointer {
|
||||
assert (descriptor.isUnit())
|
||||
assert (descriptor.fields.isEmpty())
|
||||
val typeInfo = descriptor.defaultType.typeInfoPtr!!
|
||||
assert (getStructElements(bodyType).isEmpty())
|
||||
val objHeader = objHeader(typeInfo)
|
||||
val global = this.placeGlobal(theUnitInstanceName, objHeader)
|
||||
val global = this.placeGlobal(theUnitInstanceName, objHeader, isExported = true)
|
||||
return global.pointer
|
||||
}
|
||||
|
||||
internal val ContextUtils.theUnitInstanceRef: ConstPointer
|
||||
get() = constPointer(externalGlobal(theUnitInstanceName, context.llvm.runtime.objHeaderType))
|
||||
get() {
|
||||
val unitDescriptor = context.builtIns.unit
|
||||
return if (isExternal(unitDescriptor)) {
|
||||
constPointer(externalGlobal(theUnitInstanceName, context.llvm.runtime.objHeaderType))
|
||||
} else {
|
||||
context.llvmDeclarations.getUnitInstanceRef()
|
||||
}
|
||||
}
|
||||
+23
-14
@@ -2,6 +2,7 @@ package org.jetbrains.kotlin.backend.konan.llvm
|
||||
|
||||
import llvm.*
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.types.*
|
||||
|
||||
|
||||
@@ -19,20 +20,25 @@ internal class VariableManager(val codegen: CodeGenerator) {
|
||||
override fun toString() = (if (refSlot) "refslot" else "slot") + " for ${address}"
|
||||
}
|
||||
|
||||
class ValueRecord(val value: LLVMValueRef, val descriptor: ValueDescriptor) : Record {
|
||||
class ValueRecord(val value: LLVMValueRef, val name: Name) : Record {
|
||||
override fun load() : LLVMValueRef = value
|
||||
override fun store(value: LLVMValueRef) = throw Error("writing to immutable: ${descriptor}")
|
||||
override fun address() : LLVMValueRef = throw Error("no address for: ${descriptor}")
|
||||
override fun toString() = "value of ${value} from ${descriptor}"
|
||||
override fun store(value: LLVMValueRef) = throw Error("writing to immutable: ${name}")
|
||||
override fun address() : LLVMValueRef = throw Error("no address for: ${name}")
|
||||
override fun toString() = "value of ${value} from ${name}"
|
||||
}
|
||||
|
||||
data class ScopedVariable private constructor(val descriptor: ValueDescriptor, val context: CodeContext)
|
||||
{
|
||||
constructor(scoped: Pair<ValueDescriptor, CodeContext>) : this(scoped.first, scoped.second)
|
||||
}
|
||||
|
||||
val variables: ArrayList<Record> = arrayListOf()
|
||||
val descriptors: HashMap<Pair<ValueDescriptor, CodeContext>, Int> = hashMapOf()
|
||||
val contextVariablesToIndex: HashMap<ScopedVariable, Int> = hashMapOf()
|
||||
|
||||
// Clears inner state of variable manager.
|
||||
fun clear() {
|
||||
variables.clear()
|
||||
descriptors.clear()
|
||||
contextVariablesToIndex.clear()
|
||||
}
|
||||
|
||||
fun createVariable(scoped: Pair<VariableDescriptor, CodeContext>, value: LLVMValueRef? = null) : Int {
|
||||
@@ -51,14 +57,15 @@ internal class VariableManager(val codegen: CodeGenerator) {
|
||||
fun createMutable(scoped: Pair<VariableDescriptor, CodeContext>,
|
||||
isVar: Boolean, value: LLVMValueRef? = null) : Int {
|
||||
val descriptor = scoped.first
|
||||
assert(descriptors.get(scoped) == null)
|
||||
val scopedVariable = ScopedVariable(scoped)
|
||||
assert(!contextVariablesToIndex.contains(scopedVariable))
|
||||
val index = variables.size
|
||||
val type = codegen.getLLVMType(descriptor.type)
|
||||
val slot = codegen.alloca(type, descriptor.name.asString())
|
||||
val slot = codegen.alloca(type, scopedVariable.descriptor.name.asString())
|
||||
if (value != null)
|
||||
codegen.storeAnyLocal(value, slot)
|
||||
variables.add(SlotRecord(slot, codegen.isObjectType(type), isVar))
|
||||
descriptors[scoped] = index
|
||||
contextVariablesToIndex[scopedVariable] = index
|
||||
return index
|
||||
}
|
||||
|
||||
@@ -83,16 +90,18 @@ internal class VariableManager(val codegen: CodeGenerator) {
|
||||
}
|
||||
|
||||
fun createImmutable(scoped: Pair<ValueDescriptor, CodeContext>, value: LLVMValueRef) : Int {
|
||||
val descriptor = scoped.first
|
||||
assert(descriptors.get(scoped) == null)
|
||||
val scopedVariable = ScopedVariable(scoped)
|
||||
if(contextVariablesToIndex.containsKey(scopedVariable))
|
||||
throw Error("${scopedVariable.descriptor} is already defined")
|
||||
assert(!contextVariablesToIndex.containsKey(scopedVariable))
|
||||
val index = variables.size
|
||||
variables.add(ValueRecord(value, descriptor))
|
||||
descriptors[scoped] = index
|
||||
variables.add(ValueRecord(value, scopedVariable.descriptor.name))
|
||||
contextVariablesToIndex[scopedVariable] = index
|
||||
return index
|
||||
}
|
||||
|
||||
fun indexOf(scoped: Pair<ValueDescriptor, CodeContext>) : Int {
|
||||
return descriptors.getOrElse(scoped) { -1 }
|
||||
return contextVariablesToIndex.getOrElse(ScopedVariable(scoped)) { -1 }
|
||||
}
|
||||
|
||||
fun addressOf(index: Int): LLVMValueRef {
|
||||
|
||||
+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.getKonanInternalFunctions
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.unboundCallableReferenceTypeOrNull
|
||||
import org.jetbrains.kotlin.descriptors.ParameterDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
||||
@@ -128,8 +129,17 @@ private class AutoboxingTransformer(val context: Context) : AbstractValueUsageTr
|
||||
return this.adaptIfNecessary(actualType, type)
|
||||
}
|
||||
|
||||
override fun IrExpression.useAsArgument(parameter: ParameterDescriptor): IrExpression {
|
||||
return this.useAsValue(parameter.original)
|
||||
override fun IrExpression.useAsDispatchReceiver(function: CallableDescriptor): IrExpression {
|
||||
return this.useAsArgument(function.original.dispatchReceiverParameter!!)
|
||||
}
|
||||
|
||||
override fun IrExpression.useAsExtensionReceiver(function: CallableDescriptor): IrExpression {
|
||||
return this.useAsArgument(function.original.extensionReceiverParameter!!)
|
||||
}
|
||||
|
||||
override fun IrExpression.useAsValueArgument(parameter: ValueParameterDescriptor): IrExpression {
|
||||
val function = parameter.containingDeclaration
|
||||
return this.useAsArgument(function.original.valueParameters[parameter.index])
|
||||
}
|
||||
|
||||
override fun IrExpression.useForField(field: PropertyDescriptor): IrExpression {
|
||||
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
package org.jetbrains.kotlin.backend.konan.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.DeclarationContainerLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.lower.createFunctionIrGenerator
|
||||
import org.jetbrains.kotlin.backend.common.lower.createFunctionIrBuilder
|
||||
import org.jetbrains.kotlin.backend.konan.*
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.*
|
||||
import org.jetbrains.kotlin.backend.konan.ir.*
|
||||
@@ -136,8 +136,8 @@ private class CallableReferencesUnbinder(val lower: CallableReferenceLowering,
|
||||
// Create new function descriptor:
|
||||
val newDescriptor = createSimpleFunctionImplTargetDescriptor(descriptor, unboundParams)
|
||||
|
||||
val generator = lower.context.createFunctionIrGenerator(newDescriptor)
|
||||
val blockBody = generator.irBlockBody(startOffset, endOffset) {
|
||||
val builder = lower.context.createFunctionIrBuilder(newDescriptor)
|
||||
val blockBody = builder.irBlockBody(startOffset, endOffset) {
|
||||
val boundArgsGet = irCall(simpleFunctionImplBoundArgsGetter).apply {
|
||||
dispatchReceiver = irGet(newDescriptor.valueParameters[0])
|
||||
}
|
||||
|
||||
+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.DeclarationContainerLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.lower.createFunctionIrGenerator
|
||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
import org.jetbrains.kotlin.backend.common.lower.createFunctionIrBuilder
|
||||
import org.jetbrains.kotlin.backend.common.lower.irBlockBody
|
||||
import org.jetbrains.kotlin.backend.konan.Context
|
||||
import org.jetbrains.kotlin.backend.konan.KonanPlatform
|
||||
import org.jetbrains.kotlin.backend.konan.ir.ir2string
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.impl.ClassConstructorDescriptorImpl
|
||||
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
|
||||
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
@@ -18,10 +19,11 @@ import org.jetbrains.kotlin.ir.declarations.IrDeclarationContainer
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOriginImpl
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
|
||||
import org.jetbrains.kotlin.ir.util.transformFlat
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||
@@ -49,6 +51,11 @@ class DefaultParameterStubGenerator internal constructor(val context: Context):
|
||||
|
||||
private fun lower(irFunction: IrFunction): List<IrFunction> {
|
||||
val bodies = mutableListOf<IrExpressionBody>()
|
||||
val functionDescriptor = irFunction.descriptor
|
||||
|
||||
if (hasNotDefaultParameters(functionDescriptor))
|
||||
return irFunction.singletonList()
|
||||
|
||||
irFunction.acceptChildrenVoid(object:IrElementVisitorVoid{
|
||||
override fun visitExpressionBody(body: IrExpressionBody) {
|
||||
bodies.add(body)
|
||||
@@ -59,21 +66,29 @@ class DefaultParameterStubGenerator internal constructor(val context: Context):
|
||||
}
|
||||
})
|
||||
|
||||
val functionDescriptor = irFunction.descriptor
|
||||
log("detected ${functionDescriptor.name.asString()} has got #${bodies.size} default expressions")
|
||||
functionDescriptor.overriddenDescriptors.forEach { context.log("DEFAULT-REPLACER: $it") }
|
||||
if (bodies.isNotEmpty()) {
|
||||
val (descriptor, mask, extension, dispatch) = functionDescriptor.generateDefaultsDescriptor()
|
||||
val generator = context.createFunctionIrGenerator(descriptor)
|
||||
val builder = generator.createIrBuilder()
|
||||
builder.apply {
|
||||
startOffset = irFunction.startOffset
|
||||
endOffset = irFunction.endOffset
|
||||
}
|
||||
val body = generator.irBlockBody {
|
||||
val description = functionDescriptor.getOrCreateDefaultsDescription(context)
|
||||
val builder = context.createFunctionIrBuilder(description.function)
|
||||
val body = builder.irBlockBody(irFunction) {
|
||||
val params = mutableListOf<VariableDescriptor>()
|
||||
val variables = mutableMapOf<VariableDescriptor, VariableDescriptor>()
|
||||
|
||||
val newExtensionReceiver =
|
||||
if (description.function.extensionReceiverParameter == null) {
|
||||
null
|
||||
} else {
|
||||
IrGetValueImpl(
|
||||
startOffset = irFunction.startOffset,
|
||||
endOffset = irFunction.endOffset,
|
||||
descriptor = description.function.extensionReceiverParameter!!,
|
||||
origin = null
|
||||
)
|
||||
}
|
||||
|
||||
for (valueParameter in functionDescriptor.valueParameters) {
|
||||
val parameterDescriptor = descriptor.valueParameters[valueParameter.index]
|
||||
val parameterDescriptor = description.function.valueParameters[valueParameter.index]
|
||||
if (valueParameter.hasDefaultValue()) {
|
||||
val variable = scope.createTemporaryVariable(
|
||||
irExpression = nullConst(valueParameter.type)!!,
|
||||
@@ -82,13 +97,15 @@ class DefaultParameterStubGenerator internal constructor(val context: Context):
|
||||
params.add(variableDescriptor)
|
||||
+variable
|
||||
val condition = irNotEquals(irCall(intAnd).apply {
|
||||
dispatchReceiver = irGet(mask)
|
||||
dispatchReceiver = irGet(description.mask)
|
||||
putValueArgument(0, irInt(1 shl valueParameter.index))
|
||||
}, irInt(0))
|
||||
val exprBody = getDefaultParameterExpressionBody(irFunction, valueParameter)
|
||||
/* Use previously calculated values in next expression. */
|
||||
exprBody.expression.transformChildrenVoid(object:IrElementTransformerVoid() {
|
||||
override fun visitGetValue(expression: IrGetValue): IrExpression {
|
||||
if (expression.descriptor == functionDescriptor.extensionReceiverParameter)
|
||||
return newExtensionReceiver!!
|
||||
if (!variables.containsKey(expression.descriptor))
|
||||
return expression
|
||||
return irGet(variables[expression.descriptor] as VariableDescriptor)
|
||||
@@ -97,36 +114,49 @@ class DefaultParameterStubGenerator internal constructor(val context: Context):
|
||||
/* Mapping calculated values with its origin variables. */
|
||||
variables.put(valueParameter, variableDescriptor)
|
||||
+irIfThenElse(
|
||||
type = KonanPlatform.builtIns.unitType,
|
||||
type = KonanPlatform.builtIns.unitType,
|
||||
condition = condition,
|
||||
thenPart = irSetVar(variableDescriptor, exprBody.expression),
|
||||
elsePart = irSetVar(variableDescriptor, irGet(parameterDescriptor)))
|
||||
|
||||
thenPart = irSetVar(variableDescriptor, exprBody.expression),
|
||||
elsePart = irSetVar(variableDescriptor, irGet(parameterDescriptor)))
|
||||
} else {
|
||||
params.add(parameterDescriptor)
|
||||
}
|
||||
}
|
||||
+ irReturn(irCall(functionDescriptor).apply {
|
||||
if (functionDescriptor.dispatchReceiverParameter != null) {
|
||||
dispatchReceiver = irGet(dispatch!!)
|
||||
}
|
||||
if (functionDescriptor.extensionReceiverParameter != null) {
|
||||
extensionReceiver = irGet(extension!!)
|
||||
}
|
||||
params.forEachIndexed { i, variable ->
|
||||
putValueArgument(i, irGet(variable))
|
||||
}
|
||||
})
|
||||
if (functionDescriptor !is ClassConstructorDescriptor) {
|
||||
+irReturn(irCall(functionDescriptor).apply {
|
||||
if (functionDescriptor.dispatchReceiverParameter != null) {
|
||||
dispatchReceiver = irThis()
|
||||
}
|
||||
if (functionDescriptor.extensionReceiverParameter != null) {
|
||||
extensionReceiver = newExtensionReceiver
|
||||
}
|
||||
params.forEachIndexed { i, variable ->
|
||||
putValueArgument(i, irGet(variable))
|
||||
}
|
||||
})
|
||||
} else {
|
||||
+ irReturn(IrDelegatingConstructorCallImpl(
|
||||
startOffset = irFunction.startOffset,
|
||||
endOffset = irFunction.endOffset,
|
||||
descriptor = functionDescriptor
|
||||
).apply {
|
||||
params.forEachIndexed { i, variable ->
|
||||
putValueArgument(i, irGet(variable))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
// TODO: replace irFunction with new one without expression bodies.
|
||||
return listOf(irFunction, IrFunctionImpl(
|
||||
irFunction.startOffset ,
|
||||
irFunction.startOffset,
|
||||
irFunction.endOffset,
|
||||
DECLARATION_ORIGIN_FUNCTION_FOR_DEFAULT_PARAMETER,
|
||||
descriptor, body))
|
||||
description.function, body))
|
||||
}
|
||||
return irFunction.singletonList()
|
||||
}
|
||||
|
||||
private fun log(msg:String) = context.log("DEFAULT-REPLACER: $msg")
|
||||
}
|
||||
|
||||
private fun getDefaultParameterExpressionBody(irFunction: IrFunction, valueParameter: ValueParameterDescriptor):IrExpressionBody {
|
||||
@@ -149,81 +179,143 @@ private fun nullConst(type: KotlinType): IrExpression? {
|
||||
class DefaultParameterInjector internal constructor(val context: Context): BodyLoweringPass {
|
||||
override fun lower(irBody: IrBody) {
|
||||
irBody.transformChildrenVoid(object : IrElementTransformerVoid() {
|
||||
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall): IrExpression {
|
||||
super.visitDelegatingConstructorCall(expression)
|
||||
val descriptor = expression.descriptor
|
||||
if (hasNotDefaultParameters(descriptor))
|
||||
return expression
|
||||
val argumentsCount = argumentCount(expression)
|
||||
if (argumentsCount == descriptor.valueParameters.size)
|
||||
return expression
|
||||
val (desc, params) = parametersForCall(expression)
|
||||
return IrDelegatingConstructorCallImpl(
|
||||
startOffset = irBody.startOffset,
|
||||
endOffset = irBody.endOffset,
|
||||
descriptor = desc.function as ClassConstructorDescriptor)
|
||||
.apply {
|
||||
params.forEach {
|
||||
log("call::params@${it.first.index}/${it.first.name.asString()}: ${ir2string(it.second)}")
|
||||
putValueArgument(it.first.index, it.second)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitCall(expression: IrCall): IrExpression {
|
||||
super.visitCall(expression)
|
||||
val descriptor = expression.descriptor
|
||||
if (descriptor.valueParameters.none{it.hasDefaultValue()})
|
||||
val functionDescriptor = expression.descriptor as FunctionDescriptor
|
||||
|
||||
if (hasNotDefaultParameters(functionDescriptor))
|
||||
return expression
|
||||
|
||||
val argumentsCount = argumentCount(expression)
|
||||
if (argumentsCount == functionDescriptor.valueParameters.size)
|
||||
return expression
|
||||
val (desc, params) = parametersForCall(expression)
|
||||
return IrCallImpl(
|
||||
startOffset = irBody.startOffset,
|
||||
endOffset = irBody.endOffset,
|
||||
type = desc.function.returnType!!,
|
||||
descriptor = desc.function,
|
||||
typeArguments = null)
|
||||
.apply {
|
||||
params.forEach {
|
||||
log("call::params@${it.first.index}/${it.first.name.asString()}: ${ir2string(it.second)}")
|
||||
putValueArgument(it.first.index, it.second)
|
||||
}
|
||||
expression.extensionReceiver?.apply{
|
||||
extensionReceiver = expression.extensionReceiver
|
||||
}
|
||||
expression.dispatchReceiver?.apply {
|
||||
dispatchReceiver = expression.dispatchReceiver
|
||||
}
|
||||
log("call::extension@: ${ir2string(expression.extensionReceiver)}")
|
||||
log("call::dispatch@: ${ir2string(expression.dispatchReceiver)}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun parametersForCall(expression: IrMemberAccessExpression): Pair<DefaultParameterDescription, List<Pair<ValueParameterDescriptor, IrExpression?>>> {
|
||||
var maskValue = 0
|
||||
val rawDescriptor = expression.descriptor as FunctionDescriptor
|
||||
val descriptor = rawDescriptor.overriddenDescriptors.firstOrNull()?:rawDescriptor
|
||||
val desc = descriptor.getOrCreateDefaultsDescription(context)
|
||||
descriptor.valueParameters.forEach {
|
||||
log("descriptor::${descriptor.name.asString()}#${it.index}: ${it.name.asString()}")
|
||||
}
|
||||
val params = descriptor.valueParameters.mapIndexed { i, _ ->
|
||||
val valueArgument = expression.getValueArgument(i)
|
||||
if (valueArgument == null) maskValue = maskValue or (1 shl i)
|
||||
val valueParameterDescriptor = desc.function.valueParameters[i]
|
||||
val pair = valueParameterDescriptor to (valueArgument ?: nullConst(valueParameterDescriptor.type))
|
||||
return@mapIndexed pair
|
||||
} + (desc.mask to IrConstImpl<Int>(
|
||||
startOffset = irBody.startOffset,
|
||||
endOffset = irBody.endOffset,
|
||||
type = KonanPlatform.builtIns.intType,
|
||||
kind = IrConstKind.Int,
|
||||
value = maskValue))
|
||||
params.forEach {
|
||||
log("descriptor::${desc.function.name.asString()}#${it.first.index}: ${it.first.name.asString()}")
|
||||
}
|
||||
return Pair(desc, params)
|
||||
}
|
||||
|
||||
private fun argumentCount(expression: IrMemberAccessExpression): Int {
|
||||
var argumentsCount = 0
|
||||
expression.acceptChildrenVoid(object:IrElementVisitorVoid{
|
||||
expression.acceptChildrenVoid(object : IrElementVisitorVoid {
|
||||
override fun visitElement(element: IrElement) {
|
||||
argumentsCount++
|
||||
}
|
||||
})
|
||||
if (descriptor.dispatchReceiverParameter != null || descriptor.extensionReceiverParameter != null)
|
||||
val descriptor = expression.descriptor
|
||||
if (descriptor !is ConstructorDescriptor && (descriptor.dispatchReceiverParameter != null || descriptor.extensionReceiverParameter != null))
|
||||
argumentsCount--
|
||||
if (argumentsCount == descriptor.valueParameters.size)
|
||||
return expression
|
||||
var maskValue = 0
|
||||
val functionDescriptor = descriptor as FunctionDescriptor
|
||||
val (defaultFunctiondescriptor, mask, extension, dispatch) = functionDescriptor.generateDefaultsDescriptor()
|
||||
val params = descriptor.valueParameters.mapIndexed { i, it ->
|
||||
if (expression.getValueArgument(i) == null) maskValue = maskValue or (1 shl i)
|
||||
val valueParameterDescriptor = defaultFunctiondescriptor.valueParameters[i]
|
||||
return@mapIndexed valueParameterDescriptor to (expression.getValueArgument(i) ?: nullConst(valueParameterDescriptor.type))
|
||||
} + (mask to IrConstImpl<Int>(
|
||||
startOffset = irBody.startOffset,
|
||||
endOffset = irBody.endOffset,
|
||||
type = KonanPlatform.builtIns.intType,
|
||||
kind = IrConstKind.Int,
|
||||
value = maskValue))
|
||||
return IrCallImpl(
|
||||
startOffset = irBody.startOffset,
|
||||
endOffset = irBody.endOffset,
|
||||
type = descriptor.returnType!!,
|
||||
descriptor = defaultFunctiondescriptor,
|
||||
typeArguments = null)
|
||||
.apply {
|
||||
params.forEach {
|
||||
putValueArgument(it.first.index, it.second)
|
||||
}
|
||||
extension?.apply {
|
||||
putValueArgument(extension.index, expression.extensionReceiver)
|
||||
}
|
||||
dispatch?.apply {
|
||||
putValueArgument(dispatch.index, expression.dispatchReceiver)
|
||||
}
|
||||
}
|
||||
return argumentsCount
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun log(msg: String) = context.log("DEFAULT-INJECTOR: $msg")
|
||||
}
|
||||
|
||||
private fun hasNotDefaultParameters(descriptor: CallableDescriptor) = descriptor.valueParameters.none { it.hasDefaultValue() }
|
||||
|
||||
val intDesctiptor = DescriptorUtils.getClassDescriptorForType(KonanPlatform.builtIns.intType)
|
||||
val intAnd = DescriptorUtils.getFunctionByName(intDesctiptor.unsubstitutedMemberScope, Name.identifier("and"))
|
||||
|
||||
data class DefaultParameterDescriptor(val function: FunctionDescriptor, val mask:ValueParameterDescriptor,
|
||||
val extensionReceiver:ValueParameterDescriptor?, val dispatchReceiver: ValueParameterDescriptor?)
|
||||
data class DefaultParameterDescription(val function: FunctionDescriptor, val mask:ValueParameterDescriptor,
|
||||
val hasExtensionReceiver:Boolean, val hasDispatchReceiver: Boolean)
|
||||
|
||||
fun FunctionDescriptor.generateDefaultsDescriptor():DefaultParameterDescriptor {
|
||||
val name = Name.identifier("${this.name.asString()}\$default")
|
||||
val descriptor = SimpleFunctionDescriptorImpl.create(this.containingDeclaration, Annotations.EMPTY,
|
||||
name, CallableMemberDescriptor.Kind.SYNTHESIZED, SourceElement.NO_SOURCE)
|
||||
val maskVariable = valueParameter(descriptor, valueParameters.size, "__\$mask\$__", KonanPlatform.builtIns.intType)
|
||||
|
||||
var extensionReceiver:ValueParameterDescriptor? = null
|
||||
var index = valueParameters.size + 1
|
||||
extensionReceiverParameter?.let {
|
||||
extensionReceiver = valueParameter(descriptor, index++, "__\$ext_receiver\$__", extensionReceiverParameter!!.type)
|
||||
private fun FunctionDescriptor.getOrCreateDefaultsDescription(context: Context): DefaultParameterDescription {
|
||||
return context.ir.defaultParameterDescriptions.getOrPut(this) {
|
||||
this.generateDefaultsDescription(context)
|
||||
}
|
||||
var dispatchReceiver:ValueParameterDescriptor? = null
|
||||
dispatchReceiverParameter?.let {
|
||||
dispatchReceiver = valueParameter(descriptor, index++, "__\$dispatch_receiver\$__", dispatchReceiverParameter!!.type)
|
||||
}
|
||||
|
||||
private fun FunctionDescriptor.generateDefaultsDescription(context: Context): DefaultParameterDescription {
|
||||
val descriptor = when (this){
|
||||
is ConstructorDescriptor -> DefaultParameterClassConstructorDescriptor(
|
||||
containingDeclaration = this.containingDeclaration as ClassDescriptor,
|
||||
annotations = annotations,
|
||||
original = null,
|
||||
isPrimary = false,
|
||||
source = SourceElement.NO_SOURCE,
|
||||
kind = CallableMemberDescriptor.Kind.SYNTHESIZED)
|
||||
|
||||
is FunctionDescriptor -> SimpleFunctionDescriptorImpl.create(
|
||||
/* containingDeclaration = */ this.containingDeclaration,
|
||||
/* annotations = */ Annotations.EMPTY,
|
||||
/* name = */ name,
|
||||
/* kind = */ CallableMemberDescriptor.Kind.SYNTHESIZED,
|
||||
/* source = */ SourceElement.NO_SOURCE)
|
||||
else -> TODO("FIXME!!!")
|
||||
}
|
||||
|
||||
val parameterList = mutableListOf(*valueParameters.map{
|
||||
if (it.hasDefaultValue()) ValueParameterDescriptorImpl(
|
||||
val parameterList = mutableListOf<ValueParameterDescriptor>()
|
||||
|
||||
val maskVariable = valueParameter(descriptor, valueParameters.size , "__\$mask\$__", KonanPlatform.builtIns.intType)
|
||||
|
||||
parameterList += valueParameters.map{
|
||||
ValueParameterDescriptorImpl(
|
||||
containingDeclaration = it.containingDeclaration,
|
||||
original = it.original,
|
||||
index = it.index,
|
||||
@@ -235,21 +327,22 @@ fun FunctionDescriptor.generateDefaultsDescriptor():DefaultParameterDescriptor {
|
||||
isNoinline = it.isNoinline,
|
||||
varargElementType = it.varargElementType,
|
||||
source = it.source)
|
||||
else it
|
||||
}.toTypedArray())
|
||||
}
|
||||
|
||||
parameterList.add(maskVariable)
|
||||
if (extensionReceiver != null) parameterList.add(extensionReceiver)
|
||||
if (dispatchReceiver != null) parameterList.add(dispatchReceiver)
|
||||
descriptor.initialize(
|
||||
/* receiverParameterType = */ null,
|
||||
/* dispatchReceiverParameterType = */ null,
|
||||
/* receiverParameterType = */ extensionReceiverParameter?.type,
|
||||
/* dispatchReceiverParameter = */ dispatchReceiverParameter,
|
||||
/* typeParameters = */ typeParameters,
|
||||
/* unsubstitutedValueParameters = */ parameterList,
|
||||
/* unsubstitutedReturnType = */ returnType,
|
||||
/* modality = */ this.modality,
|
||||
/* modality = */ Modality.FINAL,
|
||||
/* visibility = */ this.visibility)
|
||||
return DefaultParameterDescriptor(descriptor, maskVariable, extensionReceiver, dispatchReceiver)
|
||||
return DefaultParameterDescription(
|
||||
function = descriptor,
|
||||
mask = maskVariable,
|
||||
hasDispatchReceiver = (extensionReceiverParameter != null),
|
||||
hasExtensionReceiver = (dispatchReceiverParameter != null))
|
||||
}
|
||||
|
||||
private fun valueParameter(descriptor: FunctionDescriptor, index: Int, name: String, type: KotlinType):ValueParameterDescriptor {
|
||||
@@ -266,4 +359,21 @@ private fun valueParameter(descriptor: FunctionDescriptor, index: Int, name: Str
|
||||
varargElementType = null,
|
||||
source = SourceElement.NO_SOURCE
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* This descriptor overrides name property, for class constructor : instead of <init> -> <init>$defeult symbol.
|
||||
*/
|
||||
class DefaultParameterClassConstructorDescriptor(
|
||||
containingDeclaration: ClassDescriptor,
|
||||
original: ConstructorDescriptor?,
|
||||
annotations: Annotations,
|
||||
isPrimary: Boolean,
|
||||
kind: CallableMemberDescriptor.Kind,
|
||||
source: SourceElement
|
||||
) : ClassConstructorDescriptorImpl(containingDeclaration, original, annotations, isPrimary, kind, source) {
|
||||
|
||||
override fun getName(): Name {
|
||||
return Name.identifier("${super.getName().asString()}\$default")
|
||||
}
|
||||
}
|
||||
+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
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
fun inlineFunction(irCall: IrCall): IrBlock {
|
||||
|
||||
val functionDescriptor = irCall.descriptor as FunctionDescriptor
|
||||
val functionDeclaration = context.ir.moduleIndex.functions[functionDescriptor] // Get FunctionDeclaration by FunctionDescriptor.
|
||||
val functionDeclaration = context.ir.originalModuleIndex
|
||||
.functions[functionDescriptor] // Get FunctionDeclaration by FunctionDescriptor.
|
||||
val copyFuncDeclaration = functionDeclaration!!.accept(DeepCopyIrTree(), null) as IrFunction // Create copy of the function.
|
||||
|
||||
val startOffset = copyFuncDeclaration.startOffset
|
||||
@@ -111,11 +111,11 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoid(
|
||||
|
||||
override fun visitCall(expression: IrCall): IrExpression {
|
||||
val functionDescriptor = expression.descriptor as FunctionDescriptor
|
||||
if (!functionDescriptor.isInline) return super.visitCall(expression) // Function is not to be inlined - do nothing.
|
||||
if (!functionDescriptor.isInline) return super.visitCall(expression) // Function is not to be inlined - do nothing.
|
||||
|
||||
val functionDeclaration = context.ir.moduleIndex.functions[functionDescriptor] // Get FunctionDeclaration by FunctionDescriptor.
|
||||
if (functionDeclaration == null) return super.visitCall(expression) // Function is declared in another module.
|
||||
return inlineFunction(expression) // Return newly created IrBlock instead of IrCall.
|
||||
val functionDeclaration = context.ir.originalModuleIndex.functions[functionDescriptor] // Get FunctionDeclaration by FunctionDescriptor.
|
||||
if (functionDeclaration == null) return super.visitCall(expression) // Function is declared in another module.
|
||||
return inlineFunction(expression) // Return newly created IrBlock instead of IrCall.
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
+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
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.FunctionLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.lower.createFunctionIrGenerator
|
||||
import org.jetbrains.kotlin.backend.common.lower.irBlock
|
||||
import org.jetbrains.kotlin.backend.common.lower.*
|
||||
import org.jetbrains.kotlin.backend.konan.Context
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.getKonanInternalFunctions
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
@@ -13,6 +15,11 @@ import org.jetbrains.kotlin.ir.expressions.IrTypeOperator
|
||||
import org.jetbrains.kotlin.ir.expressions.IrTypeOperatorCall
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNullable
|
||||
|
||||
/**
|
||||
* This lowering pass lowers some [IrTypeOperatorCall]s.
|
||||
@@ -27,19 +34,83 @@ internal class TypeOperatorLowering(val context: Context) : FunctionLoweringPass
|
||||
|
||||
private class TypeOperatorTransformer(val context: Context, val function: FunctionDescriptor) : IrElementTransformerVoid() {
|
||||
|
||||
private val generator = context.createFunctionIrGenerator(function)
|
||||
private val builder = context.createFunctionIrBuilder(function)
|
||||
|
||||
val throwClassCastException by lazy {
|
||||
context.builtIns.getKonanInternalFunctions("ThrowClassCastException").single()
|
||||
}
|
||||
|
||||
override fun visitFunction(declaration: IrFunction): IrStatement {
|
||||
// ignore inner functions during this pass
|
||||
return declaration
|
||||
}
|
||||
|
||||
private fun KotlinType.erasure(): KotlinType {
|
||||
val descriptor = this.constructor.declarationDescriptor
|
||||
return when (descriptor) {
|
||||
is ClassDescriptor -> this
|
||||
is TypeParameterDescriptor -> {
|
||||
val upperBound = descriptor.upperBounds.singleOrNull() ?:
|
||||
TODO("$descriptor : ${descriptor.upperBounds}")
|
||||
|
||||
if (this.isMarkedNullable) {
|
||||
// `T?`
|
||||
upperBound.erasure().makeNullable()
|
||||
} else {
|
||||
upperBound.erasure()
|
||||
}
|
||||
}
|
||||
else -> TODO(this.toString())
|
||||
}
|
||||
}
|
||||
|
||||
private fun lowerCast(expression: IrTypeOperatorCall): IrExpression {
|
||||
builder.at(expression)
|
||||
val typeOperand = expression.typeOperand.erasure()
|
||||
|
||||
assert (!TypeUtils.hasNullableSuperType(typeOperand)) // So that `isNullable()` <=> `isMarkedNullable`.
|
||||
|
||||
// TODO: consider the case when expression type is wrong e.g. due to generics-related unchecked casts.
|
||||
|
||||
return when {
|
||||
expression.argument.type.isSubtypeOf(typeOperand) -> expression.argument
|
||||
|
||||
expression.argument.type.isNullable() -> {
|
||||
with (builder) {
|
||||
irLet(expression.argument) { argument ->
|
||||
irIfThenElse(
|
||||
type = expression.type,
|
||||
condition = irEqeqeq(irGet(argument), irNull()),
|
||||
|
||||
thenPart = if (typeOperand.isMarkedNullable)
|
||||
irNull()
|
||||
else
|
||||
irCall(throwClassCastException),
|
||||
|
||||
elsePart = irAs(irGet(argument), typeOperand.makeNotNullable())
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typeOperand.isMarkedNullable -> builder.irAs(expression.argument, typeOperand.makeNotNullable())
|
||||
|
||||
typeOperand == expression.typeOperand -> expression
|
||||
|
||||
else -> builder.irAs(expression.argument, typeOperand)
|
||||
}
|
||||
}
|
||||
|
||||
private fun KotlinType.isNullable() = TypeUtils.isNullableType(this)
|
||||
|
||||
private fun lowerSafeCast(expression: IrTypeOperatorCall): IrExpression {
|
||||
return generator.irBlock(expression) {
|
||||
val typeOperand = expression.typeOperand.erasure()
|
||||
|
||||
return builder.irBlock(expression) {
|
||||
+irLet(expression.argument) { variable ->
|
||||
irIfThenElse(expression.type,
|
||||
condition = irIs(irGet(variable), expression.typeOperand),
|
||||
thenPart = irImplicitCast(irGet(variable), expression.typeOperand),
|
||||
condition = irIs(irGet(variable), typeOperand),
|
||||
thenPart = irImplicitCast(irGet(variable), typeOperand),
|
||||
elsePart = irNull())
|
||||
}
|
||||
}
|
||||
@@ -50,6 +121,7 @@ private class TypeOperatorTransformer(val context: Context, val function: Functi
|
||||
|
||||
return when (expression.operator) {
|
||||
IrTypeOperator.SAFE_CAST -> lowerSafeCast(expression)
|
||||
IrTypeOperator.CAST -> lowerCast(expression)
|
||||
else -> expression
|
||||
}
|
||||
}
|
||||
|
||||
+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.
|
||||
|
||||
// macbook
|
||||
arch.osx = x86_64
|
||||
sysRoot.osx = target-sysroot-1-darwin-macos
|
||||
llvmHome.osx = clang+llvm-3.8.0-darwin-macos
|
||||
|
||||
sysRoot.linux = target-gcc-toolchain-3-linux-x86-64/x86_64-unknown-linux-gnu/sysroot
|
||||
llvmHome.linux = clang+llvm-3.8.0-linux-x86-64
|
||||
libGcc.linux = target-gcc-toolchain-3-linux-x86-64/lib/gcc/x86_64-unknown-linux-gnu/4.8.5
|
||||
|
||||
llvmLtoFlags.osx = -O3 -function-sections -exported-symbol=_main
|
||||
llvmLlcFlags.osx = -mtriple=x86_64-apple-macosx10.10.0
|
||||
linkerKonanFlags.osx = -lc++
|
||||
linkerOptimizationFlags.osx = -dead_strip
|
||||
macosVersionMin.osx = 10.10.0
|
||||
osVersionMin.osx = -macosx_version_min 10.10.0
|
||||
|
||||
// iphone
|
||||
arch.osx-ios = arm64
|
||||
sysRoot.osx-ios = target-sysroot-1-darwin-macos
|
||||
targetSysRoot.osx-ios = target-sysroot-1-darwin-ios
|
||||
llvmHome.osx-ios = clang+llvm-3.8.0-darwin-macos
|
||||
llvmLtoFlags.osx-ios = -O3 -function-sections -exported-symbol=_main
|
||||
llvmLlcFlags.osx-ios = -mtriple=arm64-apple-ios5.0.0
|
||||
linkerKonanFlags.osx-ios = -lc++
|
||||
linkerOptimizationFlags.osx-ios = -dead_strip
|
||||
osVersionMin.osx-ios = -iphoneos_version_min 5.0.0
|
||||
|
||||
// iphone_sim
|
||||
arch.osx-ios-sim = x86_64
|
||||
sysRoot.osx-ios-sim = target-sysroot-1-darwin-macos
|
||||
targetSysRoot.osx-ios-sim = target-sysroot-1-darwin-ios-sim
|
||||
llvmHome.osx-ios-sim = clang+llvm-3.8.0-darwin-macos
|
||||
llvmLtoFlags.osx-ios-sim = -O3 -function-sections -exported-symbol=_main
|
||||
llvmLlcFlags.osx-ios-sim = -mtriple=x86_64-apple-ios5.0.0
|
||||
linkerKonanFlags.osx-ios-sim = -lc++
|
||||
linkerOptimizationFlags.osx-ios-sim = -dead_strip
|
||||
osVersionMin.osx-ios-sim = -ios_simulator_version_min 5.0.0
|
||||
|
||||
// linux
|
||||
sysRoot.linux = target-gcc-toolchain-3-linux-x86-64/x86_64-unknown-linux-gnu/sysroot
|
||||
llvmHome.linux = clang+llvm-3.8.0-linux-x86-64
|
||||
libGcc.linux = target-gcc-toolchain-3-linux-x86-64/lib/gcc/x86_64-unknown-linux-gnu/4.8.5
|
||||
llvmLtoFlags.linux = -O3 -function-sections -exported-symbol=main
|
||||
llvmLlcFlags.linux = -march=x86-64
|
||||
linkerKonanFlags.linux = -Bstatic -lstdc++ -Bdynamic -ldl -lm -lpthread
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import groovy.json.JsonOutput
|
||||
import org.jetbrains.kotlin.*
|
||||
|
||||
configurations {
|
||||
cli_bc
|
||||
}
|
||||
@@ -6,107 +9,76 @@ dependencies {
|
||||
cli_bc project(path: ':backend.native', configuration: 'cli_bc')
|
||||
}
|
||||
|
||||
def testOutputRoot = rootProject.file("test.output").absolutePath
|
||||
def externalTestsDir = project.file("external")
|
||||
|
||||
allprojects {
|
||||
// Root directories for test output (logs, compiled files, statistics etc). Only single path must be in each set.
|
||||
sourceSets {
|
||||
// backend.native/tests
|
||||
testOutputLocal {
|
||||
output.dir(rootProject.file("$testOutputRoot/local"))
|
||||
}
|
||||
|
||||
abstract class KonanTest extends DefaultTask {
|
||||
protected String source
|
||||
def backendNative = project.project(":backend.native")
|
||||
def runtimeProject = project.project(":runtime")
|
||||
def dist = project.parent.parent.file("dist")
|
||||
def llvmLlc = llvmTool("llc")
|
||||
def runtimeBc = new File("${dist.canonicalPath}/lib/runtime.bc").absolutePath
|
||||
def launcherBc = new File("${dist.canonicalPath}/lib/launcher.bc").absolutePath
|
||||
def startKtBc = new File("${dist.canonicalPath}/lib/start.kt.bc").absolutePath
|
||||
def stdlibKtBc = new File("${dist.canonicalPath}/lib/stdlib.kt.bc").absolutePath
|
||||
def mainC = 'main.c'
|
||||
String goldValue = null
|
||||
String testData = null
|
||||
List<String> arguments = null
|
||||
|
||||
boolean enabled = true
|
||||
|
||||
public void setDisabled(boolean value) {
|
||||
this.enabled = !value
|
||||
}
|
||||
|
||||
public KonanTest(){
|
||||
// TODO: that's a long reach up the project tree.
|
||||
// May be we should reorganize a little.
|
||||
dependsOn(project.parent.parent.tasks['dist'])
|
||||
}
|
||||
|
||||
abstract void compileTest(String source, String exe)
|
||||
|
||||
protected void runCompiler(String source, String output, List<String> moreArgs) {
|
||||
project.javaexec {
|
||||
main = 'org.jetbrains.kotlin.cli.bc.K2NativeKt'
|
||||
classpath = project.configurations.cli_bc
|
||||
jvmArgs "-ea",
|
||||
"-Dkonan.home=${dist.canonicalPath}",
|
||||
"-Djava.library.path=${dist.canonicalPath}/konan/nativelib"
|
||||
args("-output", output,
|
||||
source,
|
||||
*moreArgs,
|
||||
*project.globalArgs)
|
||||
// backend.native/tests/external
|
||||
testOutputExternal {
|
||||
output.dir(rootProject.file("$testOutputRoot/external"))
|
||||
}
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
void executeTest() {
|
||||
def sourceKt = project.file(source).absolutePath
|
||||
def exe = sourceKt.replace(".kt", ".kt.exe")
|
||||
|
||||
compileTest(sourceKt, exe)
|
||||
println "execution :$exe"
|
||||
|
||||
def out = null
|
||||
project.exec {
|
||||
commandLine exe
|
||||
if (arguments != null) {
|
||||
args arguments
|
||||
}
|
||||
if (testData != null) {
|
||||
standardInput = new ByteArrayInputStream(testData.bytes)
|
||||
}
|
||||
if (goldValue != null) {
|
||||
out = new ByteArrayOutputStream()
|
||||
standardOutput = out
|
||||
}
|
||||
}
|
||||
if (goldValue != null && goldValue != out.toString())
|
||||
throw new RuntimeException("test failed")
|
||||
}
|
||||
|
||||
private String llvmTool(String tool) {
|
||||
return "${project.llvmDir}/bin/${tool}"
|
||||
}
|
||||
|
||||
protected List<String> clangLinkArgs() {
|
||||
return project.clangLinkArgs
|
||||
}
|
||||
}
|
||||
|
||||
class RunKonanTest extends KonanTest {
|
||||
void compileTest(String source, String exe) {
|
||||
runCompiler(source, exe, [])
|
||||
task clean {
|
||||
doLast {
|
||||
delete(rootProject.file(testOutputRoot))
|
||||
}
|
||||
}
|
||||
|
||||
class LinkKonanTest extends KonanTest {
|
||||
protected String lib
|
||||
|
||||
void compileTest(String source, String exe) {
|
||||
def libDir = project.file(lib).absolutePath
|
||||
def libBc = "${libDir}.bc"
|
||||
|
||||
runCompiler(lib, libBc, ['-nolink', '-nostdlib'])
|
||||
runCompiler(source, exe, ['-library', libBc])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
task run() {
|
||||
dependsOn(tasks.withType(KonanTest).matching { it.enabled })
|
||||
dependsOn(tasks.withType(KonanTest).matching { !(it instanceof RunExternalTestGroup) && it.enabled })
|
||||
}
|
||||
|
||||
task run_external () {
|
||||
// TODO Consider test output directory cleaning before execution.
|
||||
// TODO Consider using some logger instead of println
|
||||
// Create tasks for external tests
|
||||
externalTestsDir.eachDirRecurse {
|
||||
// Skip build directory and directories without *.kt files.
|
||||
if (it.name == "build" || it.listFiles().count {it.name.endsWith(".kt")} == 0) {
|
||||
return
|
||||
}
|
||||
|
||||
def taskDirectory = project.relativePath(it)
|
||||
def taskName = taskDirectory.replaceAll("[-/]", '_')
|
||||
|
||||
task("$taskName", type: RunExternalTestGroup){
|
||||
groupDirectory = "$taskDirectory"
|
||||
}
|
||||
}
|
||||
def testTasks = tasks.withType(RunExternalTestGroup).matching { it.enabled }
|
||||
dependsOn(testTasks)
|
||||
|
||||
doLast {
|
||||
Map<String, Map<String, RunExternalTestGroup.TestResult>> results = [:]
|
||||
def statistics = new RunExternalTestGroup.Statistics()
|
||||
testTasks.matching { it.state.executed }.each {
|
||||
results.put(it.name, it.results)
|
||||
statistics.add(it.statistics)
|
||||
}
|
||||
|
||||
def output = ["statistics" : statistics, "tests" : results]
|
||||
def json = JsonOutput.toJson(output)
|
||||
def reportFile = new File(sourceSets.testOutputExternal.output.getDirs().getSingleFile(), "results.json")
|
||||
reportFile.write(JsonOutput.prettyPrint(json))
|
||||
println("DONE.\n\n" +
|
||||
"TOTAL: $statistics.total\n" +
|
||||
"PASSED: $statistics.passed\n" +
|
||||
"FAILED: $statistics.failed\n" +
|
||||
"SKIPPED: $statistics.skipped")
|
||||
}
|
||||
}
|
||||
|
||||
task daily() {
|
||||
dependsOn(run_external)
|
||||
}
|
||||
|
||||
task sum (type:RunKonanTest) {
|
||||
@@ -127,7 +99,6 @@ task fields1(type: RunKonanTest) {
|
||||
}
|
||||
|
||||
task fields2(type: RunKonanTest) {
|
||||
disabled = true
|
||||
goldValue =
|
||||
"Set global = 1\n" +
|
||||
"Set member = 42\n" +
|
||||
@@ -166,6 +137,11 @@ task safe_cast(type: RunKonanTest) {
|
||||
source = "codegen/basics/safe_cast.kt"
|
||||
}
|
||||
|
||||
task typealias1(type: RunKonanTest) {
|
||||
goldValue = "42\n"
|
||||
source = "codegen/basics/typealias1.kt"
|
||||
}
|
||||
|
||||
task aritmetic(type: RunKonanTest) {
|
||||
source = "codegen/function/arithmetic.kt"
|
||||
}
|
||||
@@ -202,13 +178,11 @@ task defaults4(type: RunKonanTest) {
|
||||
}
|
||||
|
||||
task defaults5(type: RunKonanTest) {
|
||||
disabled = true
|
||||
goldValue = "5\n6\n"
|
||||
source = "codegen/function/defaults5.kt"
|
||||
}
|
||||
|
||||
task defaults6(type: RunKonanTest) {
|
||||
disabled = true
|
||||
goldValue = "42\n"
|
||||
source = "codegen/function/defaults6.kt"
|
||||
}
|
||||
@@ -231,6 +205,27 @@ task cast_simple(type: RunKonanTest) {
|
||||
source = "codegen/basics/cast_simple.kt"
|
||||
}
|
||||
|
||||
task cast_null(type: RunKonanTest) {
|
||||
goldValue = "Ok\n"
|
||||
source = "codegen/basics/cast_null.kt"
|
||||
}
|
||||
|
||||
task unchecked_cast1(type: RunKonanTest) {
|
||||
goldValue = "17\n17\n42\n42\n"
|
||||
source = "codegen/basics/unchecked_cast1.kt"
|
||||
}
|
||||
|
||||
task unchecked_cast2(type: RunKonanTest) {
|
||||
disabled = true
|
||||
goldValue = "Ok\n"
|
||||
source = "codegen/basics/unchecked_cast2.kt"
|
||||
}
|
||||
|
||||
task unchecked_cast3(type: RunKonanTest) {
|
||||
goldValue = "Ok\n"
|
||||
source = "codegen/basics/unchecked_cast3.kt"
|
||||
}
|
||||
|
||||
task null_check(type: RunKonanTest) {
|
||||
source = "codegen/basics/null_check.kt"
|
||||
}
|
||||
@@ -297,6 +292,76 @@ task empty_substring(type: RunKonanTest) {
|
||||
source = "runtime/basic/empty_substring.kt"
|
||||
}
|
||||
|
||||
task superFunCall(type: RunKonanTest) {
|
||||
goldValue = "<fun:C><fun:C1>\n<fun:C><fun:C3>\n"
|
||||
source = "codegen/basics/superFunCall.kt"
|
||||
}
|
||||
|
||||
task superGetterCall(type: RunKonanTest) {
|
||||
goldValue = "<prop:C><prop:C1>\n<prop:C><prop:C3>\n"
|
||||
source = "codegen/basics/superGetterCall.kt"
|
||||
}
|
||||
|
||||
task superSetterCall(type: RunKonanTest) {
|
||||
goldValue = "<prop:C1><prop:C>zzz\n<prop:C3><prop:C>zzz\n"
|
||||
source = "codegen/basics/superSetterCall.kt"
|
||||
}
|
||||
|
||||
task enum0(type: RunKonanTest) {
|
||||
goldValue = "VALUE\n"
|
||||
source = "codegen/enum/test0.kt"
|
||||
}
|
||||
|
||||
task enum1(type: RunKonanTest) {
|
||||
goldValue = "z12\n"
|
||||
source = "codegen/enum/test1.kt"
|
||||
}
|
||||
|
||||
task enum_valueOf(type: RunKonanTest) {
|
||||
goldValue = "E1\n"
|
||||
source = "codegen/enum/valueOf.kt"
|
||||
}
|
||||
|
||||
task enum_values(type: RunKonanTest) {
|
||||
goldValue = "E2\n"
|
||||
source = "codegen/enum/values.kt"
|
||||
}
|
||||
|
||||
task enum_vCallNoEntryClass(type: RunKonanTest) {
|
||||
goldValue = "('z3', 3)\n"
|
||||
source = "codegen/enum/vCallNoEntryClass.kt"
|
||||
}
|
||||
|
||||
task enum_vCallWithEntryClass(type: RunKonanTest) {
|
||||
goldValue = "z1z2\n"
|
||||
source = "codegen/enum/vCallWithEntryClass.kt"
|
||||
}
|
||||
|
||||
task enum_companionObject(type: RunKonanTest) {
|
||||
goldValue = "OK\n"
|
||||
source = "codegen/enum/companionObject.kt"
|
||||
}
|
||||
|
||||
task innerClass_simple(type: RunKonanTest) {
|
||||
goldValue = "OK\n"
|
||||
source = "codegen/innerClass/simple.kt"
|
||||
}
|
||||
|
||||
task innerClass_getOuterVal(type: RunKonanTest) {
|
||||
goldValue = "OK\n"
|
||||
source = "codegen/innerClass/getOuterVal.kt"
|
||||
}
|
||||
|
||||
task innerClass_generic(type: RunKonanTest) {
|
||||
goldValue = "OK\n"
|
||||
source = "codegen/innerClass/generic.kt"
|
||||
}
|
||||
|
||||
task innerClass_qualifiedThis(type: RunKonanTest) {
|
||||
goldValue = "OK\n"
|
||||
source = "codegen/innerClass/qualifiedThis.kt"
|
||||
}
|
||||
|
||||
task array0(type: RunKonanTest) {
|
||||
goldValue = "5\n6\n7\n8\n9\n10\n11\n12\n13\n"
|
||||
source = "runtime/collections/array0.kt"
|
||||
@@ -337,6 +402,11 @@ task when8(type: RunKonanTest) {
|
||||
goldValue = "true\n"
|
||||
}
|
||||
|
||||
task when9(type: RunKonanTest) {
|
||||
goldValue = "Ok\n"
|
||||
source = "codegen/branching/when9.kt"
|
||||
}
|
||||
|
||||
task when_through(type: RunKonanTest) {
|
||||
source = "codegen/branching/when_through.kt"
|
||||
}
|
||||
@@ -505,6 +575,17 @@ task boxing13(type: RunKonanTest) {
|
||||
source = "codegen/boxing/boxing13.kt"
|
||||
}
|
||||
|
||||
task boxing14(type: RunKonanTest) {
|
||||
goldValue = "42\n"
|
||||
source = "codegen/boxing/boxing14.kt"
|
||||
}
|
||||
|
||||
task boxing15(type: RunKonanTest) {
|
||||
disabled = true
|
||||
goldValue = "17\n"
|
||||
source = "codegen/boxing/boxing15.kt"
|
||||
}
|
||||
|
||||
task interface0(type: RunKonanTest) {
|
||||
goldValue = "PASSED\n"
|
||||
source = "runtime/basic/interface0.kt"
|
||||
@@ -572,6 +653,11 @@ task catch7(type: RunKonanTest) {
|
||||
source = "runtime/exceptions/catch7.kt"
|
||||
}
|
||||
|
||||
task extend_exception(type: RunKonanTest) {
|
||||
goldValue = "OK\n"
|
||||
source = "runtime/exceptions/extend0.kt"
|
||||
}
|
||||
|
||||
task catch3(type: RunKonanTest) {
|
||||
goldValue = "Before\nCaught Throwable\nDone\n"
|
||||
source = "codegen/try/catch3.kt"
|
||||
@@ -814,6 +900,21 @@ task lambda13(type: RunKonanTest) {
|
||||
source = "codegen/lambda/lambda13.kt"
|
||||
}
|
||||
|
||||
task objectExpression1(type: RunKonanTest) {
|
||||
goldValue = "aabb\n"
|
||||
source = "codegen/objectExpression/1.kt"
|
||||
}
|
||||
|
||||
task objectExpression2(type: RunKonanTest) {
|
||||
goldValue = "a\n"
|
||||
source = "codegen/objectExpression/2.kt"
|
||||
}
|
||||
|
||||
task objectExpression3(type: RunKonanTest) {
|
||||
goldValue = "\n1\n2\n"
|
||||
source = "codegen/objectExpression/3.kt"
|
||||
}
|
||||
|
||||
task initializers2(type: RunKonanTest) {
|
||||
goldValue = "init globalValue2\n" +
|
||||
"init globalValue3\n" +
|
||||
@@ -829,11 +930,15 @@ task initializers3(type: RunKonanTest) {
|
||||
}
|
||||
|
||||
task initializers4(type: RunKonanTest) {
|
||||
disabled = true
|
||||
goldValue = "fixme\n"
|
||||
goldValue = "1073741824\ntrue\n"
|
||||
source = "runtime/basic/initializers4.kt"
|
||||
}
|
||||
|
||||
task initializers5(type: RunKonanTest) {
|
||||
goldValue = "42\n"
|
||||
source = "runtime/basic/initializers5.kt"
|
||||
}
|
||||
|
||||
task expression_as_statement(type: RunKonanTest) {
|
||||
goldValue = "Ok\n"
|
||||
source = "codegen/basics/expression_as_statement.kt"
|
||||
@@ -905,6 +1010,7 @@ task inline3(type: RunKonanTest) {
|
||||
source = "codegen/inline/inline3.kt"
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
task inline4(type: RunKonanTest) {
|
||||
goldValue = "3\n"
|
||||
source = "codegen/inline/inline4.kt"
|
||||
@@ -918,4 +1024,8 @@ task inline5(type: RunKonanTest) {
|
||||
task inline9(type: RunKonanTest) {
|
||||
goldValue = "hello\n6\n"
|
||||
source = "codegen/inline/inline9.kt"
|
||||
=======
|
||||
task vararg0(type: RunKonanTest) {
|
||||
source = "lower/vararg.kt"
|
||||
>>>>>>> b4ee0e86d2890b3aca7eab4e1910a90fedff2b55
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
fun main(args: Array<String>) {
|
||||
testCast(null, false)
|
||||
testCastToNullable(null, true)
|
||||
testCastToNullable(Test(), true)
|
||||
testCastToNullable("", false)
|
||||
testCastNotNullableToNullable(Test(), true)
|
||||
testCastNotNullableToNullable("", false)
|
||||
|
||||
println("Ok")
|
||||
}
|
||||
|
||||
class Test
|
||||
|
||||
fun ensure(b: Boolean) {
|
||||
if (!b) {
|
||||
println("Error")
|
||||
}
|
||||
}
|
||||
|
||||
fun testCast(x: Any?, expectSuccess: Boolean) {
|
||||
try {
|
||||
x as Test
|
||||
} catch (e: Throwable) {
|
||||
ensure(!expectSuccess)
|
||||
return
|
||||
}
|
||||
ensure(expectSuccess)
|
||||
}
|
||||
|
||||
fun testCastToNullable(x: Any?, expectSuccess: Boolean) {
|
||||
try {
|
||||
x as Test?
|
||||
} catch (e: Throwable) {
|
||||
ensure(!expectSuccess)
|
||||
return
|
||||
}
|
||||
ensure(expectSuccess)
|
||||
}
|
||||
|
||||
fun testCastNotNullableToNullable(x: Any, expectSuccess: Boolean) {
|
||||
try {
|
||||
x as Test?
|
||||
} catch (e: Throwable) {
|
||||
ensure(!expectSuccess)
|
||||
return
|
||||
}
|
||||
ensure(expectSuccess)
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
open class C {
|
||||
open fun f() = "<fun:C>"
|
||||
}
|
||||
|
||||
class C1: C() {
|
||||
override fun f() = super<C>.f() + "<fun:C1>"
|
||||
}
|
||||
|
||||
open class C2: C() {
|
||||
}
|
||||
|
||||
class C3: C2() {
|
||||
override fun f() = super<C2>.f() + "<fun:C3>"
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
println(C1().f())
|
||||
println(C3().f())
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
open class C {
|
||||
open val p1 = "<prop:C>"
|
||||
}
|
||||
|
||||
class C1: C() {
|
||||
override val p1 = super<C>.p1 + "<prop:C1>"
|
||||
}
|
||||
|
||||
open class C2: C() {
|
||||
}
|
||||
|
||||
class C3: C2() {
|
||||
override val p1 = super<C2>.p1 + "<prop:C3>"
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
println(C1().p1)
|
||||
println(C3().p1)
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
open class C {
|
||||
open var p2 = "<prop:C>"
|
||||
set(value) { field = "<prop:C>" + value }
|
||||
}
|
||||
|
||||
class C1: C() {
|
||||
override var p2 = super<C>.p2 + "<prop:C1>"
|
||||
set(value) {
|
||||
super<C>.p2 = value
|
||||
field = "<prop:C1>" + super<C>.p2
|
||||
}
|
||||
}
|
||||
|
||||
open class C2: C() {
|
||||
}
|
||||
|
||||
class C3: C2() {
|
||||
override var p2 = super<C2>.p2 + "<prop:C3>"
|
||||
set(value) {
|
||||
super<C2>.p2 = value
|
||||
field = "<prop:C3>" + super<C2>.p2
|
||||
}
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val c1 = C1()
|
||||
val c3 = C3()
|
||||
c1.p2 = "zzz"
|
||||
c3.p2 = "zzz"
|
||||
println(c1.p2)
|
||||
println(c3.p2)
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
fun main(args: Array<String>) {
|
||||
println(Bar(42).x)
|
||||
}
|
||||
|
||||
class Foo(val x: Int)
|
||||
typealias Bar = Foo
|
||||
@@ -0,0 +1,16 @@
|
||||
fun main(args: Array<String>) {
|
||||
foo<String>("17")
|
||||
bar<String>("17")
|
||||
foo<String>(42)
|
||||
bar<String>(42)
|
||||
}
|
||||
|
||||
fun <T> foo(x: Any?) {
|
||||
val y = x as T
|
||||
println(y.toString())
|
||||
}
|
||||
|
||||
fun <T> bar(x: Any?) {
|
||||
val y = x as? T
|
||||
println(y.toString())
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fun main(args: Array<String>) {
|
||||
try {
|
||||
val x = cast<String>(Any())
|
||||
println(x.length)
|
||||
} catch (e: Throwable) {
|
||||
println("Ok")
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> cast(x: Any?) = x as T
|
||||
@@ -0,0 +1,35 @@
|
||||
fun main(args: Array<String>) {
|
||||
testCast<Test>(Test(), true)
|
||||
testCast<Test>(null, false)
|
||||
testCastToNullable<Test>(null, true)
|
||||
|
||||
println("Ok")
|
||||
}
|
||||
|
||||
class Test
|
||||
|
||||
fun ensure(b: Boolean) {
|
||||
if (!b) {
|
||||
println("Error")
|
||||
}
|
||||
}
|
||||
|
||||
fun <T : Any> testCast(x: Any?, expectSuccess: Boolean) {
|
||||
try {
|
||||
x as T
|
||||
} catch (e: Throwable) {
|
||||
ensure(!expectSuccess)
|
||||
return
|
||||
}
|
||||
ensure(expectSuccess)
|
||||
}
|
||||
|
||||
fun <T : Any> testCastToNullable(x: Any?, expectSuccess: Boolean) {
|
||||
try {
|
||||
x as T?
|
||||
} catch (e: Throwable) {
|
||||
ensure(!expectSuccess)
|
||||
return
|
||||
}
|
||||
ensure(expectSuccess)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
fun main(args: Array<String>) {
|
||||
42.println()
|
||||
}
|
||||
|
||||
fun <T> T.println() = println(this.toString())
|
||||
@@ -0,0 +1,5 @@
|
||||
fun main(args: Array<String>) {
|
||||
println(foo(17))
|
||||
}
|
||||
|
||||
fun <T : Int> foo(x: T): Int = x
|
||||
@@ -0,0 +1,10 @@
|
||||
fun main(args: Array<String>) {
|
||||
foo(0)
|
||||
println("Ok")
|
||||
}
|
||||
|
||||
fun foo(x: Int) {
|
||||
when (x) {
|
||||
0 -> 0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
enum class Game {
|
||||
ROCK,
|
||||
PAPER,
|
||||
SCISSORS;
|
||||
|
||||
companion object {
|
||||
fun foo() = ROCK
|
||||
val bar = PAPER
|
||||
val values2 = values()
|
||||
val scissors = valueOf("SCISSORS")
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
if (Game.foo() != Game.ROCK) return "Fail 1"
|
||||
if (Game.bar != Game.PAPER) return "Fail 2: ${Game.bar}"
|
||||
if (Game.values().size != 3) return "Fail 3"
|
||||
if (Game.valueOf("SCISSORS") != Game.SCISSORS) return "Fail 4"
|
||||
if (Game.values2.size != 3) return "Fail 5"
|
||||
if (Game.scissors != Game.SCISSORS) return "Fail 6"
|
||||
return "OK"
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) = println(box())
|
||||
@@ -0,0 +1,9 @@
|
||||
val TOP_LEVEL = 5
|
||||
|
||||
enum class MyEnum(value: Int) {
|
||||
VALUE(TOP_LEVEL)
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
println(MyEnum.VALUE.toString())
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
enum class Zzz(val zzz: String, val x: Int) {
|
||||
Z1("z1", 1),
|
||||
Z2("z2", 2)
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
println(Zzz.Z1.zzz + Zzz.Z2.x)
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
enum class Zzz(val zzz: String, val x: Int) {
|
||||
Z1("z1", 1),
|
||||
Z2("z2", 2),
|
||||
Z3("z3", 3);
|
||||
|
||||
override fun toString(): String{
|
||||
return "('$zzz', $x)"
|
||||
}
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
println(Zzz.Z3.toString())
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
enum class Zzz {
|
||||
Z1 {
|
||||
override fun f() = "z1"
|
||||
},
|
||||
|
||||
Z2 {
|
||||
override fun f() = "z2"
|
||||
};
|
||||
|
||||
open fun f() = ""
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
println(Zzz.Z1.f() + Zzz.Z2.f())
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
enum class E {
|
||||
E1,
|
||||
E2
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
println(E.valueOf("E1").toString())
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
enum class E {
|
||||
E1,
|
||||
E2
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
println(E.values()[1].toString())
|
||||
}
|
||||
@@ -2,10 +2,12 @@ open class A {
|
||||
open fun foo(x: Int = 42) = println(x)
|
||||
}
|
||||
|
||||
class B : A() {
|
||||
open class B : A()
|
||||
|
||||
class C : B() {
|
||||
override fun foo(x: Int) = println(x + 1)
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
B().foo()
|
||||
C().foo()
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
class Outer {
|
||||
inner class Inner<T>(val t: T) {
|
||||
fun box() = t
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
if (Outer().Inner("OK").box() != "OK") return "Fail"
|
||||
val x: Outer.Inner<String> = Outer().Inner("OK")
|
||||
return x.box()
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
println(box())
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
class Outer(val s: String) {
|
||||
inner class Inner {
|
||||
fun box() = s
|
||||
}
|
||||
}
|
||||
|
||||
fun box() = Outer("OK").Inner().box()
|
||||
|
||||
fun main(args: Array<String>)
|
||||
{
|
||||
println(box())
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
open class ABase
|
||||
{
|
||||
open fun zzz() = "a_base"
|
||||
}
|
||||
|
||||
open class BBase
|
||||
{
|
||||
open fun zzz() = "b_base"
|
||||
}
|
||||
|
||||
class D() {
|
||||
val z = "d"
|
||||
}
|
||||
|
||||
class A: ABase() { // implicit label @A
|
||||
val z = "a"
|
||||
override fun zzz() = "a"
|
||||
inner class B: BBase() { // implicit label @B
|
||||
val z = "b"
|
||||
override fun zzz() = "b"
|
||||
fun D.foo() : String { // implicit label @foo
|
||||
if(this@A.z != "a") return "Fail1"
|
||||
if(this@B.z != "b") return "Fail2"
|
||||
|
||||
if(super@A.zzz() != "a_base") return "Fail3"
|
||||
if(super<BBase>.zzz() != "b_base") return "Fail4"
|
||||
if(super@B.zzz() != "b_base") return "Fail5"
|
||||
if(this@A.zzz() != "a") return "Fail6"
|
||||
if(this@B.zzz() != "b") return "Fail7"
|
||||
|
||||
if(this.z != "d") return "Fail8"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
fun bar(d: D): String {
|
||||
return d.foo()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun box() = A().B().bar(D())
|
||||
|
||||
fun main(args : Array<String>) {
|
||||
println(box())
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
class Outer {
|
||||
inner class Inner {
|
||||
fun box() = "OK"
|
||||
}
|
||||
}
|
||||
|
||||
fun box() = Outer().Inner().box()
|
||||
|
||||
fun main(args: Array<String>)
|
||||
{
|
||||
println(box())
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fun main(args: Array<String>) {
|
||||
val a = "a"
|
||||
|
||||
val x = object {
|
||||
override fun toString(): String {
|
||||
return foo(a) + foo("b")
|
||||
}
|
||||
|
||||
fun foo(s: String) = s + s
|
||||
}
|
||||
|
||||
println(x.toString())
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
fun main(args: Array<String>) {
|
||||
val a = "a"
|
||||
|
||||
val x = object {
|
||||
override fun toString(): String {
|
||||
return foo {
|
||||
a
|
||||
}
|
||||
}
|
||||
|
||||
fun foo(lambda: () -> String) = lambda()
|
||||
}
|
||||
|
||||
print(x)
|
||||
}
|
||||
|
||||
fun print(x: Any) = println(x.toString())
|
||||
@@ -0,0 +1,17 @@
|
||||
fun main(args: Array<String>) {
|
||||
var cnt = 0
|
||||
|
||||
var x: Any = ""
|
||||
|
||||
for (i in 0 .. 1) {
|
||||
print(x)
|
||||
cnt++
|
||||
val y = object {
|
||||
override fun toString() = cnt.toString()
|
||||
}
|
||||
x = y
|
||||
}
|
||||
print(x)
|
||||
}
|
||||
|
||||
fun print(x: Any) = println(x.toString())
|
||||
+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