Entry point selection.

$ kotlinc -entry foo.bar.qux ...

selects `qux(args: Array<String>):Unit` in the package `foo.bar` as an entry point.

The short flag is `-e`.
This commit is contained in:
Alexander Gorshenev
2017-06-01 18:45:54 +03:00
committed by alexander-gorshenev
parent a2fa4938f3
commit 5192bd1313
11 changed files with 205 additions and 3 deletions
@@ -122,6 +122,7 @@ class K2Native : CLICompiler<K2NativeCompilerArguments>() {
put(CommonConfigurationKeys.MODULE_NAME, output)
put(ABI_VERSION, 1)
arguments.mainPackage ?.let{ put(ENTRY, it) }
arguments.runtimeFile ?.let{ put(RUNTIME_FILE, it) }
arguments.propertyFile ?.let{ put(PROPERTY_FILE, it) }
@@ -57,6 +57,9 @@ class K2NativeCompilerArguments : CommonCompilerArguments() {
@field:Argument(value = "-output", shortName = "-o", valueDescription = "<path>", description = "Output file path")
@JvmField var outputFile: String? = null
@field:Argument(value = "-entry", shortName = "-e", valueDescription = "<name>", description = "Qualified entry point name")
@JvmField var mainPackage: String? = null
@field:Argument(value = "-produce", shortName = "-p", valueDescription = "{program|library|bitcode}", description = "Produce either .kexe, .klib or a .bc file.")
@JvmField var produce: String? = null
@@ -40,6 +40,8 @@ class KonanConfigKeys {
= CompilerConfigurationKey.create("list backend phases")
val LIST_TARGETS: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("list available targets")
val ENTRY: CompilerConfigurationKey<String?>
= CompilerConfigurationKey.create("fully qualified main() name")
val META_INFO: CompilerConfigurationKey<List<String>>
= CompilerConfigurationKey.create("generate metadata")
val MODULE_KIND: CompilerConfigurationKey<ModuleKind>
@@ -0,0 +1,77 @@
/*
* Copyright 2010-2017 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.konan.llvm
import org.jetbrains.kotlin.backend.konan.CompilerOutputKind
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.KonanConfigKeys
import org.jetbrains.kotlin.backend.konan.descriptors.isArray
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.isUnit
import org.jetbrains.kotlin.utils.alwaysTrue
internal fun findMainEntryPoint(context: Context): FunctionDescriptor? {
val config = context.config.configuration
if (config.get(KonanConfigKeys.PRODUCE) != CompilerOutputKind.PROGRAM) return null
val entryPoint = FqName(config.get(KonanConfigKeys.ENTRY) ?: defaultEntryName)
val entryName = entryPoint.shortName()
val packageName = entryPoint.parent()
val packageScope = context.builtIns.builtInsModule.getPackage(packageName).memberScope
val main = packageScope.getContributedFunctions(entryName,
NoLookupLocation.FROM_BACKEND).singleOrNull {
it.returnType?.isUnit() == true &&
it.hasSingleArrayOfStringParameter &&
it.typeParameters.isEmpty() &&
it.isExported()
}
return main
}
private val defaultEntryName = "main"
private val defaultEntryPackage = FqName.ROOT
private val KotlinType.filterClass: ClassDescriptor?
get() {
val constr = constructor.declarationDescriptor
return constr as? ClassDescriptor
}
private val ClassDescriptor.isString
get() = fqNameSafe.asString() == "kotlin.String"
private val KotlinType.isString
get() = filterClass?.isString ?: false
private val KotlinType.isArrayOfString: Boolean
get() = (filterClass?.isArray ?: false) &&
(arguments.singleOrNull()?.type?.isString ?: false)
private val FunctionDescriptor.hasSingleArrayOfStringParameter: Boolean
get() = valueParameters.singleOrNull()?.type?.isArrayOfString ?: false
@@ -334,6 +334,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
module.acceptChildrenVoid(this)
appendLlvmUsed(context.llvm.usedFunctions)
appendStaticInitializers(context.llvm.staticInitializers)
appendEntryPointSelector(findMainEntryPoint(context))
}
//-------------------------------------------------------------------------//
@@ -2096,6 +2097,44 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid
}
}
//-------------------------------------------------------------------------//
fun entryPointSelector(entryPoint: LLVMValueRef,
entryPointType: LLVMTypeRef, selectorName: String): LLVMValueRef {
assert(LLVMCountParams(entryPoint) == 1)
val selector = LLVMAddFunction(context.llvmModule, selectorName, entryPointType)!!
codegen.prologue(selector, voidType)
// Note, that 'parameter' is an object reference, and as such, shall
// be accounted for in the rootset. However, current object management
// scheme for arguments guarantees, that reference is being held in C++
// launcher, so we could optimize out creating slot for 'parameter' in
// this function.
val parameter = LLVMGetParam(selector, 0)!!
codegen.callAtFunctionScope(entryPoint, listOf(parameter), Lifetime.IRRELEVANT)
codegen.ret(null)
codegen.epilogue()
return selector
}
//-------------------------------------------------------------------------//
fun appendEntryPointSelector(descriptor: FunctionDescriptor?) {
if (descriptor == null) return
val entryPoint = codegen.llvmFunction(descriptor)
val selectorName = "EntryPointSelector"
val entryPointType = getFunctionType(entryPoint)!!
val selector = entryPointSelector(entryPoint, entryPointType, selectorName)
LLVMSetLinkage(selector, LLVMLinkage.LLVMExternalLinkage)
}
//-------------------------------------------------------------------------//
// Create type { i32, void ()*, i8* }
+19
View File
@@ -371,6 +371,25 @@ task hello4(type: RunKonanTest) {
source = "runtime/basic/hello4.kt"
}
task entry0(type: RunKonanTest) {
goldValue = "Hello.\n"
source = "runtime/basic/entry0.kt"
flags = ["-entry", "koko.lala.main"]
}
task entry1(type: RunKonanTest) {
goldValue = "Hello.\n"
source = "runtime/basic/entry1.kt"
flags = ["-entry", "foo"]
}
task entry2(type: LinkKonanTest) {
goldValue = "Hello.\n"
source = "runtime/basic/entry2.kt"
lib = "runtime/basic/libentry2.kt"
flags = ["-entry", "foo"]
}
task tostring0(type: RunKonanTest) {
goldValue = "127\n-1\n239\nA\nЁ\nト\n1122334455\n112233445566778899\n3.14159265358\n1.0E27\n1.0E-300\ntrue\nfalse\n"
source = "runtime/basic/tostring0.kt"
@@ -0,0 +1,32 @@
package koko.lala
fun fail() {
println("Test failed, this is a wrong main() function.")
}
fun main() {
fail()
}
fun <T> main(args: Array<String>) {
fail()
}
fun main(args: Array<Int>) {
fail()
}
fun main(args: Array<String>, second_arg: Int) {
fail()
}
class Foo {
fun main(args: Array<String>) {
fail()
}
}
fun main(args: Array<String>) {
println("Hello.")
}
@@ -0,0 +1,13 @@
fun fail() {
println("Test failed, this is a wrong main() function.")
}
fun foo(args: Array<String>) {
println("Hello.")
}
fun main(args: Array<String>) {
fail()
}
@@ -0,0 +1,4 @@
fun main(args: Array<String>) {
fail()
}
@@ -0,0 +1,8 @@
fun fail() {
println("Test failed, this is a wrong main() function.")
}
fun foo(args: Array<String>) {
println("Hello.")
}
+7 -3
View File
@@ -16,14 +16,18 @@
import konan.internal.ExportForCppRuntime
external fun main(args: Array<String>)
// This function is produced by the code generator given
// the '-entry foo.bar.main' flag.
// It calls the requested entry point.
// The default is main(Array<String>):Unit in the root package.
@ExportForCppRuntime
external fun EntryPointSelector(args: Array<String>)
@ExportForCppRuntime
private fun Konan_start(args: Array<String>): Int {
try {
// This is kotlin program main entry point
main(args)
EntryPointSelector(args)
// Successfully finished:
return 0