[MERGE] Kotlin/Native history merged into kotlin/master
This commit is contained in:
@@ -62,3 +62,5 @@ local.properties
|
||||
buildSrcTmp/
|
||||
distTmp/
|
||||
outTmp/
|
||||
/test.output
|
||||
/kotlin-native/dist
|
||||
@@ -0,0 +1,8 @@
|
||||
<component name="ArtifactManager">
|
||||
<artifact type="jar" name="kotlinx.cli-jvm-1.5.255-SNAPSHOT">
|
||||
<output-path>$PROJECT_DIR$/kotlin-native/endorsedLibraries/kotlinx.cli/build/libs</output-path>
|
||||
<root id="archive" name="kotlinx.cli-jvm-1.5.255-SNAPSHOT.jar">
|
||||
<element id="module-output" name="kotlin.kotlin-native.endorsedLibraries.kotlinx.cli.jvmMain" />
|
||||
</root>
|
||||
</artifact>
|
||||
</component>
|
||||
Generated
+4
@@ -12,10 +12,14 @@
|
||||
<item index="2" class="java.lang.String" itemvalue="org.gradle.api.tasks.options.Option" />
|
||||
</list>
|
||||
</component>
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="FrameworkDetectionExcludesConfiguration">
|
||||
<file type="web" url="file://$PROJECT_DIR$" />
|
||||
</component>
|
||||
<component name="IdProvider" IDEtalkID="71A301FF1940049D6D82F12C40F1E1D5" />
|
||||
<component name="JavaScriptSettings">
|
||||
<option name="languageLevel" value="ES6" />
|
||||
</component>
|
||||
<component name="JavadocGenerationManager">
|
||||
<option name="OTHER_OPTIONS" value="" />
|
||||
</component>
|
||||
|
||||
+5
-1
@@ -27,7 +27,7 @@ buildscript {
|
||||
dependencies {
|
||||
bootstrapCompilerClasspath(kotlin("compiler-embeddable", bootstrapKotlinVersion))
|
||||
|
||||
classpath("org.jetbrains.kotlin:kotlin-build-gradle-plugin:0.0.21")
|
||||
classpath("org.jetbrains.kotlin:kotlin-build-gradle-plugin:0.0.25")
|
||||
classpath(kotlin("gradle-plugin", bootstrapKotlinVersion))
|
||||
classpath(kotlin("serialization", bootstrapKotlinVersion))
|
||||
classpath("org.jetbrains.dokka:dokka-gradle-plugin:0.9.17")
|
||||
@@ -115,6 +115,10 @@ extra["ideaUltimateSandboxDir"] = project.file(ideaUltimateSandboxDir)
|
||||
extra["ideaPluginDir"] = project.file(ideaPluginDir)
|
||||
extra["ideaUltimatePluginDir"] = project.file(ideaUltimatePluginDir)
|
||||
extra["isSonatypeRelease"] = false
|
||||
val kotlinNativeVersionObject = project.kotlinNativeVersionValue()
|
||||
subprojects {
|
||||
extra["kotlinNativeVersion"] = kotlinNativeVersionObject
|
||||
}
|
||||
|
||||
// Work-around necessary to avoid setting null javaHome. Will be removed after support of lazy task configuration
|
||||
val jdkNotFoundConst = "JDK NOT FOUND"
|
||||
|
||||
+126
-5
@@ -24,14 +24,12 @@ buildscript {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath("org.jetbrains.kotlin:kotlin-build-gradle-plugin:0.0.21")
|
||||
classpath("org.jetbrains.kotlin:kotlin-build-gradle-plugin:0.0.25")
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${project.bootstrapKotlinVersion}")
|
||||
classpath("org.jetbrains.kotlin:kotlin-sam-with-receiver:${project.bootstrapKotlinVersion}")
|
||||
}
|
||||
}
|
||||
|
||||
val cacheRedirectorEnabled = findProperty("cacheRedirectorEnabled")?.toString()?.toBoolean() == true
|
||||
|
||||
logger.info("buildSrcKotlinVersion: " + extra["bootstrapKotlinVersion"])
|
||||
logger.info("buildSrc kotlin compiler version: " + org.jetbrains.kotlin.config.KotlinCompilerVersion.VERSION)
|
||||
logger.info("buildSrc stdlib version: " + KotlinVersion.CURRENT)
|
||||
@@ -39,6 +37,7 @@ logger.info("buildSrc stdlib version: " + KotlinVersion.CURRENT)
|
||||
apply {
|
||||
plugin("kotlin")
|
||||
plugin("kotlin-sam-with-receiver")
|
||||
plugin("groovy")
|
||||
|
||||
from("../gradle/checkCacheability.gradle.kts")
|
||||
}
|
||||
@@ -81,12 +80,14 @@ extra["intellijReleaseType"] = when {
|
||||
else -> "releases"
|
||||
}
|
||||
|
||||
extra["versions.androidDxSources"] = "5.0.0_r2"
|
||||
extra["customDepsOrg"] = "kotlin.build"
|
||||
|
||||
repositories {
|
||||
jcenter()
|
||||
maven("https://jetbrains.bintray.com/intellij-third-party-dependencies/")
|
||||
maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/kotlin-dependencies")
|
||||
maven("https://kotlin.bintray.com/kotlinx")
|
||||
gradlePluginPortal()
|
||||
|
||||
extra["bootstrapKotlinRepo"]?.let {
|
||||
@@ -94,10 +95,59 @@ repositories {
|
||||
}
|
||||
}
|
||||
|
||||
val generateCompilerVersion by tasks.registering(VersionGenerator::class) {
|
||||
kotlinNativeVersionInResources=true
|
||||
defaultVersionFileLocation()
|
||||
}
|
||||
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
|
||||
dependsOn(generateCompilerVersion)
|
||||
}
|
||||
|
||||
tasks.clean {
|
||||
doFirst {
|
||||
val versionSourceDirectory = project.konanVersionGeneratedSrc()
|
||||
if (versionSourceDirectory.exists()) {
|
||||
versionSourceDirectory.delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets["main"].withConvention(org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet::class) {
|
||||
kotlin.srcDir("src/main/kotlin")
|
||||
if (!kotlinBuildProperties.isInJpsBuildIdeaSync) {
|
||||
kotlin.srcDir("../kotlin-native/shared/src/library/kotlin")
|
||||
kotlin.srcDir("../kotlin-native/shared/src/main/kotlin")
|
||||
kotlin.srcDir("../kotlin-native/build-tools/src/main/kotlin")
|
||||
kotlin.srcDir("../kotlin-native/tools/kotlin-native-gradle-plugin/src/main/kotlin")
|
||||
kotlin.srcDir("../compiler/util-klib/src")
|
||||
kotlin.srcDir("../native/utils/src")
|
||||
}
|
||||
kotlin.srcDir(project.kotlinNativeVersionSrc())
|
||||
/**
|
||||
* TODO: mentioned bellow and Co it'd be better to move to :kotlin-native:performance:buildSrc,
|
||||
* because all this relates to benchmarking.
|
||||
*/
|
||||
kotlin.exclude("**/benchmark/*.kt")
|
||||
kotlin.exclude("**/kotlin/MPPTools.kt")
|
||||
kotlin.exclude("**/kotlin/RegressionsReporter.kt")
|
||||
kotlin.exclude("**/kotlin/RunJvmTask.kt")
|
||||
kotlin.exclude("**/kotlin/RunKotlinNativeTask.kt")
|
||||
kotlin.exclude("**/kotlin/BuildRegister.kt")
|
||||
kotlin.exclude("**/kotlin/benchmarkUtils.kt")
|
||||
}
|
||||
|
||||
tasks.validatePlugins.configure {
|
||||
enabled = false
|
||||
}
|
||||
|
||||
java {
|
||||
disableAutoTargetJvm()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(kotlin("stdlib", embeddedKotlinVersion))
|
||||
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:${project.bootstrapKotlinVersion}")
|
||||
implementation("org.jetbrains.kotlin:kotlin-build-gradle-plugin:0.0.21")
|
||||
implementation("org.jetbrains.kotlin:kotlin-build-gradle-plugin:0.0.25")
|
||||
implementation("com.gradle.publish:plugin-publish-plugin:0.12.0")
|
||||
|
||||
implementation("net.rubygrapefruit:native-platform:${property("versions.native-platform")}")
|
||||
@@ -113,6 +163,27 @@ dependencies {
|
||||
|
||||
implementation("org.gradle:test-retry-gradle-plugin:1.1.9")
|
||||
implementation("com.gradle.enterprise:test-distribution-gradle-plugin:1.2.1")
|
||||
|
||||
compileOnly(gradleApi())
|
||||
|
||||
val kotlinVersion = project.bootstrapKotlinVersion
|
||||
val ktorVersion = "1.2.1"
|
||||
val slackApiVersion = "1.2.0"
|
||||
val metadataVersion = "0.0.1-dev-10"
|
||||
|
||||
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
|
||||
implementation("org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion")
|
||||
implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion")
|
||||
implementation("com.ullink.slack:simpleslackapi:$slackApiVersion")
|
||||
|
||||
implementation("io.ktor:ktor-client-auth:$ktorVersion")
|
||||
implementation("io.ktor:ktor-client-core:$ktorVersion")
|
||||
implementation("io.ktor:ktor-client-cio:$ktorVersion")
|
||||
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-metadata-klib:$metadataVersion")
|
||||
if (kotlinBuildProperties.isInJpsBuildIdeaSync) {
|
||||
implementation("org.jetbrains.kotlin:kotlin-native-utils:${project.bootstrapKotlinVersion}")
|
||||
}
|
||||
}
|
||||
|
||||
samWithReceiver {
|
||||
@@ -129,11 +200,24 @@ java {
|
||||
|
||||
tasks.withType<KotlinCompile>().configureEach {
|
||||
kotlinOptions.freeCompilerArgs += listOf(
|
||||
"-Xopt-in=kotlin.RequiresOptIn", "-Xskip-runtime-version-check", "-Xsuppress-version-warnings"
|
||||
"-Xopt-in=kotlin.RequiresOptIn",
|
||||
"-Xskip-runtime-version-check",
|
||||
"-Xsuppress-version-warnings",
|
||||
"-Xopt-in=kotlin.ExperimentalStdlibApi"
|
||||
)
|
||||
}
|
||||
|
||||
tasks["build"].dependsOn(":prepare-deps:build")
|
||||
sourceSets["main"].withConvention(org.gradle.api.tasks.GroovySourceSet::class) {
|
||||
if (!kotlinBuildProperties.isInJpsBuildIdeaSync) {
|
||||
groovy.srcDir("../kotlin-native/build-tools/src/main/groovy")
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named("compileGroovy", GroovyCompile::class.java) {
|
||||
classpath += project.files(tasks.named("compileKotlin", org.jetbrains.kotlin.gradle.tasks.KotlinCompile::class.java))
|
||||
dependsOn(tasks.named("compileKotlin"))
|
||||
}
|
||||
|
||||
allprojects {
|
||||
tasks.register("checkBuild")
|
||||
@@ -142,3 +226,40 @@ allprojects {
|
||||
apply(from = "$rootDir/../gradle/cacheRedirector.gradle.kts")
|
||||
}
|
||||
}
|
||||
|
||||
gradlePlugin {
|
||||
plugins {
|
||||
create("compileToBitcode") {
|
||||
id = "compile-to-bitcode"
|
||||
implementationClass = "org.jetbrains.kotlin.bitcode.CompileToBitcodePlugin"
|
||||
}
|
||||
create("runtimeTesting") {
|
||||
id = "runtime-testing"
|
||||
implementationClass = "org.jetbrains.kotlin.testing.native.RuntimeTestingPlugin"
|
||||
}
|
||||
create("konan") {
|
||||
id = "konan"
|
||||
implementationClass = "org.jetbrains.kotlin.gradle.plugin.konan.KonanPlugin"
|
||||
}
|
||||
// We bundle a shaded version of kotlinx-serialization plugin
|
||||
create("kotlinx-serialization-native") {
|
||||
id = "kotlinx-serialization-native"
|
||||
implementationClass = "shadow.org.jetbrains.kotlinx.serialization.gradle.SerializationGradleSubplugin"
|
||||
}
|
||||
|
||||
create("org.jetbrains.kotlin.konan") {
|
||||
id = "org.jetbrains.kotlin.konan"
|
||||
implementationClass = "org.jetbrains.kotlin.gradle.plugin.konan.KonanPlugin"
|
||||
}
|
||||
|
||||
create("native") {
|
||||
id = "native"
|
||||
implementationClass = "org.jetbrains.gradle.plugins.tools.NativePlugin"
|
||||
}
|
||||
|
||||
create("native-interop-plugin") {
|
||||
id = "native-interop-plugin"
|
||||
implementationClass = "org.jetbrains.kotlin.NativeInteropPlugin"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ buildscript {
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
classpath("org.jetbrains.kotlin:kotlin-build-gradle-plugin:0.0.21")
|
||||
classpath("org.jetbrains.kotlin:kotlin-build-gradle-plugin:0.0.25")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.konan
|
||||
import java.io.*
|
||||
|
||||
val VERSION_PATH = "/META-INF/kotlin-native.compiler.version"
|
||||
val CompilerVersion.Companion.CURRENT: CompilerVersion
|
||||
get() {
|
||||
return InputStreamReader(this::class.java.getResourceAsStream(VERSION_PATH)).use {
|
||||
it.readLines().single().parseCompilerVersion()
|
||||
}
|
||||
}
|
||||
|
||||
val currentCompilerVersion = CompilerVersion.CURRENT
|
||||
@@ -105,8 +105,8 @@ private val macOsJavaHomeOutRegexes =
|
||||
listOf(
|
||||
Regex("""\s+(\S+),\s+(\S+):\s+".*?"\s+(.+)"""),
|
||||
Regex("""\s+(\S+)\s+\((.*?)\):\s+(.+)"""),
|
||||
Regex("""\s+(\S+)\s+\((.*?)\)\s+"[^"]*"\s+-\s+"[^"]*"\s(.+)""")
|
||||
)
|
||||
Regex("""\s+(\S+)\s+\((.*?)\)\s+"[^"]*"\s+-\s+"[^"]*"\s(.+)"""),
|
||||
Regex("""\s+(\S+)\s+\((.+)\)\s+".+"\s+-\s+".+"\s+(.+)"""))
|
||||
|
||||
fun MutableCollection<JdkId>.discoverJdksOnMacOS(project: Project) {
|
||||
val procBuilder = ProcessBuilder("/usr/libexec/java_home", "-V").redirectErrorStream(true)
|
||||
|
||||
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.gradle.plugins.tools
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.plugins.BasePlugin
|
||||
import org.gradle.api.tasks.*
|
||||
import org.gradle.kotlin.dsl.apply
|
||||
import org.gradle.kotlin.dsl.withType
|
||||
import org.gradle.language.base.plugins.LifecycleBasePlugin
|
||||
import org.jetbrains.kotlin.konan.target.HostManager.Companion.hostIsMac
|
||||
import org.jetbrains.kotlin.konan.target.HostManager.Companion.hostIsMingw
|
||||
import java.io.File
|
||||
import kotlin.collections.List
|
||||
import kotlin.collections.MutableMap
|
||||
import kotlin.collections.addAll
|
||||
import kotlin.collections.drop
|
||||
import kotlin.collections.first
|
||||
import kotlin.collections.flatMap
|
||||
import kotlin.collections.forEach
|
||||
import kotlin.collections.listOf
|
||||
import kotlin.collections.map
|
||||
import kotlin.collections.mutableListOf
|
||||
import kotlin.collections.mutableMapOf
|
||||
import kotlin.collections.plusAssign
|
||||
import kotlin.collections.set
|
||||
import kotlin.collections.toTypedArray
|
||||
|
||||
open class NativePlugin : Plugin<Project> {
|
||||
override fun apply(project: Project) {
|
||||
project.apply<BasePlugin>()
|
||||
project.extensions.create("native", NativeToolsExtension::class.java, project)
|
||||
}
|
||||
}
|
||||
|
||||
abstract class ToolExecutionTask : DefaultTask() {
|
||||
@get:OutputFile
|
||||
abstract var output: File
|
||||
|
||||
@get:InputFiles
|
||||
abstract var input: List<File>
|
||||
|
||||
@get:Input
|
||||
abstract var cmd: String
|
||||
|
||||
@get:Input
|
||||
abstract var args: List<String>
|
||||
|
||||
@TaskAction
|
||||
fun action() {
|
||||
project.exec {
|
||||
executable(cmd)
|
||||
args(*this@ToolExecutionTask.args.toTypedArray())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ToolPatternImpl(val extension: NativeToolsExtension, val output:String, vararg val input: String):ToolPattern {
|
||||
val tool = mutableListOf<String>()
|
||||
val args = mutableListOf<String>()
|
||||
override fun ruleOut(): String = output
|
||||
override fun ruleInFirst(): String = input.first()
|
||||
override fun ruleInAll(): Array<String> = arrayOf(*input)
|
||||
|
||||
override fun flags(vararg args: String) {
|
||||
this.args.addAll(args)
|
||||
}
|
||||
|
||||
override fun tool(vararg arg: String) {
|
||||
tool.addAll(arg)
|
||||
}
|
||||
|
||||
override fun env(name: String) = emptyArray<String>()
|
||||
|
||||
fun configure(task: ToolExecutionTask, configureDepencies:Boolean) {
|
||||
extension.cleanupfiles += output
|
||||
task.input = input.map {
|
||||
extension.project.file(it)
|
||||
}
|
||||
task.dependsOn(":kotlin-native:dependencies:update")
|
||||
if (configureDepencies)
|
||||
task.input.forEach { task.dependsOn(it.name) }
|
||||
val file = extension.project.file(output)
|
||||
file.parentFile.mkdirs()
|
||||
task.output = file
|
||||
task.cmd = tool.first()
|
||||
task.args = listOf(*tool.drop(1).toTypedArray(), *args.toTypedArray())
|
||||
}
|
||||
}
|
||||
|
||||
open class SourceSet(
|
||||
val sourceSets: SourceSets,
|
||||
val name: String,
|
||||
val initialDirectory: File = sourceSets.project.projectDir,
|
||||
val initialSourceSet: SourceSet? = null,
|
||||
val rule: Pair<String, String>? = null
|
||||
) {
|
||||
var collection = sourceSets.project.objects.fileCollection() as FileCollection
|
||||
fun file(path: String) {
|
||||
collection = collection.plus(sourceSets.project.files("${initialDirectory.absolutePath}/$path"))
|
||||
}
|
||||
|
||||
fun dir(path: String) {
|
||||
sourceSets.project.fileTree("${initialDirectory.absolutePath}/$path").files.forEach {
|
||||
collection = collection.plus(sourceSets.project.files(it))
|
||||
}
|
||||
}
|
||||
|
||||
fun transform(suffixes: Pair<String, String>): SourceSet {
|
||||
return SourceSet(
|
||||
sourceSets,
|
||||
name,
|
||||
sourceSets.project.file("${sourceSets.project.buildDir}/$name/${suffixes.first}_${suffixes.second}/"),
|
||||
this,
|
||||
suffixes
|
||||
)
|
||||
}
|
||||
|
||||
fun implicitTasks(): Array<TaskProvider<*>> {
|
||||
rule ?: return emptyArray()
|
||||
initialSourceSet?.implicitTasks()
|
||||
return initialSourceSet!!.collection
|
||||
.filter { !it.isDirectory() }
|
||||
.filter { it.name.endsWith(rule.first) }
|
||||
.map { it.relativeTo(initialSourceSet.initialDirectory) }
|
||||
.map { it.path }
|
||||
.map { it to (it.substring(0, it.lastIndexOf(rule.first)) + rule.second) }
|
||||
.map {
|
||||
file(it.second)
|
||||
sourceSets.project.file("${initialSourceSet.initialDirectory.path}/${it.first}") to sourceSets.project.file("${initialDirectory.path}/${it.second}")
|
||||
}.map {
|
||||
sourceSets.project.tasks.register<ToolExecutionTask>(it.second.name, ToolExecutionTask::class.java) {
|
||||
val toolConfiguration = ToolPatternImpl(sourceSets.extension, it.second.path, it.first.path)
|
||||
sourceSets.extension.toolPatterns[rule]!!.invoke(toolConfiguration)
|
||||
toolConfiguration.configure(this, initialSourceSet.rule != null)
|
||||
}
|
||||
}.toTypedArray()
|
||||
}
|
||||
}
|
||||
|
||||
class SourceSets(val project: Project, val extension: NativeToolsExtension, val sources: MutableMap<String, SourceSet>) :
|
||||
MutableMap<String, SourceSet> by sources {
|
||||
operator fun String.invoke(initialDirectory: File = project.projectDir, configuration: SourceSet.() -> Unit) {
|
||||
sources[this] = SourceSet(this@SourceSets, this, initialDirectory).also {
|
||||
configuration(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
interface Environment {
|
||||
operator fun String.invoke(vararg values: String)
|
||||
}
|
||||
|
||||
interface ToolPattern {
|
||||
fun ruleOut(): String
|
||||
fun ruleInFirst(): String
|
||||
fun ruleInAll(): Array<String>
|
||||
fun flags(vararg args: String): Unit
|
||||
fun tool(vararg arg: String): Unit
|
||||
fun env(name: String): Array<String>
|
||||
}
|
||||
|
||||
|
||||
typealias ToolPatternConfiguration = ToolPattern.() -> Unit
|
||||
typealias EnvironmentConfiguration = Environment.() -> Unit
|
||||
|
||||
class ToolConfigurationPatterns(
|
||||
val extension: NativeToolsExtension,
|
||||
val patterns: MutableMap<Pair<String, String>, ToolPatternConfiguration>
|
||||
) : MutableMap<Pair<String, String>, ToolPatternConfiguration> by patterns {
|
||||
operator fun Pair<String, String>.invoke(configuration: ToolPatternConfiguration) {
|
||||
patterns[this] = configuration
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
open class NativeToolsExtension(val project: Project) {
|
||||
val sourceSets = SourceSets(project, this, mutableMapOf<String, SourceSet>())
|
||||
val toolPatterns = ToolConfigurationPatterns(this, mutableMapOf<Pair<String, String>, ToolPatternConfiguration>())
|
||||
val cleanupfiles = mutableListOf<String>()
|
||||
fun sourceSet(configuration: SourceSets.() -> Unit) {
|
||||
sourceSets.configuration()
|
||||
}
|
||||
|
||||
var environmentConfiguration: EnvironmentConfiguration? = null
|
||||
fun environment(configuration: EnvironmentConfiguration) {
|
||||
environmentConfiguration = configuration
|
||||
}
|
||||
|
||||
fun suffixes(configuration: ToolConfigurationPatterns.() -> Unit) = toolPatterns.configuration()
|
||||
|
||||
fun target(name: String, vararg objSet: SourceSet, configuration: ToolPatternConfiguration) {
|
||||
project.tasks.withType<Delete>().named(LifecycleBasePlugin.CLEAN_TASK_NAME).configure {
|
||||
doLast {
|
||||
delete(*this@NativeToolsExtension.cleanupfiles.toTypedArray())
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets.project.tasks.create<ToolExecutionTask>(name, ToolExecutionTask::class.java) {
|
||||
objSet.forEach {
|
||||
dependsOn(it.implicitTasks())
|
||||
}
|
||||
val deps = objSet.flatMap { it.collection.files }.map { it.path }
|
||||
val toolConfiguration = ToolPatternImpl(sourceSets.extension, "${project.buildDir.path}/$name", *deps.toTypedArray())
|
||||
toolConfiguration.configuration()
|
||||
toolConfiguration.configure(this, false )
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun solib(name: String) = when {
|
||||
hostIsMingw -> "$name.dll"
|
||||
hostIsMac -> "lib$name.dylib"
|
||||
else -> "lib$name.so"
|
||||
}
|
||||
|
||||
fun lib(name:String) = when {
|
||||
hostIsMingw -> "$name.lib"
|
||||
else -> "lib$name.a"
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
// DONT_TARGET_EXACT_BACKEND: WASM
|
||||
// IGNORE_BACKEND: JS
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// IGNORE_BACKEND: NATIVE
|
||||
// FILE: DiagnosticFactory0.java
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -60,7 +60,10 @@ class CodeConformanceTest : TestCase() {
|
||||
"libraries/tools/kotlin-test-nodejs-runner/node_modules",
|
||||
"libraries/tools/kotlinp/src",
|
||||
"libraries/tools/new-project-wizard/new-project-wizard-cli/build",
|
||||
"out"
|
||||
"out",
|
||||
"kotlin-native/build",
|
||||
"kotlin-native/performance",
|
||||
"kotlin-native/samples"
|
||||
)
|
||||
)
|
||||
|
||||
@@ -99,7 +102,9 @@ class CodeConformanceTest : TestCase() {
|
||||
"libraries/tools/kotlin-test-js-runner/node_modules",
|
||||
"libraries/tools/kotlin-test-nodejs-runner/.gradle",
|
||||
"libraries/tools/kotlin-test-nodejs-runner/node_modules",
|
||||
"out"
|
||||
"kotlin-native", // Have a separate licences manager
|
||||
"out",
|
||||
"kotlin-native/runtime"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -53,10 +53,8 @@ data class CompilerVersionImpl(
|
||||
append(major)
|
||||
append('.')
|
||||
append(minor)
|
||||
if (maintenance != 0) {
|
||||
append('.')
|
||||
append(maintenance)
|
||||
}
|
||||
append('.')
|
||||
append(maintenance)
|
||||
if (milestone != -1) {
|
||||
append("-M")
|
||||
append(milestone)
|
||||
|
||||
@@ -10,12 +10,15 @@ import org.jetbrains.kotlin.konan.file.*
|
||||
import java.lang.StringBuilder
|
||||
|
||||
fun <T> printMillisec(message: String, body: () -> T): T {
|
||||
val result: T
|
||||
val msec = measureTimeMillis {
|
||||
result = body()
|
||||
var msec = 0L
|
||||
try {
|
||||
msec = measureTimeMillis {
|
||||
return body()
|
||||
}
|
||||
} finally {
|
||||
println("$message: $msec msec")
|
||||
}
|
||||
println("$message: $msec msec")
|
||||
return result
|
||||
error("shouldn't happens")
|
||||
}
|
||||
|
||||
fun profile(message: String, body: () -> Unit) = profileIf(
|
||||
|
||||
@@ -4,7 +4,7 @@ plugins {
|
||||
}
|
||||
|
||||
group = "org.jetbrains.kotlin"
|
||||
version = "0.0.21"
|
||||
version = "0.0.25"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
@@ -29,10 +29,15 @@ java {
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java.setSrcDirs(listOf("src"))
|
||||
/*TODO: move version to build-plugin*/
|
||||
java.setSrcDirs(listOf("src", "../../compiler/util-io/src"))
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<GenerateModuleMetadata> {
|
||||
enabled = false
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
create<MavenPublication>("KotlinBuildGradlePlugin") {
|
||||
|
||||
@@ -120,6 +120,8 @@ class KotlinBuildProperties(
|
||||
val teamCityBootstrapUrl: String? = getOrNull("bootstrap.teamcity.url") as String?
|
||||
|
||||
val rootProjectDir: File = propertiesProvider.rootProjectDir
|
||||
|
||||
val isKotlinNativeEnabled: Boolean = getBoolean("kotlin.native.enabled")
|
||||
}
|
||||
|
||||
private const val extensionName = "kotlinBuildProperties"
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.tasks.*
|
||||
import java.io.*
|
||||
import java.util.regex.Pattern
|
||||
import java.util.Properties
|
||||
import org.gradle.api.Project
|
||||
import org.jetbrains.kotlin.konan.*
|
||||
|
||||
|
||||
fun Project.konanVersionGeneratedSrc() = rootProject.findProject(":kotlin-native")?.file("../buildSrc/build/version-generated/src/generated") ?: file("build/version-generated/src/generated")
|
||||
fun Project.kotlinNativeVersionSrc():File {
|
||||
val kotlinNativeProject = rootProject.findProject(":kotlin-native")
|
||||
return if(kotlinNativeProject != null){
|
||||
if (kotlinNativeVersionInResources)
|
||||
kotlinNativeProject.file("${findProperty("kotlin_root")!!}/buildSrc/src/kotlin-native-binary-version/kotlin")
|
||||
else
|
||||
kotlinNativeProject.file("../buildSrc/build/version-generated/src/generated")
|
||||
} else {
|
||||
if (kotlinNativeVersionInResources)
|
||||
file("src/kotlin-native-binary-version/kotlin")
|
||||
else
|
||||
file("build/version-generated/src/generated")
|
||||
}
|
||||
}
|
||||
fun Project.konanRootDir() = rootProject.findProject(":kotlin-native")?.projectDir ?: file("../kotlin-native")
|
||||
fun Project.kotlinNativeProperties() = Properties().apply{
|
||||
val kotlinNativeProperyFile = File(this@kotlinNativeProperties.konanRootDir(), "gradle.properties")
|
||||
if (!kotlinNativeProperyFile.exists())
|
||||
return@apply
|
||||
FileReader(kotlinNativeProperyFile).use {
|
||||
load(it)
|
||||
}
|
||||
}
|
||||
|
||||
val Project.kotlinNativeVersionInResources:Boolean
|
||||
get() = kotlinNativeProperties()["kotlinNativeVersionInResources"]?.toString()?.toBoolean() ?: false
|
||||
|
||||
fun Project.kotlinNativeVersionResourceFile() = File("${project.findProperty("kotlin_root")!!}/buildSrc/build/version-generated/META-INF/kotlin-native.compiler.version")
|
||||
fun Project.kotlinNativeVersionValue(): CompilerVersion? {
|
||||
return if (this.kotlinNativeVersionInResources)
|
||||
kotlinNativeVersionResourceFile().let { file ->
|
||||
file.readLines().single().parseCompilerVersion()
|
||||
}
|
||||
else null
|
||||
}
|
||||
|
||||
|
||||
open class VersionGenerator: DefaultTask() {
|
||||
private val kotlinNativeProperties = project.kotlinNativeProperties()
|
||||
|
||||
@Input
|
||||
var kotlinNativeVersionInResources = project.kotlinNativeVersionInResources
|
||||
|
||||
@OutputDirectory
|
||||
val versionSourceDirectory = project.file("build/version-generated")
|
||||
|
||||
@OutputFile
|
||||
open var versionFile: File? = project.file("${versionSourceDirectory.path}/org/jetbrains/kotlin/konan/CompilerVersionGenerated.kt")
|
||||
|
||||
@Input
|
||||
open val konanVersion = kotlinNativeProperties["konanVersion"].toString()
|
||||
|
||||
|
||||
// TeamCity passes all configuration parameters into a build script as project properties.
|
||||
// Thus we can use them here instead of environment variables.
|
||||
@Optional
|
||||
@Input
|
||||
open val buildNumber = project.findProperty("build.number")?.toString()
|
||||
|
||||
@Input
|
||||
open val meta = kotlinNativeProperties["konanMetaVersion"]?.let{ MetaVersion.valueOf(it.toString().toUpperCase()) } ?: MetaVersion.DEV
|
||||
|
||||
private val versionPattern = Pattern.compile(
|
||||
"^(\\d+)\\.(\\d+)(?:\\.(\\d+))?(?:-M(\\p{Digit}))?(?:-(\\p{Alpha}\\p{Alnum}*))?(?:-(\\d+))?$"
|
||||
)
|
||||
|
||||
fun defaultVersionFileLocation() {
|
||||
versionFile = if (kotlinNativeVersionInResources)
|
||||
project.file("${versionSourceDirectory.path}/META-INF/kotlin-native.compiler.version")
|
||||
else
|
||||
project.file("${versionSourceDirectory.path}/org/jetbrains/kotlin/konan/CompilerVersionGenerated.kt")
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
open fun generateVersion() {
|
||||
val matcher = versionPattern.matcher(konanVersion)
|
||||
require(matcher.matches()) { "Cannot parse Kotlin/Native version: \$konanVersion" }
|
||||
val major = matcher.group(1).toInt()
|
||||
val minor = matcher.group(2).toInt()
|
||||
val maintenanceStr = matcher.group(3)
|
||||
val maintenance = maintenanceStr?.toInt() ?: 0
|
||||
val milestoneStr = matcher.group(4)
|
||||
val milestone = milestoneStr?.toInt() ?: -1
|
||||
project.logger.info("BUILD_NUMBER: $buildNumber")
|
||||
var build = -1
|
||||
if (buildNumber != null) {
|
||||
val buildNumberSplit = buildNumber!!.split("-".toRegex()).toTypedArray()
|
||||
build = buildNumberSplit[buildNumberSplit.size - 1].toInt() // //7-dev-buildcount
|
||||
}
|
||||
|
||||
val versionObject = CompilerVersionImpl(meta, major, minor, maintenance, milestone, build)
|
||||
versionObject.serialize()
|
||||
|
||||
|
||||
}
|
||||
|
||||
private fun CompilerVersion.serialize() {
|
||||
versionFile!!.parentFile.mkdirs()
|
||||
project.logger.info("version file: ${versionFile}")
|
||||
if (!kotlinNativeVersionInResources) {
|
||||
PrintWriter(versionFile).use { printWriter ->
|
||||
printWriter.println(
|
||||
"""|package org.jetbrains.kotlin.konan
|
||||
|internal val currentCompilerVersion: CompilerVersion =
|
||||
|CompilerVersionImpl($meta, $major, $minor,
|
||||
| $maintenance, $milestone, $build)
|
||||
|val CompilerVersion.Companion.CURRENT: CompilerVersion
|
||||
|get() = currentCompilerVersion""".trimMargin()
|
||||
)
|
||||
}
|
||||
} else {
|
||||
PrintStream(versionFile).use {
|
||||
it.println(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,3 +42,5 @@ kotlin.stdlib.default.dependency=false
|
||||
kotlin.internal.mpp12x.deprecation.suppress=true
|
||||
kotlin.mpp.stability.nowarn=true
|
||||
kotlin.2js.nowarn=true
|
||||
kotlin.native.enabled=false
|
||||
kotlin.native.home=kotlin-native/dist
|
||||
|
||||
@@ -77,7 +77,8 @@ val mirroredUrls = listOf(
|
||||
"https://www.python.org/ftp",
|
||||
"https://www.jetbrains.com/intellij-repository/nightly",
|
||||
"https://www.jetbrains.com/intellij-repository/releases",
|
||||
"https://www.jetbrains.com/intellij-repository/snapshots"
|
||||
"https://www.jetbrains.com/intellij-repository/snapshots",
|
||||
"https://kotlin.bintray.com/kotlinx"
|
||||
)
|
||||
|
||||
val aliases = mapOf(
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
# Also see codestyle/cpp/README.md
|
||||
# For documentation on options see: https://releases.llvm.org/8.0.0/tools/clang/docs/ClangFormatStyleOptions.html
|
||||
# "Kt N/A" means that this C/C++/ObjC construct have no corresponding Kotlin construct to refer to
|
||||
# "Kt U" means that formatting is unspecified in Kotlin formatting guide
|
||||
# "Kt <url>" means that this is specified in Kotlin formatting guide with url pointing to it
|
||||
---
|
||||
DisableFormat: false
|
||||
Standard: Cpp11
|
||||
|
||||
# Kt N/A. Different from 0 to make modifiers stand out. An alternative is -2, but it introduces another level of indentation.
|
||||
AccessModifierOffset: -4
|
||||
# Kt https://kotlinlang.org/docs/reference/coding-conventions.html#method-call-formatting
|
||||
AlignAfterOpenBracket: AlwaysBreak
|
||||
# Kt U. Not touching
|
||||
AlignConsecutiveAssignments: false
|
||||
# Kt U. Not touching
|
||||
AlignConsecutiveDeclarations: false
|
||||
# Kt N/A. Not touching
|
||||
AlignEscapedNewlines: DontAlign
|
||||
# Kt U. Not touching
|
||||
AlignOperands: false
|
||||
# Kt U. Not touching
|
||||
AlignTrailingComments: false
|
||||
# Kt https://kotlinlang.org/docs/reference/coding-conventions.html#function-formatting
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
# Kt https://kotlinlang.org/docs/reference/coding-conventions.html#formatting-control-flow-statements
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
# Kt U. The closest is https://kotlinlang.org/docs/reference/coding-conventions.html#formatting-control-flow-statements
|
||||
# Using false, because case statements usually contain at least 2 statements: doing something + break, which makes them multiline
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
# Kt https://kotlinlang.org/docs/reference/coding-conventions.html#function-formatting Inline is somewhat close to "single expression" functions
|
||||
AllowShortFunctionsOnASingleLine: Inline
|
||||
# Kt U. The closest is https://kotlinlang.org/docs/reference/coding-conventions.html#using-conditional-statements however it mostly refers to the ternary operator.
|
||||
AllowShortIfStatementsOnASingleLine: true
|
||||
# Kt U. Same as previous
|
||||
AllowShortLoopsOnASingleLine: true
|
||||
# Kt N/A. In Kotlin return type is in a trailing position.
|
||||
AlwaysBreakAfterReturnType: None
|
||||
# Kt U. Not touching.
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
# Kt N/A. In Kotlin type parameters are declared inline in a much more compact way.
|
||||
# Using Yes to make it easy to detect function name
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
# Kt https://kotlinlang.org/docs/reference/coding-conventions.html#method-call-formatting
|
||||
BinPackArguments: true
|
||||
# Kt https://kotlinlang.org/docs/reference/coding-conventions.html#function-formatting
|
||||
BinPackParameters: false
|
||||
BraceWrapping:
|
||||
# Kt https://kotlinlang.org/docs/reference/coding-conventions.html#formatting
|
||||
AfterClass: false
|
||||
# Kt https://kotlinlang.org/docs/reference/coding-conventions.html#formatting
|
||||
AfterControlStatement: false
|
||||
# Kt https://kotlinlang.org/docs/reference/coding-conventions.html#formatting
|
||||
AfterEnum: false
|
||||
# Kt https://kotlinlang.org/docs/reference/coding-conventions.html#formatting
|
||||
AfterFunction: false
|
||||
# Kt https://kotlinlang.org/docs/reference/coding-conventions.html#formatting
|
||||
AfterNamespace: false
|
||||
# Kt https://kotlinlang.org/docs/reference/coding-conventions.html#formatting
|
||||
AfterObjCDeclaration: false
|
||||
# Kt https://kotlinlang.org/docs/reference/coding-conventions.html#formatting
|
||||
AfterStruct: false
|
||||
# Kt https://kotlinlang.org/docs/reference/coding-conventions.html#formatting
|
||||
AfterUnion: false
|
||||
# Kt https://kotlinlang.org/docs/reference/coding-conventions.html#formatting
|
||||
AfterExternBlock: false
|
||||
# Kt https://kotlinlang.org/docs/reference/coding-conventions.html#formatting-control-flow-statements
|
||||
BeforeCatch: false
|
||||
# Kt https://kotlinlang.org/docs/reference/coding-conventions.html#formatting-control-flow-statements
|
||||
BeforeElse: false
|
||||
# Kt https://kotlinlang.org/docs/reference/coding-conventions.html#formatting
|
||||
IndentBraces: false
|
||||
# Only used when AfterFunction is true
|
||||
SplitEmptyFunction: true
|
||||
# Only used when AfterRecord is true
|
||||
SplitEmptyRecord: true
|
||||
# Only used if AfterNamespace is true
|
||||
SplitEmptyNamespace: true
|
||||
# Kt U. Break after operators
|
||||
BreakBeforeBinaryOperators: None
|
||||
# Configured by BraceWrapping
|
||||
BreakBeforeBraces: Custom
|
||||
# Kt U. Using true because it looks more like an if-expression with "then" and "else" branches marked "?" and ":"
|
||||
BreakBeforeTernaryOperators: true
|
||||
# Kt N/A. The closest is https://kotlinlang.org/docs/reference/coding-conventions.html#class-header-formatting AfterColon looks the most consistent
|
||||
BreakConstructorInitializers: AfterColon
|
||||
# Kt https://kotlinlang.org/docs/reference/coding-conventions.html#class-header-formatting
|
||||
BreakInheritanceList: AfterColon
|
||||
# Kt U. Choosing to break long strings to fit into line length
|
||||
BreakStringLiterals: true
|
||||
# Kt U. IDEA displays a vertical guide at 120. Choosing it to avoid very long lines.
|
||||
ColumnLimit: 140
|
||||
# Kt N/A. Probably should use nested namespaces instead.
|
||||
CompactNamespaces: false
|
||||
# Kt N/A. The closest is https://kotlinlang.org/docs/reference/coding-conventions.html#class-header-formatting
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||
# Kt https://kotlinlang.org/docs/reference/coding-conventions.html#formatting
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
# Kt https://kotlinlang.org/docs/reference/coding-conventions.html#formatting IDEA seems to indent continuations with 8
|
||||
ContinuationIndentWidth: 8
|
||||
# Kt N/A. The closest is https://kotlinlang.org/docs/reference/coding-conventions.html#horizontal-whitespace
|
||||
# Braced initalizer braces are close to parens in function call or to brackets in array initialier. And styleguide asks not to put spaces in them.
|
||||
Cpp11BracedListStyle: true
|
||||
# Configured by PointerAlignment
|
||||
DerivePointerAlignment: false
|
||||
# Confgured by BinPack*
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
# Kt N/A. Setting to true because it helps to see when anonymous namespace ends
|
||||
FixNamespaceComments: true
|
||||
# Kt U. Not touching manually created including blocks
|
||||
IncludeBlocks: Preserve
|
||||
# Kt U. Sorting: main header (priority 0) > system headers > project headers
|
||||
IncludeCategories:
|
||||
- Regex: '^<.*'
|
||||
Priority: 1
|
||||
- Regex: '.*'
|
||||
Priority: 2
|
||||
# Kt N/A. Main header must match current filename (modulo extension) exactly.
|
||||
IncludeIsMainRegex: '$'
|
||||
# Kt https://kotlinlang.org/docs/reference/coding-conventions.html#formatting-control-flow-statements
|
||||
IndentCaseLabels: true
|
||||
# Kt N/A. Do not indent macro code
|
||||
IndentPPDirectives: None
|
||||
# Kt https://kotlinlang.org/docs/reference/coding-conventions.html#formatting
|
||||
IndentWidth: 4
|
||||
# Kt U. IDEA seems to indent
|
||||
IndentWrappedFunctionNames: true
|
||||
# Kt U. Do not touch.
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
# Kt U. IDEA keeps 1 empty line by default.
|
||||
MaxEmptyLinesToKeep: 1
|
||||
# Kt N/A.
|
||||
NamespaceIndentation: None
|
||||
# Kt N/A. The closest is https://kotlinlang.org/docs/reference/coding-conventions.html#class-header-formatting
|
||||
ObjCBinPackProtocolList: Never
|
||||
# Kt https://kotlinlang.org/docs/reference/coding-conventions.html#formatting
|
||||
ObjCBlockIndentWidth: 4
|
||||
# Kt N/A. Following Google and LLVM styleguides.
|
||||
ObjCSpaceAfterProperty: false
|
||||
# Kt N/A. The closest is class inheritance list. Following Google and LLVM styleguides.
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
# Kt N/A.
|
||||
PointerAlignment: Left
|
||||
# Kt U. Reflow comments to fit into line width
|
||||
ReflowComments: true
|
||||
# Kt U. IDEA does not sort by default, but allows this option. Do not touch.
|
||||
SortIncludes: false
|
||||
# Kt U. Like SortIncludes.
|
||||
SortUsingDeclarations: false
|
||||
# Kt N/A.
|
||||
SpaceAfterCStyleCast: false
|
||||
# Kt N/A. IDEA puts space in `fun <T>`
|
||||
SpaceAfterTemplateKeyword: true
|
||||
# Kt U. https://kotlinlang.org/docs/reference/coding-conventions.html puts spaces
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
# Kt N/A. The closest is https://kotlinlang.org/docs/reference/coding-conventions.html#horizontal-whitespace which does not put space before parens and brackets.
|
||||
SpaceBeforeCpp11BracedList: false
|
||||
# Kt N/A. The closest is https://kotlinlang.org/docs/reference/coding-conventions.html#colon
|
||||
SpaceBeforeCtorInitializerColon: true
|
||||
# Kt https://kotlinlang.org/docs/reference/coding-conventions.html#colon
|
||||
SpaceBeforeInheritanceColon: true
|
||||
# Kt https://kotlinlang.org/docs/reference/coding-conventions.html#horizontal-whitespace
|
||||
SpaceBeforeParens: ControlStatements
|
||||
# Kt https://kotlinlang.org/docs/reference/coding-conventions.html#horizontal-whitespace
|
||||
SpaceBeforeRangeBasedForLoopColon: true
|
||||
# Kt https://kotlinlang.org/docs/reference/coding-conventions.html#horizontal-whitespace
|
||||
SpaceInEmptyParentheses: false
|
||||
# Kt U. https://kotlinlang.org/docs/reference/coding-conventions.html uses 1
|
||||
SpacesBeforeTrailingComments: 1
|
||||
# Kt https://kotlinlang.org/docs/reference/coding-conventions.html#horizontal-whitespace
|
||||
SpacesInAngles: false
|
||||
# Kt https://kotlinlang.org/docs/reference/coding-conventions.html#horizontal-whitespace
|
||||
SpacesInContainerLiterals: false
|
||||
# Kt N/A.
|
||||
SpacesInCStyleCastParentheses: false
|
||||
# Kt https://kotlinlang.org/docs/reference/coding-conventions.html#horizontal-whitespace
|
||||
SpacesInParentheses: false
|
||||
# Kt https://kotlinlang.org/docs/reference/coding-conventions.html#horizontal-whitespace
|
||||
SpacesInSquareBrackets: false
|
||||
# Kt https://kotlinlang.org/docs/reference/coding-conventions.html#formatting
|
||||
TabWidth: 4
|
||||
# Kt https://kotlinlang.org/docs/reference/coding-conventions.html#formatting
|
||||
UseTab: Never
|
||||
|
||||
PenaltyBreakAssignment: 2
|
||||
PenaltyBreakBeforeFirstCallParameter: 1
|
||||
PenaltyBreakComment: 300
|
||||
PenaltyBreakFirstLessLess: 120
|
||||
PenaltyBreakString: 1000
|
||||
PenaltyBreakTemplateDeclaration: 10
|
||||
PenaltyExcessCharacter: 1000000
|
||||
PenaltyReturnTypeOnItsOwnLine: 200
|
||||
...
|
||||
|
||||
@@ -0,0 +1,336 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
ij_continuation_indent_size = 8
|
||||
ij_formatter_off_tag = @formatter:off
|
||||
ij_formatter_on_tag = @formatter:on
|
||||
ij_formatter_tags_enabled = false
|
||||
ij_smart_tabs = false
|
||||
ij_wrap_on_typing = false
|
||||
|
||||
[*.java]
|
||||
ij_java_align_consecutive_assignments = false
|
||||
ij_java_align_consecutive_variable_declarations = false
|
||||
ij_java_align_group_field_declarations = false
|
||||
ij_java_align_multiline_annotation_parameters = false
|
||||
ij_java_align_multiline_array_initializer_expression = false
|
||||
ij_java_align_multiline_assignment = false
|
||||
ij_java_align_multiline_binary_operation = false
|
||||
ij_java_align_multiline_chained_methods = false
|
||||
ij_java_align_multiline_extends_list = false
|
||||
ij_java_align_multiline_for = true
|
||||
ij_java_align_multiline_method_parentheses = false
|
||||
ij_java_align_multiline_parameters = true
|
||||
ij_java_align_multiline_parameters_in_calls = false
|
||||
ij_java_align_multiline_parenthesized_expression = false
|
||||
ij_java_align_multiline_records = true
|
||||
ij_java_align_multiline_resources = true
|
||||
ij_java_align_multiline_ternary_operation = false
|
||||
ij_java_align_multiline_text_blocks = false
|
||||
ij_java_align_multiline_throws_list = false
|
||||
ij_java_align_subsequent_simple_methods = false
|
||||
ij_java_align_throws_keyword = false
|
||||
ij_java_annotation_parameter_wrap = off
|
||||
ij_java_array_initializer_new_line_after_left_brace = false
|
||||
ij_java_array_initializer_right_brace_on_new_line = false
|
||||
ij_java_array_initializer_wrap = off
|
||||
ij_java_assert_statement_colon_on_next_line = false
|
||||
ij_java_assert_statement_wrap = off
|
||||
ij_java_assignment_wrap = off
|
||||
ij_java_binary_operation_sign_on_next_line = false
|
||||
ij_java_binary_operation_wrap = off
|
||||
ij_java_blank_lines_after_anonymous_class_header = 0
|
||||
ij_java_blank_lines_after_class_header = 0
|
||||
ij_java_blank_lines_after_imports = 1
|
||||
ij_java_blank_lines_after_package = 1
|
||||
ij_java_blank_lines_around_class = 1
|
||||
ij_java_blank_lines_around_field = 0
|
||||
ij_java_blank_lines_around_field_in_interface = 0
|
||||
ij_java_blank_lines_around_initializer = 1
|
||||
ij_java_blank_lines_around_method = 1
|
||||
ij_java_blank_lines_around_method_in_interface = 1
|
||||
ij_java_blank_lines_before_class_end = 0
|
||||
ij_java_blank_lines_before_imports = 1
|
||||
ij_java_blank_lines_before_method_body = 0
|
||||
ij_java_blank_lines_before_package = 0
|
||||
ij_java_block_brace_style = end_of_line
|
||||
ij_java_block_comment_at_first_column = true
|
||||
ij_java_call_parameters_new_line_after_left_paren = false
|
||||
ij_java_call_parameters_right_paren_on_new_line = false
|
||||
ij_java_call_parameters_wrap = off
|
||||
ij_java_case_statement_on_separate_line = true
|
||||
ij_java_catch_on_new_line = false
|
||||
ij_java_class_annotation_wrap = split_into_lines
|
||||
ij_java_class_brace_style = end_of_line
|
||||
ij_java_class_count_to_use_import_on_demand = 5
|
||||
ij_java_class_names_in_javadoc = 1
|
||||
ij_java_do_not_indent_top_level_class_members = false
|
||||
ij_java_do_not_wrap_after_single_annotation = false
|
||||
ij_java_do_while_brace_force = never
|
||||
ij_java_doc_add_blank_line_after_description = true
|
||||
ij_java_doc_add_blank_line_after_param_comments = false
|
||||
ij_java_doc_add_blank_line_after_return = false
|
||||
ij_java_doc_add_p_tag_on_empty_lines = true
|
||||
ij_java_doc_align_exception_comments = true
|
||||
ij_java_doc_align_param_comments = true
|
||||
ij_java_doc_do_not_wrap_if_one_line = false
|
||||
ij_java_doc_enable_formatting = true
|
||||
ij_java_doc_enable_leading_asterisks = true
|
||||
ij_java_doc_indent_on_continuation = false
|
||||
ij_java_doc_keep_empty_lines = true
|
||||
ij_java_doc_keep_empty_parameter_tag = true
|
||||
ij_java_doc_keep_empty_return_tag = true
|
||||
ij_java_doc_keep_empty_throws_tag = true
|
||||
ij_java_doc_keep_invalid_tags = true
|
||||
ij_java_doc_param_description_on_new_line = false
|
||||
ij_java_doc_preserve_line_breaks = false
|
||||
ij_java_doc_use_throws_not_exception_tag = true
|
||||
ij_java_else_on_new_line = false
|
||||
ij_java_entity_dd_suffix = EJB
|
||||
ij_java_entity_eb_suffix = Bean
|
||||
ij_java_entity_hi_suffix = Home
|
||||
ij_java_entity_lhi_prefix = Local
|
||||
ij_java_entity_lhi_suffix = Home
|
||||
ij_java_entity_li_prefix = Local
|
||||
ij_java_entity_pk_class = java.lang.String
|
||||
ij_java_entity_vo_suffix = VO
|
||||
ij_java_enum_constants_wrap = off
|
||||
ij_java_extends_keyword_wrap = off
|
||||
ij_java_extends_list_wrap = off
|
||||
ij_java_field_annotation_wrap = split_into_lines
|
||||
ij_java_finally_on_new_line = false
|
||||
ij_java_for_brace_force = never
|
||||
ij_java_for_statement_new_line_after_left_paren = false
|
||||
ij_java_for_statement_right_paren_on_new_line = false
|
||||
ij_java_for_statement_wrap = off
|
||||
ij_java_generate_final_locals = false
|
||||
ij_java_generate_final_parameters = false
|
||||
ij_java_if_brace_force = never
|
||||
ij_java_imports_layout = *,|,javax.**,java.**,|,$*
|
||||
ij_java_indent_case_from_switch = true
|
||||
ij_java_insert_inner_class_imports = false
|
||||
ij_java_insert_override_annotation = true
|
||||
ij_java_keep_blank_lines_before_right_brace = 2
|
||||
ij_java_keep_blank_lines_between_package_declaration_and_header = 2
|
||||
ij_java_keep_blank_lines_in_code = 2
|
||||
ij_java_keep_blank_lines_in_declarations = 2
|
||||
ij_java_keep_control_statement_in_one_line = true
|
||||
ij_java_keep_first_column_comment = true
|
||||
ij_java_keep_indents_on_empty_lines = false
|
||||
ij_java_keep_line_breaks = true
|
||||
ij_java_keep_multiple_expressions_in_one_line = false
|
||||
ij_java_keep_simple_blocks_in_one_line = false
|
||||
ij_java_keep_simple_classes_in_one_line = false
|
||||
ij_java_keep_simple_lambdas_in_one_line = false
|
||||
ij_java_keep_simple_methods_in_one_line = false
|
||||
ij_java_label_indent_absolute = false
|
||||
ij_java_label_indent_size = 0
|
||||
ij_java_lambda_brace_style = end_of_line
|
||||
ij_java_layout_static_imports_separately = true
|
||||
ij_java_line_comment_add_space = false
|
||||
ij_java_line_comment_at_first_column = true
|
||||
ij_java_message_dd_suffix = EJB
|
||||
ij_java_message_eb_suffix = Bean
|
||||
ij_java_method_annotation_wrap = split_into_lines
|
||||
ij_java_method_brace_style = end_of_line
|
||||
ij_java_method_call_chain_wrap = off
|
||||
ij_java_method_parameters_new_line_after_left_paren = false
|
||||
ij_java_method_parameters_right_paren_on_new_line = false
|
||||
ij_java_method_parameters_wrap = off
|
||||
ij_java_modifier_list_wrap = false
|
||||
ij_java_names_count_to_use_import_on_demand = 3
|
||||
ij_java_new_line_after_lparen_in_record_header = false
|
||||
ij_java_packages_to_use_import_on_demand = java.awt.*,javax.swing.*
|
||||
ij_java_parameter_annotation_wrap = off
|
||||
ij_java_parentheses_expression_new_line_after_left_paren = false
|
||||
ij_java_parentheses_expression_right_paren_on_new_line = false
|
||||
ij_java_place_assignment_sign_on_next_line = false
|
||||
ij_java_prefer_longer_names = true
|
||||
ij_java_prefer_parameters_wrap = false
|
||||
ij_java_record_components_wrap = normal
|
||||
ij_java_repeat_synchronized = true
|
||||
ij_java_replace_instanceof_and_cast = false
|
||||
ij_java_replace_null_check = true
|
||||
ij_java_replace_sum_lambda_with_method_ref = true
|
||||
ij_java_resource_list_new_line_after_left_paren = false
|
||||
ij_java_resource_list_right_paren_on_new_line = false
|
||||
ij_java_resource_list_wrap = off
|
||||
ij_java_rparen_on_new_line_in_record_header = false
|
||||
ij_java_session_dd_suffix = EJB
|
||||
ij_java_session_eb_suffix = Bean
|
||||
ij_java_session_hi_suffix = Home
|
||||
ij_java_session_lhi_prefix = Local
|
||||
ij_java_session_lhi_suffix = Home
|
||||
ij_java_session_li_prefix = Local
|
||||
ij_java_session_si_suffix = Service
|
||||
ij_java_space_after_closing_angle_bracket_in_type_argument = false
|
||||
ij_java_space_after_colon = true
|
||||
ij_java_space_after_comma = true
|
||||
ij_java_space_after_comma_in_type_arguments = true
|
||||
ij_java_space_after_for_semicolon = true
|
||||
ij_java_space_after_quest = true
|
||||
ij_java_space_after_type_cast = true
|
||||
ij_java_space_before_annotation_array_initializer_left_brace = false
|
||||
ij_java_space_before_annotation_parameter_list = false
|
||||
ij_java_space_before_array_initializer_left_brace = false
|
||||
ij_java_space_before_catch_keyword = true
|
||||
ij_java_space_before_catch_left_brace = true
|
||||
ij_java_space_before_catch_parentheses = true
|
||||
ij_java_space_before_class_left_brace = true
|
||||
ij_java_space_before_colon = true
|
||||
ij_java_space_before_colon_in_foreach = true
|
||||
ij_java_space_before_comma = false
|
||||
ij_java_space_before_do_left_brace = true
|
||||
ij_java_space_before_else_keyword = true
|
||||
ij_java_space_before_else_left_brace = true
|
||||
ij_java_space_before_finally_keyword = true
|
||||
ij_java_space_before_finally_left_brace = true
|
||||
ij_java_space_before_for_left_brace = true
|
||||
ij_java_space_before_for_parentheses = true
|
||||
ij_java_space_before_for_semicolon = false
|
||||
ij_java_space_before_if_left_brace = true
|
||||
ij_java_space_before_if_parentheses = true
|
||||
ij_java_space_before_method_call_parentheses = false
|
||||
ij_java_space_before_method_left_brace = true
|
||||
ij_java_space_before_method_parentheses = false
|
||||
ij_java_space_before_opening_angle_bracket_in_type_parameter = false
|
||||
ij_java_space_before_quest = true
|
||||
ij_java_space_before_switch_left_brace = true
|
||||
ij_java_space_before_switch_parentheses = true
|
||||
ij_java_space_before_synchronized_left_brace = true
|
||||
ij_java_space_before_synchronized_parentheses = true
|
||||
ij_java_space_before_try_left_brace = true
|
||||
ij_java_space_before_try_parentheses = true
|
||||
ij_java_space_before_type_parameter_list = false
|
||||
ij_java_space_before_while_keyword = true
|
||||
ij_java_space_before_while_left_brace = true
|
||||
ij_java_space_before_while_parentheses = true
|
||||
ij_java_space_inside_one_line_enum_braces = false
|
||||
ij_java_space_within_empty_array_initializer_braces = false
|
||||
ij_java_space_within_empty_method_call_parentheses = false
|
||||
ij_java_space_within_empty_method_parentheses = false
|
||||
ij_java_spaces_around_additive_operators = true
|
||||
ij_java_spaces_around_assignment_operators = true
|
||||
ij_java_spaces_around_bitwise_operators = true
|
||||
ij_java_spaces_around_equality_operators = true
|
||||
ij_java_spaces_around_lambda_arrow = true
|
||||
ij_java_spaces_around_logical_operators = true
|
||||
ij_java_spaces_around_method_ref_dbl_colon = false
|
||||
ij_java_spaces_around_multiplicative_operators = true
|
||||
ij_java_spaces_around_relational_operators = true
|
||||
ij_java_spaces_around_shift_operators = true
|
||||
ij_java_spaces_around_type_bounds_in_type_parameters = true
|
||||
ij_java_spaces_around_unary_operator = false
|
||||
ij_java_spaces_within_angle_brackets = false
|
||||
ij_java_spaces_within_annotation_parentheses = false
|
||||
ij_java_spaces_within_array_initializer_braces = false
|
||||
ij_java_spaces_within_braces = false
|
||||
ij_java_spaces_within_brackets = false
|
||||
ij_java_spaces_within_cast_parentheses = false
|
||||
ij_java_spaces_within_catch_parentheses = false
|
||||
ij_java_spaces_within_for_parentheses = false
|
||||
ij_java_spaces_within_if_parentheses = false
|
||||
ij_java_spaces_within_method_call_parentheses = false
|
||||
ij_java_spaces_within_method_parentheses = false
|
||||
ij_java_spaces_within_parentheses = false
|
||||
ij_java_spaces_within_switch_parentheses = false
|
||||
ij_java_spaces_within_synchronized_parentheses = false
|
||||
ij_java_spaces_within_try_parentheses = false
|
||||
ij_java_spaces_within_while_parentheses = false
|
||||
ij_java_special_else_if_treatment = true
|
||||
ij_java_subclass_name_suffix = Impl
|
||||
ij_java_ternary_operation_signs_on_next_line = false
|
||||
ij_java_ternary_operation_wrap = off
|
||||
ij_java_test_name_suffix = Test
|
||||
ij_java_throws_keyword_wrap = off
|
||||
ij_java_throws_list_wrap = off
|
||||
ij_java_use_external_annotations = false
|
||||
ij_java_use_fq_class_names = false
|
||||
ij_java_use_relative_indents = false
|
||||
ij_java_use_single_class_imports = true
|
||||
ij_java_variable_annotation_wrap = off
|
||||
ij_java_visibility = public
|
||||
ij_java_while_brace_force = never
|
||||
ij_java_while_on_new_line = false
|
||||
ij_java_wrap_comments = false
|
||||
ij_java_wrap_first_method_in_call_chain = false
|
||||
ij_java_wrap_long_lines = false
|
||||
|
||||
[{*.gradle.kts,*.kt,*.kts,*.main.kts,KotlinBuildPusher}]
|
||||
ij_kotlin_align_in_columns_case_branch = false
|
||||
ij_kotlin_align_multiline_binary_operation = false
|
||||
ij_kotlin_align_multiline_extends_list = false
|
||||
ij_kotlin_align_multiline_method_parentheses = false
|
||||
ij_kotlin_align_multiline_parameters = true
|
||||
ij_kotlin_align_multiline_parameters_in_calls = false
|
||||
ij_kotlin_allow_trailing_comma = false
|
||||
ij_kotlin_allow_trailing_comma_on_call_site = false
|
||||
ij_kotlin_assignment_wrap = off
|
||||
ij_kotlin_blank_lines_after_class_header = 0
|
||||
ij_kotlin_blank_lines_around_block_when_branches = 0
|
||||
ij_kotlin_blank_lines_before_declaration_with_comment_or_annotation_on_separate_line = 1
|
||||
ij_kotlin_block_comment_at_first_column = true
|
||||
ij_kotlin_call_parameters_new_line_after_left_paren = false
|
||||
ij_kotlin_call_parameters_right_paren_on_new_line = false
|
||||
ij_kotlin_call_parameters_wrap = off
|
||||
ij_kotlin_catch_on_new_line = false
|
||||
ij_kotlin_class_annotation_wrap = split_into_lines
|
||||
ij_kotlin_continuation_indent_for_chained_calls = true
|
||||
ij_kotlin_continuation_indent_for_expression_bodies = true
|
||||
ij_kotlin_continuation_indent_in_argument_lists = true
|
||||
ij_kotlin_continuation_indent_in_elvis = true
|
||||
ij_kotlin_continuation_indent_in_if_conditions = true
|
||||
ij_kotlin_continuation_indent_in_parameter_lists = true
|
||||
ij_kotlin_continuation_indent_in_supertype_lists = true
|
||||
ij_kotlin_else_on_new_line = false
|
||||
ij_kotlin_enum_constants_wrap = off
|
||||
ij_kotlin_extends_list_wrap = off
|
||||
ij_kotlin_field_annotation_wrap = split_into_lines
|
||||
ij_kotlin_finally_on_new_line = false
|
||||
ij_kotlin_if_rparen_on_new_line = false
|
||||
ij_kotlin_import_nested_classes = false
|
||||
ij_kotlin_insert_whitespaces_in_simple_one_line_method = true
|
||||
ij_kotlin_keep_blank_lines_before_right_brace = 2
|
||||
ij_kotlin_keep_blank_lines_in_code = 2
|
||||
ij_kotlin_keep_blank_lines_in_declarations = 2
|
||||
ij_kotlin_keep_first_column_comment = true
|
||||
ij_kotlin_keep_indents_on_empty_lines = false
|
||||
ij_kotlin_keep_line_breaks = true
|
||||
ij_kotlin_lbrace_on_next_line = false
|
||||
ij_kotlin_line_comment_add_space = false
|
||||
ij_kotlin_line_comment_at_first_column = true
|
||||
ij_kotlin_method_annotation_wrap = split_into_lines
|
||||
ij_kotlin_method_call_chain_wrap = off
|
||||
ij_kotlin_method_parameters_new_line_after_left_paren = false
|
||||
ij_kotlin_method_parameters_right_paren_on_new_line = false
|
||||
ij_kotlin_method_parameters_wrap = off
|
||||
ij_kotlin_name_count_to_use_star_import = 5
|
||||
ij_kotlin_name_count_to_use_star_import_for_members = 3
|
||||
ij_kotlin_parameter_annotation_wrap = off
|
||||
ij_kotlin_space_after_comma = true
|
||||
ij_kotlin_space_after_extend_colon = true
|
||||
ij_kotlin_space_after_type_colon = true
|
||||
ij_kotlin_space_before_catch_parentheses = true
|
||||
ij_kotlin_space_before_comma = false
|
||||
ij_kotlin_space_before_extend_colon = true
|
||||
ij_kotlin_space_before_for_parentheses = true
|
||||
ij_kotlin_space_before_if_parentheses = true
|
||||
ij_kotlin_space_before_lambda_arrow = true
|
||||
ij_kotlin_space_before_type_colon = false
|
||||
ij_kotlin_space_before_when_parentheses = true
|
||||
ij_kotlin_space_before_while_parentheses = true
|
||||
ij_kotlin_spaces_around_additive_operators = true
|
||||
ij_kotlin_spaces_around_assignment_operators = true
|
||||
ij_kotlin_spaces_around_equality_operators = true
|
||||
ij_kotlin_spaces_around_function_type_arrow = true
|
||||
ij_kotlin_spaces_around_logical_operators = true
|
||||
ij_kotlin_spaces_around_multiplicative_operators = true
|
||||
ij_kotlin_spaces_around_range = false
|
||||
ij_kotlin_spaces_around_relational_operators = true
|
||||
ij_kotlin_spaces_around_unary_operator = false
|
||||
ij_kotlin_spaces_around_when_arrow = true
|
||||
ij_kotlin_variable_annotation_wrap = off
|
||||
ij_kotlin_while_on_new_line = false
|
||||
ij_kotlin_wrap_elvis_expressions = 1
|
||||
ij_kotlin_wrap_expression_body_functions = 0
|
||||
ij_kotlin_wrap_first_method_in_call_chain = false
|
||||
@@ -0,0 +1,80 @@
|
||||
.DS_Store
|
||||
.idea/shelf
|
||||
*.iml
|
||||
/dependencies/all
|
||||
dist
|
||||
kotlin-native-*.tar.gz
|
||||
kotlin-native-*.zip
|
||||
translator/src/test/kotlin/tests/*/linked
|
||||
out
|
||||
tmp
|
||||
workspace.xml
|
||||
*.versionsBackup
|
||||
|
||||
local.properties
|
||||
.gradle
|
||||
build
|
||||
translator/.gradle/2.9/taskArtifacts
|
||||
|
||||
kotstd/kotstd.iml
|
||||
|
||||
# test suit products.
|
||||
*.bc
|
||||
*.bc.o
|
||||
*.kt.S
|
||||
*.kt.exe
|
||||
*.log
|
||||
test.output
|
||||
*.kexe
|
||||
|
||||
# Ignore Gradle GUI config
|
||||
gradle-app.setting
|
||||
|
||||
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
|
||||
!gradle-wrapper.jar
|
||||
|
||||
# Cache of project
|
||||
.gradletasknamecache
|
||||
|
||||
# local project files
|
||||
lib/
|
||||
.idea/
|
||||
proto/compiler/protoc-artifacts
|
||||
proto/compiler/tests
|
||||
|
||||
proto/compiler/google/src/google/protobuf/compiler/kotlin/bin
|
||||
proto/compiler/google/src/google/protobuf/compiler/kotlin/protoc
|
||||
|
||||
peformance/build
|
||||
|
||||
# translator auto generated artifacts
|
||||
kotstd/ll
|
||||
|
||||
# test teamcity property: commitable only with -f
|
||||
backend.native/tests/teamcity-test.property
|
||||
|
||||
# Sample output
|
||||
samples/**/*.kt.bc-build
|
||||
samples/androidNativeActivity/Polyhedron
|
||||
|
||||
# CMake
|
||||
llvmCoverageMappingC/CMakeLists.txt
|
||||
tools/performance-server/node_modules
|
||||
tools/performance-server/server
|
||||
tools/performance-server/ui/js
|
||||
runtime/cmake-build-debug/
|
||||
|
||||
# compilation database
|
||||
compile_commands.json
|
||||
|
||||
# clangd caches
|
||||
.clangd/
|
||||
|
||||
# googletest framework used by runtime tests
|
||||
runtime/googletest/
|
||||
|
||||
# Toolchain builder artifacts
|
||||
tools/toolchain_builder/artifacts
|
||||
|
||||
# ignore the project model created by CLion (for now).
|
||||
runtime/.idea
|
||||
Generated
+20
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="IssueNavigationConfiguration">
|
||||
<option name="links">
|
||||
<list>
|
||||
<IssueNavigationLink>
|
||||
<option name="issueRegexp" value="((KT|KTI|IDEA)\-(\d+))" />
|
||||
<option name="linkRegexp" value="https://youtrack.jetbrains.com/issue/$1" />
|
||||
</IssueNavigationLink>
|
||||
<IssueNavigationLink>
|
||||
<option name="issueRegexp" value="#(\d+)" />
|
||||
<option name="linkRegexp" value="https://github.com/JetBrains/kotlin-native/issues/$1" />
|
||||
</IssueNavigationLink>
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -0,0 +1,65 @@
|
||||
# Building Apple LLVM for Kotlin/Native
|
||||
|
||||
This document describes how to compile LLVM distribution and use it to build Kotlin/Native on macOS.
|
||||
Usually, you don't need to compile LLVM by yourself:
|
||||
* If you use Kotlin/Native compiler it will download LLVM on the first run.
|
||||
* If you contribute to kotlin-native repository then use `:dependencies:update` Gradle task.
|
||||
|
||||
But if you don't want to download prebuilt LLVM or want to experiment with your own distribution,
|
||||
you came to the right place.
|
||||
|
||||
## Part 1. Building the right LLVM version for macOS.
|
||||
|
||||
For macOS host we use LLVM from [Apple downstream](https://github.com/apple/llvm-project).
|
||||
Branch is [**apple/stable/20190104**](https://github.com/apple/llvm-project/tree/apple/stable/20190104)
|
||||
because it is similar (or even the same) to what Apple ships with Xcode 11.*.
|
||||
After cloning the repo and changing the branch we perform the following steps to build LLVM toolchain:
|
||||
|
||||
```bash
|
||||
mkdir build
|
||||
|
||||
cd build
|
||||
|
||||
cmake -DLLVM_ENABLE_PROJECTS="clang;lld;libcxx;libcxxabi" \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DLLVM_ENABLE_ASSERTIONS=Off \
|
||||
-G Ninja \
|
||||
-DCMAKE_INSTALL_PREFIX=clang-llvm-apple-8.0.0-darwin-macos \
|
||||
../llvm
|
||||
|
||||
ninja install
|
||||
```
|
||||
|
||||
After these steps `clang-llvm-apple-8.0.0-darwin-macos` directory will contain LLVM distribution that is suitable for building Kotlin/Native.
|
||||
|
||||
## Part 2. Building Kotlin/Native against given LLVM distribution.
|
||||
|
||||
By default, Kotlin/Native will try to download LLVM distribution from CDN if it is not present in `$HOME/.konan/dependencies` folder.
|
||||
There are two ways to bypass this behaviour.
|
||||
|
||||
#### Option A. Substitute prebuilt distribution.
|
||||
This option doesn't require you to edit compiler sources, but a bit harder.
|
||||
|
||||
The compiler checks dependency presence by reading contents of `$HOME/.konan/dependencies/.extracted` file.
|
||||
So to avoid LLVM downloading, we should manually add a record to the `.extracted` file:
|
||||
1. Create `$HOME/.konan/dependencies/.extracted` file if it is not created.
|
||||
2. Add `clang-llvm-apple-8.0.0-darwin-macos` line.
|
||||
|
||||
and put `clang-llvm-apple-8.0.0-darwin-macos` directory from the Part 1 to `$HOME/.konan/dependencies/`.
|
||||
|
||||
#### Option B. Provide an absolute path to the distribution.
|
||||
This option requires user to edit [konan.properties file](konan/konan.properties).
|
||||
Set `llvmHome.<HOST_NAME>` to an absolute path to your LLVM distribution and
|
||||
set `llvmVersion.<HOST_NAME>` to its version.
|
||||
For example, provide a path to `clang-llvm-apple-8.0.0-darwin-macos` from the Part 1 and set version to 8.0.0.
|
||||
|
||||
Now we are ready to build Kotlin/Native itself. The process is described in [README.md](README.md).
|
||||
Please note that we still need to run `./gradlew dependencies:update` to download other dependencies (e.g. libffi).
|
||||
|
||||
## Q&A
|
||||
|
||||
— Can I override `.konan` location?
|
||||
— Yes, by setting `$KONAN_DATA_DIR` environment variable. See [HACKING.md](HACKING.md#compiler-environment-variables).
|
||||
|
||||
- Can I use another LLVM distribution without rebuilding Kotlin/Native?
|
||||
- Yes, see [HACKING.md](HACKING.md#using-different-llvm-distributions-as-part-of-kotlinnative-compilation-pipeline).
|
||||
@@ -0,0 +1,359 @@
|
||||
# 1.4.30 (Feb 2021)
|
||||
* [KT-44083](https://youtrack.jetbrains.com/issue/KT-44083) Fix NSUInteger size for Watchos x64
|
||||
|
||||
# 1.4.30-RC (Jan 2021)
|
||||
* [KT-44271](https://youtrack.jetbrains.com/issue/KT-44271) Incorrect linking when targeting linux_x64 from mingw_x64 host
|
||||
* [KT-44219](https://youtrack.jetbrains.com/issue/KT-44219) Non-reified type parameters with recursive bounds are not supported yet
|
||||
* [KT-43599](https://youtrack.jetbrains.com/issue/KT-43599) K/N: Unbound symbols not allowed
|
||||
* [KT-42172](https://youtrack.jetbrains.com/issue/KT-42172) Kotlin/Native: StableRef.dispose race condition on Kotlin deinitRuntime
|
||||
* [KT-42482](https://youtrack.jetbrains.com/issue/KT-42482) Kotlin subclasses of Obj-C classes are incompatible with ISA swizzling (it causes crashes)
|
||||
|
||||
# 1.4.30-M1 (Dec 2020)
|
||||
* [KT-43597](https://youtrack.jetbrains.com/issue/KT-43597) Xcode 12.2 support
|
||||
* [KT-43276](https://youtrack.jetbrains.com/issue/KT-43276) Add watchos_x64 target
|
||||
* [KT-43198](https://youtrack.jetbrains.com/issue/KT-43198) Init blocks inside of inline classes
|
||||
* [KT-42649](https://youtrack.jetbrains.com/issue/KT-42649) Fix secondary constructors of generic inline classes
|
||||
* [KT-38772](https://youtrack.jetbrains.com/issue/KT-38772) Support non-reified type parameters in typeOf
|
||||
* [KT-42428](https://youtrack.jetbrains.com/issue/KT-42428) Inconsistent behavior of map.entries on Kotlin.Native
|
||||
* Compiler customization
|
||||
* [KT-40584](https://youtrack.jetbrains.com/issue/KT-40584) Untie Kotlin/Native from the fixed LLVM distribution
|
||||
* [KT-42234](https://youtrack.jetbrains.com/issue/KT-42234) Move LLVM optimization parameters into konan.properties
|
||||
* [KT-40670](https://youtrack.jetbrains.com/issue/KT-40670) Allow to override konan.properties via CLI
|
||||
* Runtime
|
||||
* [KT-42822](https://youtrack.jetbrains.com/issue/KT-42822) Kotlin/Native Worker leaks ObjC/Swift autorelease references (and indirectly bridged K/N references) on Darwin targets
|
||||
* [KT-42397](https://youtrack.jetbrains.com/issue/KT-42397) Reverse-C interop usage of companion object reports spurious leaks
|
||||
* [GH-4482](https://github.com/JetBrains/kotlin-native/pull/4482) Add a switch to destroy runtime only on shutdown
|
||||
* [GH-4575](https://github.com/JetBrains/kotlin-native/pull/4575) Fix unchecked runtime shutdown
|
||||
* [GH-4194](https://github.com/JetBrains/kotlin-native/pull/4194) Fix possible race in terminate handler
|
||||
* C-interop
|
||||
* [KT-42412](https://youtrack.jetbrains.com/issue/KT-42412) Modality of generated property accessors is always FINAL
|
||||
* [KT-38530](https://youtrack.jetbrains.com/issue/KT-38530) values() method of enum classes is not exposed to Objective-C/Swift
|
||||
* [GH-4572](https://github.com/JetBrains/kotlin-native/pull/4572) Fix for interop enum and struct generation
|
||||
* Optimizations
|
||||
* [KT-42294](https://youtrack.jetbrains.com/issue/KT-42294) Significantly improved compilation time
|
||||
* [KT-42942](https://youtrack.jetbrains.com/issue/KT-42942) Optimize peak backend memory by clearing BindingContext after psi2ir
|
||||
* [KT-31072](https://youtrack.jetbrains.com/issue/KT-31072) Don't use non-reified arguments to specialize type operations in IR inliner
|
||||
|
||||
# 1.4.21 (Dec 2020)
|
||||
* Fixed [KT-43517](https://youtrack.jetbrains.com/issue/KT-43517)
|
||||
* Fixed [KT-43530](https://youtrack.jetbrains.com/issue/KT-43530)
|
||||
* Fixed [KT-43265](https://youtrack.jetbrains.com/issue/KT-43265)
|
||||
|
||||
# 1.4.20 (Nov 2020)
|
||||
* XCode 12 support
|
||||
* Completely reworked escape analysis for object allocation
|
||||
* Use ForeignException wrapper to handle native exceptions ([GH-3553](https://github.com/JetBrains/kotlin-native/issues/3553))
|
||||
* CocoaPods plugin improvements
|
||||
* equals/hashCode support for adapted callable references ([KT-39800](https://youtrack.jetbrains.com/issue/KT-39800))
|
||||
* equals/hashCode support for fun interfaces ([KT-39798](https://youtrack.jetbrains.com/issue/KT-39798))
|
||||
* IR-level optimizations
|
||||
* Constant folding
|
||||
* String concatenation flattening
|
||||
* Various fixes/improvements to compiler caches
|
||||
* Some fixes to samples (calculator, tensorflow)
|
||||
* Bug fixes
|
||||
* Eliminate recursive GC calls ([KT-42275](https://youtrack.jetbrains.com/issue/KT-42275))
|
||||
* Fix support for @OverrideInit constructors with default arguments ([KT-41910](https://youtrack.jetbrains.com/issue/KT-41910))
|
||||
* Fix support for forward declarations ([KT-41655](https://youtrack.jetbrains.com/issue/KT-41655))
|
||||
* [KT-41394](https://youtrack.jetbrains.com/issue/KT-41394)
|
||||
* [KT-41811](https://youtrack.jetbrains.com/issue/KT-41811)
|
||||
* [KT-41716](https://youtrack.jetbrains.com/issue/KT-41716)
|
||||
* [KT-41250](https://youtrack.jetbrains.com/issue/KT-41250)
|
||||
* [KT-42000](https://youtrack.jetbrains.com/issue/KT-42000)
|
||||
* [KT-41907](https://youtrack.jetbrains.com/issue/KT-41907)
|
||||
|
||||
# 1.4.10 (Sep 2020)
|
||||
* Fixed a newline handling in @Deprecated annotation in ObjCExport ([KT-39206](https://youtrack.jetbrains.com/issue/KT-39206))
|
||||
* Fixed suspend function types in ObjCExport ([KT-40976](https://youtrack.jetbrains.com/issue/KT-40976))
|
||||
* Fixed support for unsupported C declarations in cinterop ([KT-39762](https://youtrack.jetbrains.com/issue/KT-39762))
|
||||
|
||||
# v1.4.0 (Aug 2020)
|
||||
* Objective-C/Swift interop:
|
||||
* Reworked exception handling ([GH-3822](https://github.com/JetBrains/kotlin-native/pull/3822), [GH-3842](https://github.com/JetBrains/kotlin-native/pull/3842))
|
||||
* Enabled support for Objective-C generics by default ([GH-3778](https://github.com/JetBrains/kotlin-native/pull/3778))
|
||||
* Support for Kotlin’s suspending functions ([GH-3915](https://github.com/JetBrains/kotlin-native/pull/3915))
|
||||
* Handle variadic block types in ObjC interop ([`KT-36766`](https://youtrack.jetbrains.com/issue/KT-36766))
|
||||
* Added native-specific frontend checkers (implemented in the main Kotlin repository: [GH-3293](https://github.com/JetBrains/kotlin/pull/3293), [GH-3091](https://github.com/JetBrains/kotlin/pull/3091), [GH-3172](https://github.com/JetBrains/kotlin/pull/3172))
|
||||
* .dSYMs for release binaries on Apple platforms ([GH-4085](https://github.com/JetBrains/kotlin-native/pull/4085))
|
||||
* Improved compilation time of builds with interop libraries by reworking cinterop under the hood.
|
||||
* Experimental mimalloc allocator support (-Xallocator=mimalloc) to improve execution time performance. ([GH-3704](https://github.com/JetBrains/kotlin-native/pull/3704))
|
||||
* Tune GC to improve execution time performance
|
||||
* Various fixes to compiler caches and Gradle daemon usage
|
||||
|
||||
# v1.3.72 (April 2020)
|
||||
* Fix ios_x64 platform libs cache for iOS 11 and 12 (GH-4071)
|
||||
|
||||
# v1.3.71 (March 2020)
|
||||
* Fix `lazy {}` memory leak regression ([`KT-37232`](https://youtrack.jetbrains.com/issue/KT-37232), GH-3944)
|
||||
* Fix using cached Kotlin subclasses of Objective-C classes (GH-3986)
|
||||
|
||||
# v1.3.70 (Dec 2019)
|
||||
* Support compiler caches for debug mode (GH-3650)
|
||||
* Support running Kotlin/Native compiler from Gradle daemon (GH-3442)
|
||||
* Support multiple independent Kotlin frameworks in the same application (GH-3457)
|
||||
* Compile-time allocation for some singleton objects (GH-3645)
|
||||
* Native support for SIMD vector types in compiler and interop (GH-3498)
|
||||
* API for runtime detector of cyclic garbage (GH-3616)
|
||||
* Commonized StringBuilder (GH-3593) and Float.rangeTo (KT-35299)
|
||||
* Fix interop with localized strings (GH-3562)
|
||||
* Provide utility for user-side generation of platform libraries (GH-3538)
|
||||
* On-stack allocation using local escape analysis (GH-3625)
|
||||
* Code coverage support on Linux and Windows (GH-3403)
|
||||
* Debugging experience improvements (GH-3561, GH-3638, GH-3606)
|
||||
|
||||
# v1.3.60 (Oct 2019)
|
||||
* Support XCode 11
|
||||
* Switch to LLVM 8.0
|
||||
* New compiler targets:
|
||||
* watchOS targets, watchos_x86, watchos_arm64 and watchos_arm32 (GH-3323, GH-3404, GH-3344)
|
||||
* tvOS targets tvos_x64 and tvos_arm64 (GH-3303, GH-3363)
|
||||
* native Android targets android_x86 and android_x64 (GH-3306, GH-3314)
|
||||
* Standard CLI library kotlinx.cli is shipped with the compiler distribution (GH-3215)
|
||||
* Improved debug information for inline functions (KT-28929, GH-3292)
|
||||
* Improved runtime performance of interface calls, up to 5x faster (GH-3377)
|
||||
* Improved runtime performance of type checks, up to 50x faster (GH-3291)
|
||||
* Produce native binaries directly from klibs, speeds up large project compilation (GH-3246)
|
||||
* Supported arbitrary (up to 255 inclusive) function arity (GH-3253)
|
||||
* Supported callable references to suspend functions (GH-3197)
|
||||
* Implemented experimental -Xg0 switch, symbolication of release binaries for iOS (GH-3233, GH-3367)
|
||||
* Interop:
|
||||
* Allow passing untyped null as variadic function's parameter (GH-3312, KT-33525)
|
||||
* Standard library:
|
||||
* Allow scheduling jobs in arbitrary K/N context, not only Worker (GH-3316)
|
||||
* Important bug fixes:
|
||||
* Boxed negative values can lead to crashes on ios_arm64 (GH-3296)
|
||||
* Implemented thread-safe tracking of Objective-C references to Kotlin objects (GH-3267)
|
||||
|
||||
# v1.3.50 (Aug 2019)
|
||||
* Kotlin/Native versioning now aligned with Kotlin versioning
|
||||
* Exhaustive platform libraries on macOS (GH-3141)
|
||||
* Update to Gradle 5.5 (GH-3166)
|
||||
* Improved debug information correctness (GH-3130)
|
||||
* Major memory manager refactoring (GH-3129)
|
||||
* Embed actual bitcode in produced frameworks (GH-2974)
|
||||
* Compilation speed improvements
|
||||
* Interop:
|
||||
* Support kotlin.Deprecated when producing framework (GH-3114)
|
||||
* Ensure produced Objective-C header does not have warnings (GH-3101)
|
||||
* Speed up interop stub generator (GH-3082, GH-3050)
|
||||
* getOriginalKotlinClass() to get KClass for Kotlin classes in Objective-C (GH-3036)
|
||||
* Supported nullable primitive types in reverse C interop (GH-3198)
|
||||
* Standard library
|
||||
* API for delayed job execution on worker (GH-2971)
|
||||
* API for running via worker's job queue (GH-3078)
|
||||
* MonoClock and Duration support (GH-3028)
|
||||
* Support typeOf (KT-29917, KT-28625)
|
||||
* New zero-terminated utf8 to String conversion API (GH-3116)
|
||||
* Optimize StringBuilder for certain cases (GH-3202)
|
||||
* Implemented Array.fill API (GH-3244)
|
||||
|
||||
# v1.3.0 (Jun 2019)
|
||||
* CoreLocation platform library on macOS (GH-3041)
|
||||
* Converting Unit type to Void during producing framework for Objective-C/Swift (GH-2549, GH-1271)
|
||||
* Support linux/arm64 targets (GH-2917)
|
||||
* Performance improvements of memory manager (GH-2813)
|
||||
* FreezableAtomicReference prototype (GH-2776)
|
||||
* Logging and error messages enhancements
|
||||
* Interop:
|
||||
* Support nullable String return type in reverse C interop (GH-2956)
|
||||
* Support setting custom exception hook in reverse C interop (GH-2941)
|
||||
* Experimental generics support for produced frameworks for Objective-C/Swift implemented by Kevin Galligan (GH-2850)
|
||||
* Improve support for Objective-C methods clashing with methods of Any (GH-2914)
|
||||
* Support variadic Objective-C functions (GH-2896)
|
||||
|
||||
# v1.2.1 (Apr 2019)
|
||||
* Fix Objective-C interop with React (GH-2872)
|
||||
* Fix “not in vtable” compiler crash when generating frameworks (GH-2865)
|
||||
* Implement some optimizations (GH-2854)
|
||||
* Fix release build for 32-bit Windows (GH-2848)
|
||||
* Fix casts to type parameters with multiple bounds (GH-2888)
|
||||
* Fix “could not get descriptor uniq id for deserialized class FlagEnum” compiler crash when generating framework (GH-2874)
|
||||
|
||||
# v1.2.0 (Apr 2019)
|
||||
* New intermediate representation based library format allowing global optimizations
|
||||
* Exception backtraces in debug mode on macOS and iOS targets contains symbolic information
|
||||
* Support for 32-bit Windows targets (target mingw_x86)
|
||||
* Support for cross-compilation to Linux (x86-64 and arm32) from macOS and Windows hosts
|
||||
* Static Apple frameworks can be produced
|
||||
* Support Gradle 5.1
|
||||
* Fix alignment-related issues on ARM32 and MIPS platforms
|
||||
* Write unhandled exceptions stacktrace on device to iOS crash log
|
||||
* Fix undefined behavior in some arithmetic operations
|
||||
* Interop:
|
||||
* Get rid of libffi dependency
|
||||
* Support returning struct from C callbacks
|
||||
* Support passing Kotlin strings to C interop functions accepting UTF-32 arguments
|
||||
* Fix bool conversion
|
||||
* Support variable length arrays
|
||||
* Provide Kotlin access to C compiler intrinsics via platform.builtins package
|
||||
* Support clang modules (for Objective-C only)
|
||||
* Experimental integration with CocoaPods
|
||||
* IDE
|
||||
* Kotlin/Native plugin is supported in CLion 2018.3 and AppCode/CLion 2019.1
|
||||
* Basic highlighting support for .def files
|
||||
* Navigation to source files from exception backtrace
|
||||
|
||||
## v1.1.0 (Dec 2018)
|
||||
* Performance optimizations:
|
||||
* runtime: optimization of queue of finalization
|
||||
* compiler: loop generation optimization
|
||||
* compiler: reduce RTTI size
|
||||
* runtime: reduce size of the object header
|
||||
* Contracts support
|
||||
* Regex engine: fix quantifier processing
|
||||
|
||||
## v0.9.3 (Sep 2018)
|
||||
* Bugfixes
|
||||
|
||||
## v0.9.2 (Sep 2018)
|
||||
* Support Xcode 10.0
|
||||
* iOS 9.0 is the minimal supported version for all targets
|
||||
* Swift interop improvements
|
||||
* Support shared top level values of some immutable types (i.e. String and atomic references)
|
||||
* Support release Kotlin 1.3.0
|
||||
|
||||
## v0.9.1 (Sep 2018)
|
||||
* Improve naming in produced Objective-C frameworks. Use ‘Kotlin’ prefix instead of ‘Stdlib’ prefix.
|
||||
* Improvements in KLIB: Library versioning, IDEA-friendly internal format.
|
||||
|
||||
# v0.9 (Sep 2018)
|
||||
* Support Kotlin 1.3M2
|
||||
* Note: Common modules of multiplatform projects also should use Kotlin 1.3
|
||||
* Major standard library (native parts) rework and rename
|
||||
* New Gradle plugin with multiplatform integration and reworked DSL
|
||||
* Support unsigned types in Kotlin and interop
|
||||
* Support non-experimental coroutines API (kotlin.coroutines)
|
||||
* Top level object var/val can only be accessed from the main thread
|
||||
* Support lazy properties in singleton objects
|
||||
* Update LLVM to 6.0.1
|
||||
|
||||
## v0.8 (Jul 2018)
|
||||
* Singleton objects are frozen after creation, and shared between threads
|
||||
* String and primitives types are frozen by default
|
||||
* Common stdlib with Kotlin/JVM and Kotlin/JS
|
||||
* Implemented `kotlin.random.*` and `Collection.shuffle`
|
||||
* Implemented atomic integers and atomic references
|
||||
* Multiple bugfixes in compiler (coroutines, inliner)
|
||||
* Support 32-bit iOS (target `ios_arm32`)
|
||||
* New experimental Gradle plugin
|
||||
* Support Xcode 9.4.1
|
||||
* Optimizations (switch by enum, memory management)
|
||||
|
||||
## v0.7.1 (Jun 2018)
|
||||
* Bugfixes in the runtime (indexOf, GC for kotlin.Array, enum equality) and the compiler
|
||||
* Fix NSBlock problem, preventing upload of binaries to the AppStore
|
||||
* Create primitive type boxes and kotlin.String as frozen by default
|
||||
* Support Gradle 4.7, provide separate run task for each executable
|
||||
* Support Xcode 9.4 and CoreML and ClassKit frameworks on Apple platforms
|
||||
* Improved runtime Kotlin variable examination
|
||||
* Minor performance optimizations in compiled code and runtime
|
||||
* Add `disableDesignatedInitializerChecks` definition file support
|
||||
|
||||
## v0.7 (May 2018)
|
||||
* Interop with Objective-C/Swift changes:
|
||||
* Uniform direct and reverse interops (values could be passed in both directions now)
|
||||
* Interop by exceptions
|
||||
* Type conversion and checks (`as`, `is`) for interop types
|
||||
* Seamless interop on numbers, strings, lists, maps and sets
|
||||
* Better interop on constructors and initializers
|
||||
* Switched to Xcode 9.3 on Apple platforms
|
||||
* Introduced object freezing API, frozen object could be used from multiple threads
|
||||
* Kotlin enums are frozen by default
|
||||
* Switch to Gradle 4.6
|
||||
* Use Gradle native dependency model, allowing to use `.klib` as Maven artifacts
|
||||
* Introduced typed arrays API
|
||||
* Introduced weak references API
|
||||
* Activated global devirtualization analysis
|
||||
* Performance improvements (box caching, bridge inlining, others)
|
||||
|
||||
## v0.6.2 (Mar 2018)
|
||||
* Support several `expectedBy`-dependencies in Gradle plugin.
|
||||
* Improved interaction between Gradle plugin and IDE.
|
||||
* Various bugfixes
|
||||
|
||||
## v0.6.1 (Mar 2018)
|
||||
* Various bugfixes
|
||||
* Support total ordering in FP comparisons
|
||||
* Interop generates string constants from string macrodefinitions
|
||||
* STM32 blinky demo in pure Kotlin/Native
|
||||
* Top level variables initialization redesign (proper dependency order)
|
||||
* Support kotlin.math on WebAssembly targets
|
||||
* Support embedded targets on Windows hosts
|
||||
|
||||
## v0.6 (Feb 2018)
|
||||
* Support multiplatform projects (expect/actual) in compiler and Gradle plugin
|
||||
* Support first embedded target (STM32 board)
|
||||
* Support Kotlin 1.2.20
|
||||
* Support Java 9
|
||||
* Support Gradle 4.5
|
||||
* Transparent Objective-C/Kotlin container classes interoperability
|
||||
* Produce optimized WebAssembly binaries (10x smaller than it used to be)
|
||||
* Improved APIs for object transfer between threads and workers
|
||||
* Allow exporting top level C function in reverse interop with @CName annotation
|
||||
* Supported debugging of code with inline functions
|
||||
* Multiple bugfixes and performance optimizations
|
||||
|
||||
## v0.5 (Dec 2017)
|
||||
* Reverse interop allowing to call Kotlin/Native code compiled as framework from Objective-C/Swift programs
|
||||
* Reverse interop allowing to call Kotlin/Native code compiled as shared object from C/C++ programs
|
||||
* Support generation of shared objects and DLLs by the compiler
|
||||
* Migration to LLVM 5.0
|
||||
* Support WebAssembly target on Linux and Windows hosts
|
||||
* Make string conversions more robust
|
||||
* Support kotlin.math package
|
||||
* Refine workers and string conversion APIs
|
||||
|
||||
## v0.4 (Nov 2017) ##
|
||||
* Objective-C frameworks interop for iOS and macOS targets
|
||||
* Platform API libraries for Linux, iOS, macOS and Windows
|
||||
* Kotlin 1.2 supported
|
||||
* `val` and function parameters can be inspected in debugger
|
||||
* Experimental support for WebAssembly (wasm32 target)
|
||||
* Linux MIPS support (little and big endian, mips and mipsel targets)
|
||||
* Gradle plugin DSL fully reworked
|
||||
* Support for unit testing annotations and automatic test runner generation
|
||||
* Final executable size reduced
|
||||
* Various interop improvements (forward declaration, better handling of unsupported types)
|
||||
* Workers object subgraph transfer checks implemented
|
||||
* Optimized low level memory management using more efficient cycle tracing algorithm
|
||||
|
||||
## v0.3.4 (Oct 2017) ##
|
||||
* Intermediate release
|
||||
|
||||
## v0.3.2 (Sep 2017) ##
|
||||
* Bug fixes
|
||||
|
||||
## v0.3.1 (Aug 2017) ##
|
||||
* Improvements in C interop tools (function pointers, bitfields, bugfixes)
|
||||
* Improvements to Gradle plugin and dependency downloader
|
||||
* Support for immutable data linked into an executable via ImmutableDataBlob class
|
||||
* Kotlin 1.1.4 supported
|
||||
* Basic variable inspection support in the debugger
|
||||
* Some performance improvements ("for" loops, memory management)
|
||||
* .klib improvements (keep options from .def file, faster inline handling)
|
||||
* experimental workers API added (see [`sample`](https://github.com/JetBrains/kotlin-native/blob/master/samples/workers))
|
||||
|
||||
## v0.3 (Jun 2017) ##
|
||||
* Preliminary support for x86-64 Windows hosts and targets
|
||||
* Support for producing native activities on 32- and 64-bit Android targets
|
||||
* Extended standard library (bitsets, character classification, regular expression)
|
||||
* Preliminary support for Kotlin/Native library format (.klib)
|
||||
* Preliminary source-level debugging support (stepping only, no variable inspection)
|
||||
* Compiler switch `-entry` to select entry point
|
||||
* Symbolic backtrace in runtime for unstripped binaries, for all supported targets
|
||||
|
||||
## v0.2 (May 2017) ##
|
||||
* Added support for coroutines
|
||||
* Fixed most stdlib incompatibilities
|
||||
* Improved memory management performance
|
||||
* Cross-module inline function support
|
||||
* Unicode support independent from installed system locales
|
||||
* Interoperability improvements
|
||||
* file-based filtering in definition file
|
||||
* stateless lambdas could be used as C callbacks
|
||||
* any Unicode string could be passed to C function
|
||||
* Very basic debugging support
|
||||
* Improve compilation and linking performance
|
||||
|
||||
## v0.1 (Mar 2017) ##
|
||||
Initial technical preview of Kotlin/Native
|
||||
@@ -0,0 +1,3 @@
|
||||
# CocoaPods integration
|
||||
|
||||
The content of this page is moved to https://kotlinlang.org/docs/native-cocoapods.html
|
||||
@@ -0,0 +1,67 @@
|
||||
# Code Coverage
|
||||
Kotlin/Native has a code coverage support that is based on Clang's
|
||||
[Source-based Code Coverage](https://clang.llvm.org/docs/SourceBasedCodeCoverage.html).
|
||||
|
||||
**Please note**:
|
||||
1. Coverage support is in it's very early days and is in active development. Known issues are:
|
||||
* Coverage information may be inaccurate.
|
||||
* Line execution counts may be wrong.
|
||||
|
||||
2. Most of described functionality will be incorporated into Gradle plugin.
|
||||
|
||||
### Usage
|
||||
|
||||
#### TL;DR
|
||||
```bash
|
||||
kotlinc-native main.kt -Xcoverage
|
||||
./program.kexe
|
||||
llvm-profdata merge program.kexe.profraw -o program.profdata
|
||||
llvm-cov report program.kexe -instr-profile program.profdata
|
||||
```
|
||||
|
||||
#### Compiling with coverage enabled
|
||||
|
||||
There are 2 compiler flags that allows to generate coverage information:
|
||||
* `-Xcoverage`. Generate coverage for immediate sources.
|
||||
* `-Xlibrary-to-cover=<path>`. Generate coverage for specified `klib`.
|
||||
Note that library also should be either linked via `-library/-l` compiler option or be a transitive dependency.
|
||||
|
||||
#### Running covered executable
|
||||
|
||||
After the execution of the compiled binary (ex. `program.kexe`) `program.kexe.profraw` will be generated.
|
||||
By default it will be generated in the same location where binary was created. The are two ways to override this behavior:
|
||||
* `-Xcoverage-file=<path>` compiler flag.
|
||||
* `LLVM_PROFILE_FILE` environment variable. So if you run your program like this:
|
||||
```
|
||||
LLVM_PROFILE_FILE=build/program.profraw ./program.kexe
|
||||
```
|
||||
Then the coverage information will be stored to the `build` dir as `program.profraw`.
|
||||
|
||||
#### Parsing `*.profraw`
|
||||
|
||||
Generated file can be parsed with `llvm-profdata` utility. Basic usage:
|
||||
```
|
||||
llvm-profdata merge default.profraw -o program.profdata
|
||||
```
|
||||
See [command guide](http://llvm.org/docs/CommandGuide/llvm-profdata.html) for more options.
|
||||
|
||||
#### Creating reports
|
||||
|
||||
The last step is to create a report from the `program.profdata` file.
|
||||
It can be done with `llvm-cov` utility (refer to [command guide](http://llvm.org/docs/CommandGuide/llvm-cov.html) for detailed usage).
|
||||
For example, we can see a basic report using:
|
||||
```
|
||||
llvm-cov report program.kexe -instr-profile program.profdata
|
||||
```
|
||||
Or show a line-by-line coverage information in html:
|
||||
```
|
||||
llvm-cov show program.kexe -instr-profile program.profdata -format=html > report.html
|
||||
```
|
||||
|
||||
### Sample
|
||||
Usually coverage information is collected during running of the tests.
|
||||
Please refer to `samples/coverage` to see how it can be done.
|
||||
|
||||
|
||||
### Useful links
|
||||
* [LLVM Code Coverage Mapping Format](https://llvm.org/docs/CoverageMappingFormat.html)
|
||||
@@ -0,0 +1,3 @@
|
||||
## Concurrency in Kotlin/Native
|
||||
|
||||
The content of this page is moved to https://kotlinlang.org/docs/native-concurrency.html
|
||||
@@ -0,0 +1,3 @@
|
||||
## Debugging
|
||||
|
||||
The content of this page is moved to https://kotlinlang.org/docs/native-debugging.html
|
||||
@@ -0,0 +1,33 @@
|
||||
# Kotlin/Native #
|
||||
|
||||
_Kotlin/Native_ is a LLVM backend for the Kotlin compiler, runtime
|
||||
implementation and native code generation facility using LLVM toolchain.
|
||||
|
||||
_Kotlin/Native_ is primarily designed to allow compilation for platforms where
|
||||
virtual machines are not desirable or possible (such as iOS, embedded targets),
|
||||
or where developer is willing to produce reasonably-sized self-contained program
|
||||
without need to ship an additional execution runtime.
|
||||
|
||||
_Kotlin/Native_ could be used either as standalone compiler toolchain or as Gradle
|
||||
plugin. See [documentation](https://kotlinlang.org/docs/reference/native/gradle_plugin.html)
|
||||
for more details on how to use this plugin.
|
||||
|
||||
Compile your programs like that:
|
||||
|
||||
export PATH=kotlin-native-<platform>-<version>/bin:$PATH
|
||||
kotlinc hello.kt -o hello
|
||||
|
||||
For an optimized compilation use -opt:
|
||||
|
||||
kotlinc hello.kt -o hello -opt
|
||||
|
||||
To generate interoperability stubs create library definition file
|
||||
(take a look on [Tetris sample](samples/tetris))
|
||||
and run `cinterop` tool like this:
|
||||
|
||||
cinterop -def lib.def
|
||||
|
||||
See [C interop documentation](https://kotlinlang.org/docs/reference/native/c_interop.html)
|
||||
for more information on how to use C libraries from _Kotlin/Native_.
|
||||
|
||||
See [`RELEASE_NOTES.md`](https://github.com/JetBrains/kotlin-native/blob/master/RELEASE_NOTES.md) for information on supported platforms and current limitations.
|
||||
@@ -0,0 +1 @@
|
||||
The content of this page is moved to https://kotlinlang.org/docs/native-faq.html
|
||||
@@ -0,0 +1,729 @@
|
||||
# Kotlin/Native Gradle plugin
|
||||
|
||||
Since 1.3.40, a separate Gradle plugin for Kotlin/Native is deprecated in favor of the `kotlin-multiplatform` plugin.
|
||||
This plugin provides an IDE support along with support of the new multiplatform project model introduced in Kotlin 1.3.0.
|
||||
Below you can find a short list of differences between `kotlin-platform-native` and `kotlin-muliplatform` plugins.
|
||||
For more information see the `kotlin-muliplatform` [documentation page](https://kotlinlang.org/docs/mpp-discover-project.html).
|
||||
For `kotlin-platform-native` reference see the [corresponding section](#kotlin-platform-native-reference).
|
||||
|
||||
### Applying the multiplatform plugin
|
||||
|
||||
To apply the `kotlin-multiplatform` plugin, just add the following snippet into your build script:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="groovy">
|
||||
|
||||
```groovy
|
||||
plugins {
|
||||
id("org.jetbrains.kotlin.multiplatform") version '1.3.40'
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
### Managing targets
|
||||
|
||||
With the `kotlin-platform-native` plugin a set of target platforms is specified as a list in properties of the main component:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="groovy">
|
||||
|
||||
```groovy
|
||||
components.main {
|
||||
targets = ['macos_x64', 'linux_x64', 'mingw_x64']
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
With the `kotlin-multiplatform` plugin target platforms can be added into a project using special methods available in the `kotlin` extension.
|
||||
Each method adds into a project one __target__ which can be accessed using the `targets` property. Each target can be configured independently
|
||||
including output kinds, additional compiler options etc. See details about targets at the [corresponding page](https://kotlinlang.org/docs/reference/building-mpp-with-gradle.html#setting-up-targets).
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="groovy">
|
||||
|
||||
```groovy
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
|
||||
|
||||
kotlin {
|
||||
// These targets are declared without any target-specific settings.
|
||||
macosX64()
|
||||
linuxX64()
|
||||
|
||||
// You can specify a custom name used to access the target.
|
||||
mingwX64("windows")
|
||||
|
||||
iosArm64 {
|
||||
// Additional settings for ios_arm64.
|
||||
}
|
||||
|
||||
// You can access declared targets using the `targets` property.
|
||||
println(targets.macosX64)
|
||||
println(targets.windows)
|
||||
|
||||
// You also can configure all native targets in a single block.
|
||||
targets.withType(KotlinNativeTarget) {
|
||||
// Native target configuration.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Each target includes two __compilations__: `main` and `test` compiling product and test sources respectively. A compilation is an abstraction
|
||||
over a compiler invocation and described at the [corresponding page](https://kotlinlang.org/docs/reference/building-mpp-with-gradle.html#configuring-compilations).
|
||||
|
||||
### Managing sources
|
||||
|
||||
With the `kotlin-platform-native` plugin source sets are used to separate test and product sources. Also you can specify different sources
|
||||
for different platforms in the same source set:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="groovy">
|
||||
|
||||
```groovy
|
||||
sourceSets {
|
||||
// Adding target-independent sources.
|
||||
main.kotlin.srcDirs += 'src/main/mySources'
|
||||
|
||||
// Adding Linux-specific code.
|
||||
main.target('linux_x64').srcDirs += 'src/main/linux'
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
With the `kotlin-multiplatform` plugin __source__ __sets__ are also used to group sources but source files for different platforms are located in different source sets.
|
||||
For each declared target two source sets are created: `<target-name>Main` and `<target-name>Test` containing product and test sources for this platform. Common for all
|
||||
platforms sources are located in `commonMain` and `commonTest` source sets created by default. More information about source sets can be found
|
||||
[here](https://kotlinlang.org/docs/reference/building-mpp-with-gradle.html#configuring-source-sets).
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="groovy">
|
||||
|
||||
```groovy
|
||||
kotlin {
|
||||
sourceSets {
|
||||
// Adding target-independent sources.
|
||||
commonMain.kotlin.srcDirs += file("src/main/mySources")
|
||||
|
||||
// Adding Linux-specific code.
|
||||
linuxX64Main.kotlin.srcDirs += file("src/main/linux")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
### Managing dependencies
|
||||
|
||||
With the `kotlin-platform-native` plugin dependencies are configured in a traditional for Gradle way by grouping them into configurations
|
||||
using the project `dependencies` block:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="groovy">
|
||||
|
||||
```groovy
|
||||
dependencies {
|
||||
implementation 'org.sample.test:mylibrary:1.0'
|
||||
testImplementation 'org.sample.test:testlibrary:1.0'
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
The `kotlin-multiplatform` plugin also uses configurations under the hood but it also provides a `dependencies` block for each source set
|
||||
allowing configuring dependencies of this sources set:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="groovy">
|
||||
|
||||
```groovy
|
||||
kotlin.sourceSets {
|
||||
commonMain {
|
||||
dependencies {
|
||||
implementation("org.sample.test:mylibrary:1.0")
|
||||
}
|
||||
}
|
||||
|
||||
commonTest {
|
||||
dependencies {
|
||||
implementation("org.sample.test:testlibrary:1.0")
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Note that a module referenced by a dependency declared for `commonMain` or `commonTest` source set must be published using the `kotlin-multiplatform` plugin.
|
||||
If you want to use libraries published by the `kotlin-platform-native` plugin, you need to declare a separate source set for common native sources.
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="groovy">
|
||||
|
||||
```groovy
|
||||
kotlin.sourceSets {
|
||||
// Create a common source set used by native targets only.
|
||||
nativeMain {
|
||||
dependsOn(commonMain)
|
||||
dependencies {
|
||||
// Depend on a library published by the kotlin-platform-naive plugin.
|
||||
implementation("org.sample.test:mylibrary:1.0")
|
||||
}
|
||||
}
|
||||
|
||||
// Configure all native platform sources sets to use it as a common one.
|
||||
linuxX64Main.dependsOn(nativeMain)
|
||||
macosX64Main.dependsOn(nativeMain)
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
See more info about dependencies at the [corresponding page](https://kotlinlang.org/docs/reference/building-mpp-with-gradle.html#adding-dependencies).
|
||||
|
||||
### Output kinds
|
||||
|
||||
With the `kotlin-platform-native` plugin output kinds are specified as a list in properties of a component:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="groovy">
|
||||
|
||||
```groovy
|
||||
components.main {
|
||||
// Compile the component into an executable and a Kotlin/Native library.
|
||||
outputKinds = [EXECUTABLE, KLIBRARY]
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
With the `kotlin-multiplatform` plugin a compilation always produces a `*.klib` file. A separate `binaries` block is used to configure what
|
||||
final native binaries should be produced by each target. Each binary can be configured independently including linker options, executable entry point etc.
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="groovy">
|
||||
|
||||
```groovy
|
||||
kotlin {
|
||||
macosX64 {
|
||||
binaries {
|
||||
executable {
|
||||
// Binary configuration: linker options, name, etc.
|
||||
}
|
||||
framework {
|
||||
// ...
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
See more about native binaries declaration at the [corresponding page](https://kotlinlang.org/docs/reference/building-mpp-with-gradle.html#building-final-native-binaries).
|
||||
|
||||
|
||||
### Publishing
|
||||
|
||||
Both `kotlin-platform-native` and `kotlin-multiplatform` plugins automatically set up artifact publication when the
|
||||
`maven-publish` plugin is applied. See details about publication at the [corresponding page](https://kotlinlang.org/docs/reference/building-mpp-with-gradle.html#publishing-a-multiplatform-library).
|
||||
Note that currently only Kotlin/Native libraries (`*.klib`) can be published for native targets.
|
||||
|
||||
### Cinterop support
|
||||
|
||||
With the `kotlin-platform-native` plugin interop with a native library can be declared in component dependencies:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="groovy">
|
||||
|
||||
```groovy
|
||||
components.main {
|
||||
dependencies {
|
||||
cinterop('mystdio') {
|
||||
// Cinterop configuration.
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
With the `kotlin-multiplatform` plugin interops are configured as a part of a compilation (see details [here](https://kotlinlang.org/docs/reference/building-mpp-with-gradle.html#cinterop-support)).
|
||||
The rest of an interop configuration is the same as for the `kotlin-platform-native` plugin.
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="groovy">
|
||||
|
||||
```groovy
|
||||
kotlin {
|
||||
macosX64 {
|
||||
compilations.main.cinterops {
|
||||
mystdio {
|
||||
// Cinterop configuration.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
## `kotlin-platform-native` reference
|
||||
|
||||
### Overview
|
||||
|
||||
You may use the Gradle plugin to build _Kotlin/Native_ projects. Builds of the plugin are
|
||||
[available](https://plugins.gradle.org/plugin/org.jetbrains.kotlin.platform.native) at the Gradle plugin portal, so you can apply it
|
||||
using Gradle plugin DSL:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="groovy">
|
||||
|
||||
```groovy
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.platform.native" version "1.3.0-rc-146"
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
You also can get the plugin from a Bintray repository. In addition to releases, this repo contains old and development
|
||||
versions of the plugin which are not available at the plugin portal. To get the plugin from the Bintray repo, include
|
||||
the following snippet in your build script:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="groovy">
|
||||
|
||||
```groovy
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven {
|
||||
url "https://dl.bintray.com/jetbrains/kotlin-native-dependencies"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath "org.jetbrains.kotlin:kotlin-native-gradle-plugin:1.3.0-rc-146"
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'org.jetbrains.kotlin.platform.native'
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
By default the plugin downloads the Kotlin/Native compiler during the first run. If you have already downloaded the compiler
|
||||
manually you can specify the path to its root directory using `org.jetbrains.kotlin.native.home` project property (e.g. in `gradle.properties`).
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="groovy">
|
||||
|
||||
```groovy
|
||||
org.jetbrains.kotlin.native.home=/home/user/kotlin-native-0.8
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
In this case the compiler will not be downloaded by the plugin.
|
||||
|
||||
### Source management
|
||||
|
||||
Source management in the `kotlin.platform.native` plugin is uniform with other Kotlin plugins and is based on source sets.
|
||||
A source set is a group of Kotlin/Native source which may contain both common and platform-specific code. The plugin
|
||||
provides a top-level script block `sourceSets` allowing you to configure source sets. Also it creates the default
|
||||
source sets `main` and `test` (for production and test code respectively).
|
||||
|
||||
By default the production sources are located in `src/main/kotlin` and the test sources - in `src/test/kotlin`.
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="groovy">
|
||||
|
||||
```groovy
|
||||
sourceSets {
|
||||
// Adding target-independent sources.
|
||||
main.kotlin.srcDirs += 'src/main/mySources'
|
||||
|
||||
// Adding Linux-specific code. It will be compiled in Linux binaries only.
|
||||
main.target('linux_x64').srcDirs += 'src/main/linux'
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
### Targets and output kinds
|
||||
|
||||
By default the plugin creates software components for the main and test source sets. You can access them via the
|
||||
`components` container provided by Gradle or via the `component` property of a corresponding source set:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="groovy">
|
||||
|
||||
```groovy
|
||||
// Main component.
|
||||
components.main
|
||||
sourceSets.main.component
|
||||
|
||||
// Test component.
|
||||
components.test
|
||||
sourceSets.test.component
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Components allow you to specify:
|
||||
|
||||
* Targets (e.g. Linux/x64 or iOS/arm64 etc)
|
||||
* Output kinds (e.g. executable, library, framework etc)
|
||||
* Dependencies (including interop ones)
|
||||
|
||||
Targets can be specified by setting a corresponding component property:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="groovy">
|
||||
|
||||
```groovy
|
||||
components.main {
|
||||
// Compile this component for 64-bit MacOS, Linux and Windows.
|
||||
targets = ['macos_x64', 'linux_x64', 'mingw_x64']
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
The plugin uses the same notation as the compiler. By default, test component uses the same targets as specified for the main one.
|
||||
|
||||
Output kinds can also be specified using a special property:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="groovy">
|
||||
|
||||
```groovy
|
||||
components.main {
|
||||
// Compile the component into an executable and a Kotlin/Native library.
|
||||
outputKinds = [EXECUTABLE, KLIBRARY]
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
All constants used here are available inside a component configuration script block.
|
||||
The plugin supports producing binaries of the following kinds:
|
||||
|
||||
* `EXECUTABLE` - an executable file;
|
||||
* `KLIBRARY` - a Kotlin/Native library (*.klib);
|
||||
* `FRAMEWORK` - an Objective-C framework;
|
||||
* `DYNAMIC` - shared native library;
|
||||
* `STATIC` - static native library.
|
||||
|
||||
Also each native binary is built in two variants (build types): `debug` (debuggable, not optimized) and `release` (not debuggable, optimized).
|
||||
Note that Kotlin/Native libraries have only `debug` variant because optimizations are preformed only during compilation
|
||||
of a final binary (executable, static lib etc) and affect all libraries used to build it.
|
||||
|
||||
### Compile tasks
|
||||
|
||||
The plugin creates a compilation task for each combination of the target, output kind, and build type. The tasks have the following naming convention:
|
||||
|
||||
compile<ComponentName><BuildType><OutputKind><Target>KotlinNative
|
||||
|
||||
For example `compileDebugKlibraryMacos_x64KotlinNative`, `compileTestDebugKotlinNative`.
|
||||
|
||||
The name contains the following parts (some of them may be empty):
|
||||
|
||||
* `<ComponentName>` - name of a component. Empty for the main component.
|
||||
* `<BuildType>` - `Debug` or `Release`.
|
||||
* `<OutputKind>` - output kind name, e.g. `Executabe` or `Dynamic`. Empty if the component has only one output kind.
|
||||
* `<Target>` - target the component is built for, e.g. `Macos_x64` or `Wasm32`. Empty if the component is built only for one target.
|
||||
|
||||
Also the plugin creates a number of aggregate tasks allowing you to build all the binaries for a build type (e.g.
|
||||
`assembleAllDebug`) or all the binaries for a particular target (e.g. `assembleAllWasm32`).
|
||||
|
||||
Basic lifecycle tasks like `assemble`, `build`, and `clean` are also available.
|
||||
|
||||
### Running tests
|
||||
|
||||
The plugin builds a test executable for all the targets specified for the `test` component. If the current host platform is
|
||||
included in this list the test running tasks are also created. To run tests, execute the standard lifecycle `check` task:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="shell">
|
||||
|
||||
```bash
|
||||
./gradlew check
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
### Dependencies
|
||||
|
||||
The plugin allows you to declare dependencies on files and other projects using traditional Gradle's mechanism of
|
||||
configurations. The plugin supports Kotlin multiplatform projects allowing you to declare the `expectedBy` dependencies
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="groovy">
|
||||
|
||||
```groovy
|
||||
dependencies {
|
||||
implementation files('path/to/file/dependencies')
|
||||
implementation project('library')
|
||||
testImplementation project('testLibrary')
|
||||
expectedBy project('common')
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
It's possible to depend on a Kotlin/Native library published earlier in a maven repo. The plugin relies on Gradle's
|
||||
[metadata](https://github.com/gradle/gradle/blob/master/subprojects/docs/src/docs/design/gradle-module-metadata-latest-specification.md)
|
||||
support so the corresponding feature must be enabled. Add the following line in your `settings.gradle`:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="groovy">
|
||||
|
||||
```groovy
|
||||
enableFeaturePreview('GRADLE_METADATA')
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
Now you can declare a dependency on a Kotlin/Native library in the traditional `group:artifact:version` notation:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="groovy">
|
||||
|
||||
```groovy
|
||||
dependencies {
|
||||
implementation 'org.sample.test:mylibrary:1.0'
|
||||
testImplementation 'org.sample.test:testlibrary:1.0'
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Dependency declaration is also possible in the component block:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="groovy">
|
||||
|
||||
```groovy
|
||||
components.main {
|
||||
dependencies {
|
||||
implementation 'org.sample.test:mylibrary:1.0'
|
||||
}
|
||||
}
|
||||
|
||||
components.test {
|
||||
dependencies {
|
||||
implementation 'org.sample.test:testlibrary:1.0'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
### Using cinterop
|
||||
|
||||
It's possible to declare a cinterop dependency for a component:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="groovy">
|
||||
|
||||
```groovy
|
||||
components.main {
|
||||
dependencies {
|
||||
cinterop('mystdio') {
|
||||
// src/main/c_interop/mystdio.def is used as a def file.
|
||||
|
||||
// Set up compiler options
|
||||
compilerOpts '-I/my/include/path'
|
||||
|
||||
// It's possible to set up different options for different targets
|
||||
target('linux') {
|
||||
compilerOpts '-I/linux/include/path'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Here an interop library will be built and added in the component dependencies.
|
||||
|
||||
Often it's necessary to specify target-specific linker options for a Kotlin/Native binary using an interop. It can be
|
||||
done using the `target` script block:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="groovy">
|
||||
|
||||
```groovy
|
||||
components.main {
|
||||
target('linux') {
|
||||
linkerOpts '-L/path/to/linux/libs'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Also the `allTargets` block is available.
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="groovy">
|
||||
|
||||
```groovy
|
||||
components.main {
|
||||
// Configure all targets.
|
||||
allTargets {
|
||||
linkerOpts '-L/path/to/libs'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
### Publishing
|
||||
|
||||
In the presence of `maven-publish` plugin the publications for all the binaries built are created. The plugin uses Gradle
|
||||
metadata to publish the artifacts so this feature must be enabled (see the [dependencies](#dependencies) section).
|
||||
|
||||
Now you can publish the artifacts with the standard Gradle `publish` task:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="shell">
|
||||
|
||||
```bash
|
||||
./gradlew publish
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Only `EXECUTABLE` and `KLIBRARY` binaries are published currently.
|
||||
|
||||
The plugin allows you to customize the pom generated for the publication with the `pom` code block available for every component:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="groovy">
|
||||
|
||||
```groovy
|
||||
components.main {
|
||||
pom {
|
||||
withXml {
|
||||
def root = asNode()
|
||||
root.appendNode('name', 'My library')
|
||||
root.appendNode('description', 'A Kotlin/Native library')
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
### Serialization plugin
|
||||
|
||||
The plugin is shipped with a customized version of the `kotlinx.serialization` plugin. To use it you don't have to
|
||||
add new buildscript dependencies, just apply the plugins and add a dependency on the serialization library:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="groovy">
|
||||
|
||||
```groovy
|
||||
apply plugin: 'org.jetbrains.kotlin.platform.native'
|
||||
apply plugin: 'kotlinx-serialization-native'
|
||||
|
||||
dependencies {
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-runtime-native'
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
The [example project](https://github.com/ilmat192/kotlin-native-serialization-sample) for details.
|
||||
|
||||
### DSL example
|
||||
|
||||
In this section a commented DSL is shown.
|
||||
See also the example projects that use this plugin, e.g.
|
||||
[Kotlinx.coroutines](https://github.com/Kotlin/kotlinx.coroutines),
|
||||
[MPP http client](https://github.com/e5l/http-client-common/tree/master/samples/ios-test-application)
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="groovy">
|
||||
|
||||
```groovy
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.platform.native" version "1.3.0-rc-146"
|
||||
}
|
||||
|
||||
sourceSets.main {
|
||||
// Plugin uses Gradle's source directory sets here,
|
||||
// so all the DSL methods available in SourceDirectorySet can be called here.
|
||||
// Platform independent sources.
|
||||
kotlin.srcDirs += 'src/main/customDir'
|
||||
|
||||
// Linux-specific sources
|
||||
target('linux').srcDirs += 'src/main/linux'
|
||||
}
|
||||
|
||||
components.main {
|
||||
|
||||
// Set up targets
|
||||
targets = ['linux_x64', 'macos_x64', 'mingw_x64']
|
||||
|
||||
// Set up output kinds
|
||||
outputKinds = [EXECUTABLE, KLIBRARY, FRAMEWORK, DYNAMIC, STATIC]
|
||||
|
||||
// Specify custom entry point for executables
|
||||
entryPoint = "org.test.myMain"
|
||||
|
||||
// Target-specific options
|
||||
target('linux_x64') {
|
||||
linkerOpts '-L/linux/lib/path'
|
||||
}
|
||||
|
||||
// Targets independent options
|
||||
allTargets {
|
||||
linkerOpts '-L/common/lib/path'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
// Dependency on a published Kotlin/Native library.
|
||||
implementation 'org.test:mylib:1.0'
|
||||
|
||||
// Dependency on a project
|
||||
implementation project('library')
|
||||
|
||||
// Cinterop dependency
|
||||
cinterop('interop-name') {
|
||||
// Def-file describing the native API.
|
||||
// The default path is src/main/c_interop/<interop-name>.def
|
||||
defFile project.file("deffile.def")
|
||||
|
||||
// Package to place the Kotlin API generated.
|
||||
packageName 'org.sample'
|
||||
|
||||
// Options to be passed to compiler and linker by cinterop tool.
|
||||
compilerOpts 'Options for native stubs compilation'
|
||||
linkerOpts 'Options for native stubs'
|
||||
|
||||
// Additional headers to parse.
|
||||
headers project.files('header1.h', 'header2.h')
|
||||
|
||||
// Directories to look for headers.
|
||||
includeDirs {
|
||||
// All objects accepted by the Project.file method may be used with both options.
|
||||
|
||||
// Directories for header search (an analogue of the -I<path> compiler option).
|
||||
allHeaders 'path1', 'path2'
|
||||
|
||||
// Additional directories to search headers listed in the 'headerFilter' def-file option.
|
||||
// -headerFilterAdditionalSearchPrefix command line option analogue.
|
||||
headerFilterOnly 'path1', 'path2'
|
||||
}
|
||||
// A shortcut for includeDirs.allHeaders.
|
||||
includeDirs "include/directory" "another/directory"
|
||||
|
||||
// Pass additional command line options to the cinterop tool.
|
||||
extraOpts '-verbose'
|
||||
|
||||
// Additional configuration for Linux.
|
||||
target('linux') {
|
||||
compilerOpts 'Linux-specific options'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Additional pom settings for publication.
|
||||
pom {
|
||||
withXml {
|
||||
def root = asNode()
|
||||
root.appendNode('name', 'My library')
|
||||
root.appendNode('description', 'A Kotlin/Native library')
|
||||
}
|
||||
}
|
||||
|
||||
// Additional options passed to the compiler.
|
||||
extraOpts '--time'
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,342 @@
|
||||
## Profiling the compiler
|
||||
|
||||
### Profiling with Async profiler
|
||||
|
||||
IDEA Ultimate contains an Async sampling profiler.
|
||||
As of IDEA 2018.3 Async sampling profiler is still an experimental feature, so use Ctrl-Alt-Shift-/ on Linux,
|
||||
Cmd-Alt-Shift-/ on macOS to activate it. Then start compilation in CLI with `--no-daemon` and
|
||||
`-Porg.gradle.workers.max=1` flags (running Gradle task with the profiler doesn't seem to work properly) and attach
|
||||
to the running process using "Run/Attach Profiler to Local Process" menu item.
|
||||
|
||||
Select "K2NativeKt" or "org.jetbrains.kotlin.cli.utilities.MainKt" process.
|
||||
On completion profiler will produce flame diagram which could be navigated with the mouse
|
||||
(click-drag moves, wheel scales). More RAM in IDE (>4G) could be helpful when analyzing longer runs.
|
||||
As Async is a sampling profiler, to get sensible coverage longer runs are important.
|
||||
|
||||
### Profiling with YourKit
|
||||
|
||||
Unlike Async profiler in IDEA, YourKit can work as an exact profiler and provide complete coverage
|
||||
of all methods along with exact invocation counters.
|
||||
|
||||
Install the YourKit profiler for your platform from https://www.yourkit.com/java/profiler.
|
||||
Set AGENT variable to the JVMTI agent provided by YourKit, like
|
||||
|
||||
export AGENT=/Applications/YourKit-Java-Profiler-2018.04.app/Contents/Resources/bin/mac/libyjpagent.jnilib
|
||||
|
||||
To profile standard library compilation:
|
||||
|
||||
./gradlew -PstdLibJvmArgs="-agentpath:$AGENT=probe_disable=*,listen=all,tracing" dist
|
||||
|
||||
To profile platform libraries start build of proper target like this:
|
||||
|
||||
./gradlew -PplatformLibsJvmArgs="-agentpath:$AGENT=probe_disable=*,listen=all,tracing" ios_arm64PlatformLibs
|
||||
|
||||
To profile standalone code compilation use:
|
||||
|
||||
JAVA_OPTS="-agentpath:$AGENT=probe_disable=*,listen=all,tracing" ./dist/bin/konanc file.kt
|
||||
|
||||
Then attach to the desired application in YourKit GUI and use CPU tab to inspect CPU consuming methods.
|
||||
Saving the trace may be needed for more analysis. Adjusting `-Xmx` in `$HOME/.yjp/ui.ini` could help
|
||||
with the big traces.
|
||||
|
||||
To perform memory profiling follow the steps above, and after attachment to the running process
|
||||
use "Start Object Allocation Recording" button. See https://www.yourkit.com/docs/java/help/allocations.jsp for more details.
|
||||
|
||||
## Compiler Gradle options
|
||||
|
||||
There are several gradle flags one can use for Konan build.
|
||||
|
||||
* **-Pbuild_flags** passes flags to the compiler used to build stdlib
|
||||
|
||||
./gradlew -Pbuild_flags="--disable lower_inline --print_ir" stdlib
|
||||
|
||||
* **-Pshims** compiles LLVM interface with tracing "shims". Allowing one
|
||||
to trace the LLVM calls from the compiler.
|
||||
Make sure to rebuild the project.
|
||||
|
||||
./gradlew -Pshims=true dist
|
||||
|
||||
## Compiler environment variables
|
||||
|
||||
* **KONAN_DATA_DIR** changes `.konan` local data directory location (`$HOME/.konan` by default). Works both with cli compiler and gradle plugin
|
||||
|
||||
## Testing
|
||||
|
||||
### Compiler integration tests
|
||||
|
||||
To run blackbox compiler tests from JVM Kotlin use (takes time):
|
||||
|
||||
./gradlew run_external
|
||||
|
||||
To update the blackbox compiler tests set TeamCity build number in `gradle.properties`:
|
||||
|
||||
testKotlinVersion=<build number>
|
||||
|
||||
* **-Pfilter** allows one to choose test files to run.
|
||||
|
||||
./gradlew -Pfilter=overflowLong.kt run_external
|
||||
|
||||
* **-Pprefix** allows one to choose external test directories to run. Only tests from directories with given prefix will be executed.
|
||||
|
||||
./gradlew -Pprefix=build_external_compiler_codegen_box_cast run_external
|
||||
|
||||
* **-Ptest_flags** passes flags to the compiler used to compile tests
|
||||
|
||||
./gradlew -Ptest_flags="--time" backend.native:tests:array0
|
||||
|
||||
* **-Ptest_target** specifies cross target for a test run.
|
||||
|
||||
./gradlew -Ptest_target=raspberrypi backend.native:tests:array0
|
||||
|
||||
* **-Premote=user@host** sets remote test execution login/hostname. Good for cross compiled tests.
|
||||
|
||||
./gradlew -Premote=kotlin@111.22.33.444 backend.native:tests:run
|
||||
|
||||
* **-Ptest_verbose** enables printing compiler args and other helpful information during a test execution.
|
||||
|
||||
./gradlew -Ptest_verbose :backend.native:tests:mpp_optional_expectation
|
||||
|
||||
* **-Ptest_two_stage** enables two-stage compilation of tests. If two-stage compilation is enabled, test sources are compiled into a klibrary
|
||||
and then a final native binary is produced from this klibrary using the -Xinclude compiler flag.
|
||||
|
||||
./gradlew -Ptest_two_stage backend.native:tests:array0
|
||||
|
||||
* **-Ptest_with_cache_kind=static|dynamic** enables using caches during testing.
|
||||
|
||||
### Runtime unit tests
|
||||
|
||||
To run runtime unit tests on the host machine for both mimalloc and the standard allocator:
|
||||
|
||||
./gradlew hostRuntimeTests
|
||||
|
||||
To run tests for only one of these two allocators, run `hostStdAllocRuntimeTests` or `hostMimallocRuntimeTests`.
|
||||
|
||||
We use [Google Test](https://github.com/google/googletest) to execute the runtime unit tests. The build automatically fetches
|
||||
the specified Google Test revision to `runtime/googletest`. It is possible to manually modify the downloaded GTest sources for debug
|
||||
purposes; the build will not overwrite them by default.
|
||||
|
||||
To forcibly redownload Google Test when running tests, use the corresponding project property:
|
||||
|
||||
./gradlew hostRuntimeTests -Prefresh-gtest
|
||||
|
||||
or run the `downloadGTest` task directly with the `--refresh` CLI key:
|
||||
|
||||
./gradlew downloadGTest --refresh
|
||||
|
||||
To use a local GTest copy instead of the downloaded one, add the following line to `runtime/build.gradle.kts`:
|
||||
|
||||
googletest.useLocalSources("<path to local GTest sources>")
|
||||
|
||||
## Performance measurement
|
||||
|
||||
Firstly, it's necessary to build analyzer tool to have opportunity to compare different performance results:
|
||||
|
||||
cd tools/benchmarksAnalyzer
|
||||
../../gradlew build
|
||||
|
||||
To measure performance of Kotlin/Native compiler on existing benchmarks:
|
||||
|
||||
cd performance
|
||||
../gradlew :konanRun
|
||||
|
||||
**NOTE**: **konanRun** task needs built compiler and libs. To test against working tree make sure to run
|
||||
|
||||
./gradlew dist distPlatformLibs
|
||||
|
||||
before **konanRun**
|
||||
|
||||
**konanRun** task can be run separately for one/several benchmark applications:
|
||||
|
||||
cd performance
|
||||
../gradlew :cinterop:konanRun
|
||||
|
||||
**konanRun** task has parameter `filter` which allows to run only some subset of benchmarks:
|
||||
|
||||
cd performance
|
||||
../gradlew :cinterop:konanRun --filter=struct,macros
|
||||
|
||||
Or you can use `filterRegex` if you want to specify the filter as regexes:
|
||||
|
||||
cd performance
|
||||
../gradlew :ring:konanRun --filterRegex=String.*,Loop.*
|
||||
|
||||
There us also verbose mode to follow progress of running benchmarks
|
||||
|
||||
cd performance
|
||||
../gradlew :cinterop:konanRun --verbose
|
||||
|
||||
> Task :performance:cinterop:konanRun
|
||||
[DEBUG] Warm up iterations for benchmark macros
|
||||
[DEBUG] Running benchmark macros
|
||||
...
|
||||
|
||||
There are also tasks for running benchmarks on JVM (pay attention, some benchmarks e.g. cinterop benchmarks can't be run on JVM)
|
||||
|
||||
cd performance
|
||||
../gradlew :jvmRun
|
||||
|
||||
Files with results of benchmarks run are saved in `performance/build/nativeReport.json` for konanRun and `jvmReport.json` for jvmRun.
|
||||
You can change the output filename by setting the `nativeJson` property for konanRun and `jvmJson` for jvmRun:
|
||||
|
||||
cd performance
|
||||
../gradlew :ring:konanRun --filter=String.*,Loop.* -PnativeJson=stringsAndLoops.json
|
||||
|
||||
You can use the `compilerArgs` property to pass flags to the compiler used to compile the benchmarks:
|
||||
|
||||
cd performance
|
||||
../gradlew :konanRun -PcompilerArgs="--time -g"
|
||||
|
||||
To compare different results run benchmarksAnalyzer tool:
|
||||
|
||||
cd tools/benchmarksAnalyzer/build/bin/<target>/benchmarksAnalyzerReleaseExecutable/
|
||||
./benchmarksAnalyzer.kexe <file1> <file2>
|
||||
|
||||
Tool has several renders which allow produce output report in different forms (text, html, etc.). To set up render use flag `--render/-r`.
|
||||
Output can be redirected to file with flag `--output/-o`.
|
||||
To get detailed information about supported options, please use `--help/-h`.
|
||||
|
||||
Analyzer tool can compare both local files and files placed on Artifactory/TeamCity.
|
||||
|
||||
File description stored on Artifactory
|
||||
|
||||
artifactory:<build number>:<target (Linux|Windows10|MacOSX)>:<filename>
|
||||
|
||||
Example
|
||||
|
||||
artifactory:1.2-dev-7942:Windows10:nativeReport.json
|
||||
|
||||
File description stored on TeamCity
|
||||
|
||||
teamcity:<build locator>:<filename>
|
||||
|
||||
Example
|
||||
|
||||
teamcity:id:42491947:nativeReport.json
|
||||
|
||||
Pay attention, user and password information(with flag `-u <username>:<password>`) should be provided to get data from TeamCity.
|
||||
|
||||
## Composite build and testing
|
||||
|
||||
If you have a fix spanning both Kotlin and Kotlin/native workspaces you need to be able to test Kotlin/Native composite build. Here's how to do it manually:
|
||||
|
||||
### Have a composite build with the proper Kotlin tag.
|
||||
|
||||
Find the version of Kotlin the current native is guaranteed to build with.
|
||||
The version is specified in `kotlin-native/gradle.properties`. For example:
|
||||
```
|
||||
kotlinVersion=1.3.70-dev-1526
|
||||
```
|
||||
Checkout `kotlin` workspace to tag `build-1.3.70-dev-1526`. Make sure its path ends with `.../kotlin`.
|
||||
Otherwise issues will arise.
|
||||
Direct `kotlin-native` build to the kotlin with `kotlinProjectPath` in native's `gradle.properties`.
|
||||
|
||||
Now you have the kotlin + kotlin-native combination that is known to build.
|
||||
Apply your fix on top of both workspaces and run
|
||||
```
|
||||
$ ./gradlew dist
|
||||
```
|
||||
|
||||
in `kotlin-native` to check the buildability.
|
||||
|
||||
### Testing native
|
||||
|
||||
For a quick check use:
|
||||
```
|
||||
$ ./gradlew sanity 2>&1 | tee log
|
||||
```
|
||||
|
||||
For a longer, more thorough testing build the complete build. Make sure you are running it on a macOS.
|
||||
|
||||
|
||||
Have a complete build:
|
||||
|
||||
```
|
||||
$ ./gradlew bundle # includes dist as its part
|
||||
```
|
||||
|
||||
then run two test sets:
|
||||
|
||||
```
|
||||
$ ./gradlew backend.native:tests:run 2>&1 | tee log
|
||||
|
||||
$ ./gradlew backend.native:tests:runExternal -Ptest_two_stage=true 2>&1 | tee log
|
||||
|
||||
```
|
||||
|
||||
## LLVM
|
||||
|
||||
See [BUILDING_LLVM.md](BUILDING_LLVM.md) if you want to build and use your own LLVM distribution
|
||||
instead of provided one.
|
||||
|
||||
### Using different LLVM distributions as part of Kotlin/Native compilation pipeline.
|
||||
|
||||
`llvmHome.<HOST_NAME>` variable in `<distribution_location>/konan/konan.properties` controls
|
||||
which LLVM distribution Kotlin/Native will use in its compilation pipeline.
|
||||
You can replace its value with either `$llvm.<HOST_NAME>.{dev, user}` to use one of predefined distributions
|
||||
or pass an absolute to your own distribution.
|
||||
Don't forget to set `llvmVersion.<HOST_NAME>` to the version of your LLVM distribution.
|
||||
|
||||
#### Example. Using LLVM from an absolute path.
|
||||
Assuming LLVM distribution is installed at `/usr` path, one can specify a path to it
|
||||
with the `-Xoverride-konan-properties` option:
|
||||
```
|
||||
konanc main.kt -Xoverride-konan-properties=llvmHome.linux_x64=/usr
|
||||
```
|
||||
|
||||
### Playing with compilation pipeline.
|
||||
|
||||
Following compiler phases control different parts of LLVM pipeline:
|
||||
1. `LinkBitcodeDependencies`. Linkage of produced bitcode with runtime and some other dependencies.
|
||||
2. `BitcodeOptimization`. Running LLVM optimization pipeline.
|
||||
3. `ObjectFiles`. Compilation of bitcode with Clang.
|
||||
|
||||
For example, pass `-Xdisable-phases=BitcodeOptimization` to skip optimization pipeline.
|
||||
Note that disabling `LinkBitcodeDependencies` or `ObjectFiles` will break compilation pipeline.
|
||||
|
||||
Compiler takes options for Clang from [konan.properties](konan/konan.properties) file
|
||||
by combining `clangFlags.<TARGET>` and `clang<Noopt/Opt/Debug>Flags.<TARGET>` properties.
|
||||
Use `-Xoverride-konan-properties=<key_1=value_1; ...;key_n=value_n>` flag to override default values.
|
||||
|
||||
Please note:
|
||||
1. Kotlin Native passes bitcode files to Clang instead of C or C++, so many flags won't work.
|
||||
2. `-cc1 -emit-obj` should be passed because Kotlin/Native calls linker by itself.
|
||||
3. Use `clang -cc1 -help` to see a list of available options.
|
||||
|
||||
Another useful compiler option is `-Xtemporary-files-dir=<PATH>` which allows
|
||||
to specify a directory for intermediate compiler artifacts like bitcode and object files.
|
||||
|
||||
#### Example 1. Bitcode right after IR to Bitcode translation.
|
||||
```shell script
|
||||
konanc main.kt -produce bitcode -o bitcode.bc
|
||||
```
|
||||
|
||||
#### Example 2. Bitcode after LLVM optimizations.
|
||||
```shell script
|
||||
konanc main.kt -Xtemporary-files-dir=<PATH> -o <OUTPUT_NAME>
|
||||
```
|
||||
`<PATH>/<OUTPUT_NAME>.kt.bc` will contain bitcode after LLVM optimization pipeline.
|
||||
|
||||
#### Example 3. Replace predefined LLVM pipeline with Clang options.
|
||||
```shell script
|
||||
CLANG_FLAGS="clangFlags.macos_x64=-cc1 -emit-obj;clangNooptFlags.macos_x64=-O2"
|
||||
konanc main.kt -Xdisable-phases=BitcodeOptimization -Xoverride-konan-properties="$CLANG_FLAGS"
|
||||
```
|
||||
|
||||
## Running Clang the same way Kotlin/Native compiler does
|
||||
|
||||
Kotlin/Native compiler (including `cinterop` tool) has machinery that manages LLVM, Clang and native SDKs for supported targets
|
||||
and runs bundled Clang with proper arguments.
|
||||
To utilize this machinery, use `$dist/bin/run_konan clang $tool $target $arguments`, e.g.
|
||||
```
|
||||
$dist/bin/run_konan clang clang ios_arm64 1.c
|
||||
```
|
||||
will print and run the following command:
|
||||
```
|
||||
~/.konan/dependencies/clang-llvm-apple-8.0.0-darwin-macos/bin/clang \
|
||||
-B/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin \
|
||||
-fno-stack-protector -stdlib=libc++ -arch arm64 \
|
||||
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.5.sdk \
|
||||
-miphoneos-version-min=9.0 1.c
|
||||
```
|
||||
|
||||
The similar helper is available for LLVM tools, `$dist/bin/run_konan llvm $tool $arguments`.
|
||||
@@ -0,0 +1,3 @@
|
||||
# Immutability in Kotlin/Native
|
||||
|
||||
The content of this page is moved to https://kotlinlang.org/docs/native-immutability.html
|
||||
@@ -0,0 +1,3 @@
|
||||
# _Kotlin/Native_ interoperability #
|
||||
|
||||
The content of this page is moved to https://kotlinlang.org/docs/native-c-interop.html
|
||||
@@ -0,0 +1,3 @@
|
||||
# Symbolicating iOS crash reports
|
||||
|
||||
The content of this page is moved to https://kotlinlang.org/docs/native-ios-symbolication.html
|
||||
Generated
+22
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<resourceExtensions />
|
||||
<wildcardResourcePatterns>
|
||||
<entry name="!?*.java" />
|
||||
<entry name="!?*.form" />
|
||||
<entry name="!?*.class" />
|
||||
<entry name="!?*.groovy" />
|
||||
<entry name="!?*.scala" />
|
||||
<entry name="!?*.flex" />
|
||||
<entry name="!?*.kt" />
|
||||
<entry name="!?*.clj" />
|
||||
<entry name="!?*.aj" />
|
||||
</wildcardResourcePatterns>
|
||||
<annotationProcessing>
|
||||
<profile default="true" name="Default" enabled="false">
|
||||
<processorPath useClasspath="true" />
|
||||
</profile>
|
||||
</annotationProcessing>
|
||||
</component>
|
||||
</project>
|
||||
Generated
+23
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="distributionType" value="WRAPPED" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="gradleJvm" value="1.8" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$/.." />
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/Indexer" />
|
||||
<option value="$PROJECT_DIR$/Runtime" />
|
||||
<option value="$PROJECT_DIR$/StubGenerator" />
|
||||
<option value="$PROJECT_DIR$/../InteropExample" />
|
||||
</set>
|
||||
</option>
|
||||
<option name="resolveModulePerSourceSet" value="false" />
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
Generated
+11
@@ -0,0 +1,11 @@
|
||||
<component name="libraryTable">
|
||||
<library name="Gradle: org.jetbrains.kotlin:kotlin-runtime:1.0.3">
|
||||
<CLASSES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-runtime/1.0.3/10f40d016700cf4287e49fa1d51c2a8507e9b946/kotlin-runtime-1.0.3.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-runtime/1.0.3/44d99f6d9a1d69f25797590cdd3efe2cbce8bcb6/kotlin-runtime-1.0.3-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
</component>
|
||||
Generated
+11
@@ -0,0 +1,11 @@
|
||||
<component name="libraryTable">
|
||||
<library name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.0.3">
|
||||
<CLASSES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.0.3/20738122b53399036c321eeb84687367757d622a/kotlin-stdlib-1.0.3.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES>
|
||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.0.3/efeac5a1b15300f742d2119667f90df44230b94a/kotlin-stdlib-1.0.3-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
</component>
|
||||
Generated
+10
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/Indexer/Indexer.iml" filepath="$PROJECT_DIR$/.idea/modules/Indexer/Indexer.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/Runtime/Runtime.iml" filepath="$PROJECT_DIR$/.idea/modules/Runtime/Runtime.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/StubGenerator/StubGenerator.iml" filepath="$PROJECT_DIR$/.idea/modules/StubGenerator/StubGenerator.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module external.linked.project.id=":Interop:Indexer" external.linked.project.path="$MODULE_DIR$/../../../Indexer" external.root.project.path="$MODULE_DIR$/../../.." external.system.id="GRADLE" external.system.module.group="experiments.Interop" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="false">
|
||||
<output url="file://$MODULE_DIR$/../../../Indexer/build/classes/main" />
|
||||
<output-test url="file://$MODULE_DIR$/../../../Indexer/build/classes/test" />
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$/../../../Indexer">
|
||||
<sourceFolder url="file://$MODULE_DIR$/../../../Indexer/prebuilt/nativeInteropStubs" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/../../../Indexer/src/main/java" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/../../../Indexer/src/main/kotlin" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/../../../Indexer/src/test/java" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/../../../Indexer/src/test/kotlin" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/../../../Indexer/src/main/resources" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/../../../Indexer/src/test/resources" type="java-test-resource" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/../../../Indexer/.gradle" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/../../../Indexer/build" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="module" module-name="Runtime" />
|
||||
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.0.3" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-runtime:1.0.3" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module external.linked.project.id=":Interop:Runtime" external.linked.project.path="$MODULE_DIR$/../../../Runtime" external.root.project.path="$MODULE_DIR$/../../.." external.system.id="GRADLE" external.system.module.group="experiments.Interop" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="false">
|
||||
<output url="file://$MODULE_DIR$/../../../Runtime/build/classes/main" />
|
||||
<output-test url="file://$MODULE_DIR$/../../../Runtime/build/classes/test" />
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$/../../../Runtime">
|
||||
<sourceFolder url="file://$MODULE_DIR$/../../../Runtime/src/main/java" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/../../../Runtime/src/main/kotlin" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/../../../Runtime/src/test/java" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/../../../Runtime/src/test/kotlin" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/../../../Runtime/src/main/resources" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/../../../Runtime/src/test/resources" type="java-test-resource" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/../../../Runtime/.gradle" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/../../../Runtime/build" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.0.3" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-runtime:1.0.3" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module external.linked.project.id=":Interop:StubGenerator" external.linked.project.path="$MODULE_DIR$/../../../StubGenerator" external.root.project.path="$MODULE_DIR$/../../.." external.system.id="GRADLE" external.system.module.group="experiments.Interop" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="false">
|
||||
<output url="file://$MODULE_DIR$/../../../StubGenerator/build/classes/main" />
|
||||
<output-test url="file://$MODULE_DIR$/../../../StubGenerator/build/classes/test" />
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$/../../../StubGenerator">
|
||||
<sourceFolder url="file://$MODULE_DIR$/../../../StubGenerator/src/main/java" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/../../../StubGenerator/src/main/kotlin" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/../../../StubGenerator/src/test/java" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/../../../StubGenerator/src/test/kotlin" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/../../../StubGenerator/src/main/resources" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/../../../StubGenerator/src/test/resources" type="java-test-resource" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/../../../StubGenerator/.gradle" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/../../../StubGenerator/build" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="module" module-name="Indexer" />
|
||||
<orderEntry type="module" module-name="Runtime" />
|
||||
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.0.3" level="project" />
|
||||
<orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-runtime:1.0.3" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import org.jetbrains.gradle.plugins.tools.lib
|
||||
import org.jetbrains.gradle.plugins.tools.solib
|
||||
import org.jetbrains.kotlin.*
|
||||
import org.jetbrains.kotlin.konan.target.*
|
||||
import org.jetbrains.kotlin.konan.target.ClangArgs
|
||||
import org.jetbrains.kotlin.konan.target.Family.*
|
||||
import org.jetbrains.kotlin.konan.target.HostManager.Companion.hostIsMac
|
||||
|
||||
plugins {
|
||||
`kotlin`
|
||||
`native-interop-plugin`
|
||||
`native`
|
||||
}
|
||||
|
||||
|
||||
val libclangextProject = project(":kotlin-native:libclangext")
|
||||
val libclangextTask = libclangextProject.path + ":build"
|
||||
val libclangextDir = libclangextProject.buildDir
|
||||
val libclangextIsEnabled = libclangextProject.findProperty("isEnabled")!! as Boolean
|
||||
val llvmDir = project.findProperty("llvmDir")
|
||||
|
||||
|
||||
val libclang =
|
||||
if (HostManager.hostIsMingw) {
|
||||
"bin/libclang.dll"
|
||||
} else {
|
||||
"lib/${System.mapLibraryName("clang")}"
|
||||
}
|
||||
|
||||
val cflags = mutableListOf( "-I$llvmDir/include",
|
||||
"-I${project(":kotlin-native:libclangext").projectDir.absolutePath}/src/main/include",
|
||||
*platformManager.hostPlatform.clang.hostCompilerArgsForJni)
|
||||
if (!HostManager.hostIsMingw) {
|
||||
cflags += "-fPIC"
|
||||
}
|
||||
|
||||
val ldflags = mutableListOf("$llvmDir/$libclang", "-L${libclangextDir.absolutePath}", "-lclangext")
|
||||
|
||||
if (libclangextIsEnabled) {
|
||||
assert(HostManager.hostIsMac)
|
||||
ldflags.addAll(listOf("-Wl,--no-demangle", "-Wl,-search_paths_first", "-Wl,-headerpad_max_install_names", "-Wl,-U,_futimens",
|
||||
"-Wl,-U,__ZN4llvm7remarks11parseFormatENS_9StringRefE",
|
||||
"-Wl,-U,__ZN4llvm7remarks22createRemarkSerializerENS0_6FormatENS0_14SerializerModeERNS_11raw_ostreamE",
|
||||
"-Wl,-U,__ZN4llvm7remarks14YAMLSerializerC1ERNS_11raw_ostreamENS0_14UseStringTableE"))
|
||||
|
||||
val llvmLibs = listOf(
|
||||
"clangAST", "clangASTMatchers", "clangAnalysis", "clangBasic", "clangDriver", "clangEdit",
|
||||
"clangFrontend", "clangFrontendTool", "clangLex", "clangParse", "clangSema", "clangEdit",
|
||||
"clangRewrite", "clangRewriteFrontend", "clangStaticAnalyzerFrontend",
|
||||
"clangStaticAnalyzerCheckers", "clangStaticAnalyzerCore", "clangSerialization",
|
||||
"clangToolingCore",
|
||||
"clangTooling", "clangFormat", "LLVMTarget", "LLVMMC", "LLVMLinker", "LLVMTransformUtils",
|
||||
"LLVMBitWriter", "LLVMBitReader", "LLVMAnalysis", "LLVMProfileData", "LLVMCore",
|
||||
"LLVMSupport", "LLVMBinaryFormat", "LLVMDemangle"
|
||||
).map { "$llvmDir/lib/lib${it}.a" }
|
||||
|
||||
ldflags.addAll(llvmLibs)
|
||||
ldflags.addAll(listOf("-lpthread", "-lz", "-lm", "-lcurses"))
|
||||
}
|
||||
|
||||
val solib = when{
|
||||
HostManager.hostIsMingw -> "dll"
|
||||
HostManager.hostIsMac -> "dylib"
|
||||
else -> "so"
|
||||
}
|
||||
val lib = if (HostManager.hostIsMingw) "lib" else "a"
|
||||
|
||||
|
||||
native {
|
||||
val obj = if (HostManager.hostIsMingw) "obj" else "o"
|
||||
val host = rootProject.project(":kotlin-native").extra["hostName"]
|
||||
val hostLibffiDir = rootProject.project(":kotlin-native").extra["${host}LibffiDir"]
|
||||
val cxxflags = listOf("-std=c++11", *cflags.toTypedArray())
|
||||
suffixes {
|
||||
(".c" to ".$obj") {
|
||||
tool(*platformManager.hostPlatform.clang.clangC("").toTypedArray())
|
||||
flags(*cflags.toTypedArray(),
|
||||
"-c", "-o", ruleOut(), ruleInFirst())
|
||||
}
|
||||
(".cpp" to ".$obj") {
|
||||
tool(*platformManager.hostPlatform.clang.clangCXX("").toTypedArray())
|
||||
flags(*cxxflags.toTypedArray(), "-c", "-o", ruleOut(), ruleInFirst())
|
||||
}
|
||||
|
||||
}
|
||||
sourceSet {
|
||||
"main-c" {
|
||||
dir("prebuilt/nativeInteropStubs/c")
|
||||
}
|
||||
"main-cpp" {
|
||||
dir("src/nativeInteropStubs/cpp")
|
||||
}
|
||||
}
|
||||
val objSet = arrayOf(sourceSets["main-c"]!!.transform(".c" to ".$obj"),
|
||||
sourceSets["main-cpp"]!!.transform(".cpp" to ".$obj"))
|
||||
|
||||
target(solib("clangstubs"), *objSet) {
|
||||
tool(*platformManager.hostPlatform.clang.clangCXX("").toTypedArray())
|
||||
flags(
|
||||
"-shared",
|
||||
"-o", ruleOut(), *ruleInAll(),
|
||||
*ldflags.toTypedArray())
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named(solib("clangstubs")).configure {
|
||||
dependsOn(":kotlin-native:libclangext:${lib("clangext")}")
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" {
|
||||
java {
|
||||
srcDirs("prebuilt/nativeInteropStubs/kotlin")
|
||||
}
|
||||
kotlin{
|
||||
|
||||
target {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
dependencies {
|
||||
compile(project(":kotlin-stdlib"))
|
||||
compile(project(":kotlin-native:Interop:Runtime"))
|
||||
}
|
||||
|
||||
val nativelibs = project.tasks.create<Copy>("nativelibs") {
|
||||
dependsOn(solib("clangstubs"))
|
||||
|
||||
from("$buildDir/")
|
||||
into("$buildDir/nativelibs/")
|
||||
}
|
||||
|
||||
kotlinNativeInterop {
|
||||
this.create("clang") {
|
||||
defFile("clang.def")
|
||||
compilerOpts(cflags)
|
||||
linkerOpts = ldflags
|
||||
genTask.dependsOn(libclangextTask)
|
||||
genTask.inputs.dir(libclangextDir)
|
||||
}
|
||||
}
|
||||
val compileKotlin: org.jetbrains.kotlin.gradle.tasks.KotlinCompile by tasks
|
||||
|
||||
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
|
||||
kotlinOptions {
|
||||
allWarningsAsErrors = true
|
||||
freeCompilerArgs = listOf("-Xskip-prerelease-check")
|
||||
}
|
||||
}
|
||||
|
||||
tasks.matching { it.name == "linkClangstubsSharedLibrary" }.all {
|
||||
dependsOn(libclangextTask)
|
||||
inputs.dir(libclangextDir)
|
||||
}
|
||||
|
||||
tasks.create("updatePrebuilt") {
|
||||
dependsOn("genClangInteropStubs")
|
||||
|
||||
doLast {
|
||||
copy {
|
||||
from("$buildDir/nativeInteropStubs/clang/kotlin") {
|
||||
include("clang/clang.kt")
|
||||
}
|
||||
into("prebuilt/nativeInteropStubs/kotlin")
|
||||
}
|
||||
|
||||
copy {
|
||||
from("$buildDir/interopTemp") {
|
||||
include("clangstubs.c")
|
||||
}
|
||||
into("prebuilt/nativeInteropStubs/c")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
headers = clang-c/Index.h clang-c/ext.h clang-c/ExtVector.h
|
||||
|
||||
headerFilter = clang-c/**
|
||||
|
||||
compiler = clang
|
||||
|
||||
compilerOpts = -std=c99 -fPIC
|
||||
linkerOpts.linux = -Wl,-z,noexecstack
|
||||
|
||||
linker = clang++
|
||||
|
||||
linkerOpts = -fPIC
|
||||
|
||||
strictEnums = CXErrorCode CXCursorKind CXTypeKind CXDiagnosticSeverity CXLoadDiag_Error CXSaveError \
|
||||
CXTUResourceUsageKind CXLinkageKind CXVisibilityKind CXLanguageKind CXCallingConv CXChildVisitResult \
|
||||
CXTokenKind CXEvalResultKind CXVisitorResult CXResult CXIdxEntityKind
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.native.interop.indexer
|
||||
|
||||
import java.io.File
|
||||
|
||||
class HeaderToIdMapper(sysRoot: String) {
|
||||
private val headerPathToId = mutableMapOf<String, HeaderId>()
|
||||
private val sysRoot = File(sysRoot).canonicalFile.toPath()
|
||||
|
||||
internal fun getHeaderId(filePath: String) = headerPathToId.getOrPut(filePath) {
|
||||
val path = File(filePath).canonicalFile.toPath()
|
||||
val headerIdValue = if (path.startsWith(sysRoot)) {
|
||||
val relative = sysRoot.relativize(path)
|
||||
relative.toString()
|
||||
} else {
|
||||
headerContentsHash(filePath)
|
||||
}
|
||||
HeaderId(headerIdValue)
|
||||
}
|
||||
}
|
||||
+1046
File diff suppressed because it is too large
Load Diff
+322
@@ -0,0 +1,322 @@
|
||||
/*
|
||||
* 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.native.interop.indexer
|
||||
|
||||
import clang.*
|
||||
import kotlinx.cinterop.*
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* Finds all "macro constants" and registers them as [NativeIndex.constants] in given index.
|
||||
*/
|
||||
internal fun findMacros(
|
||||
nativeIndex: NativeIndexImpl,
|
||||
compilation: CompilationWithPCH,
|
||||
translationUnit: CXTranslationUnit,
|
||||
headers: Set<CXFile?>
|
||||
) {
|
||||
val names = collectMacroNames(nativeIndex, translationUnit, headers)
|
||||
// TODO: apply user-defined filters.
|
||||
val macros = expandMacros(compilation, names, typeConverter = { nativeIndex.convertType(it) })
|
||||
|
||||
macros.filterIsInstanceTo(nativeIndex.macroConstants)
|
||||
macros.filterIsInstanceTo(nativeIndex.wrappedMacros)
|
||||
}
|
||||
|
||||
private typealias TypeConverter = (CValue<CXType>) -> Type
|
||||
|
||||
/**
|
||||
* For each name expands the macro with this name declared in the library,
|
||||
* checking if it gets expanded to a constant expression.
|
||||
*
|
||||
* Note: in the worst case this method parses the code against the library a lot of times,
|
||||
* so it requires library headers precompiled to significantly speed up the parsing and avoid visiting headers' AST.
|
||||
*
|
||||
* @return the list of constants.
|
||||
*/
|
||||
private fun expandMacros(
|
||||
library: CompilationWithPCH,
|
||||
names: List<String>,
|
||||
typeConverter: TypeConverter
|
||||
): List<MacroDef> {
|
||||
withIndex(excludeDeclarationsFromPCH = true) { index ->
|
||||
val sourceFile = library.createTempSource()
|
||||
val compilerArgs = library.compilerArgs.toMutableList()
|
||||
// We disable implicit function declaration to filter out cases when a macro is expanded as a function
|
||||
// or function-like construction (e.g. #define FOO throw()) but such a function is undeclared.
|
||||
compilerArgs += "-Werror=implicit-function-declaration"
|
||||
|
||||
// Ensure libclang reports all errors:
|
||||
compilerArgs += "-ferror-limit=0"
|
||||
|
||||
val translationUnit = parseTranslationUnit(index, sourceFile, compilerArgs, options = 0)
|
||||
try {
|
||||
val nameToMacroDef = mutableMapOf<String, MacroDef>()
|
||||
val unprocessedMacros = names.toMutableList()
|
||||
|
||||
// Note: will be slow for a library with a lot of macros having unbalanced '{'. TODO: Optimize this case too.
|
||||
|
||||
while (unprocessedMacros.isNotEmpty()) {
|
||||
val processedMacros =
|
||||
tryExpandMacros(library, translationUnit, sourceFile, unprocessedMacros, typeConverter)
|
||||
|
||||
unprocessedMacros -= (processedMacros.keys + unprocessedMacros.first())
|
||||
// Note: removing first macro should not have any effect, doing this to ensure the loop is finite.
|
||||
|
||||
processedMacros.forEach { (name, macroDef) ->
|
||||
if (macroDef != null) nameToMacroDef[name] = macroDef
|
||||
}
|
||||
}
|
||||
|
||||
return names.mapNotNull { nameToMacroDef[it] }
|
||||
} finally {
|
||||
clang_disposeTranslationUnit(translationUnit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to expand macros [names] defined in [library].
|
||||
* Returns the map of successfully processed macros with resulting constant as a value
|
||||
* or `null` if the result is not a constant (expression).
|
||||
*
|
||||
* As a side effect, modifies the [sourceFile] and reparses the [translationUnit].
|
||||
*/
|
||||
private fun tryExpandMacros(
|
||||
library: CompilationWithPCH,
|
||||
translationUnit: CXTranslationUnit,
|
||||
sourceFile: File,
|
||||
names: List<String>,
|
||||
typeConverter: TypeConverter
|
||||
): Map<String, MacroDef?> {
|
||||
|
||||
reparseWithCodeSnippets(library, translationUnit, sourceFile, names)
|
||||
|
||||
val macrosWithErrorsInSnippetFunctionHeader = mutableSetOf<String>()
|
||||
val macrosWithErrorsInSnippetFunctionBody = mutableSetOf<String>()
|
||||
|
||||
val preambleSize = library.preambleLines.size
|
||||
|
||||
translationUnit.getErrorLineNumbers().map { it - preambleSize - 1 }.forEach { lineNumber ->
|
||||
val index = lineNumber / CODE_SNIPPET_LINES_NUMBER
|
||||
if (index >= 0 && index < names.size) {
|
||||
when (lineNumber % CODE_SNIPPET_LINES_NUMBER) {
|
||||
0 -> macrosWithErrorsInSnippetFunctionHeader += names[index]
|
||||
1 -> macrosWithErrorsInSnippetFunctionBody += names[index]
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val result = mutableMapOf<String, MacroDef?>()
|
||||
|
||||
visitChildren(translationUnit) { cursor, _ ->
|
||||
if (cursor.kind == CXCursorKind.CXCursor_FunctionDecl) {
|
||||
val functionName = getCursorSpelling(cursor)
|
||||
if (functionName.startsWith(CODE_SNIPPET_FUNCTION_NAME_PREFIX)) {
|
||||
val macroName = functionName.removePrefix(CODE_SNIPPET_FUNCTION_NAME_PREFIX)
|
||||
if (macroName in macrosWithErrorsInSnippetFunctionHeader) {
|
||||
// Code snippet is likely affected by previous macros' snippets, skip it for now.
|
||||
} else {
|
||||
result[macroName] = if (macroName in macrosWithErrorsInSnippetFunctionBody) {
|
||||
// Code snippet is likely unaffected by previous ones but parsed with its own errors,
|
||||
// so suppose macro is processed successfully as non-expression:
|
||||
null
|
||||
} else {
|
||||
processCodeSnippet(cursor, macroName, typeConverter)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
CXChildVisitResult.CXChildVisit_Continue
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private const val CODE_SNIPPET_LINES_NUMBER = 3
|
||||
private const val CODE_SNIPPET_FUNCTION_NAME_PREFIX = "kni_indexer_function_"
|
||||
|
||||
/**
|
||||
* Adds code snippets to be then processed with [processCodeSnippet] to the [sourceFile]
|
||||
* and reparses the [translationUnit].
|
||||
*
|
||||
* - If a code snippet allows extracting the constant value using libclang API, we'll add a [ConstantDef] in the
|
||||
* native index and generate a Kotlin constant for it.
|
||||
* - If the expression type can be inferred by libclang, we'll add a [WrappedMacroDef] in the native index and
|
||||
* generate a bridge for this macro.
|
||||
* - Otherwise the macro is skipped.
|
||||
*/
|
||||
private fun reparseWithCodeSnippets(library: CompilationWithPCH,
|
||||
translationUnit: CXTranslationUnit, sourceFile: File,
|
||||
names: List<String>) {
|
||||
|
||||
// TODO: consider using CXUnsavedFile instead of writing the modified file to OS file system.
|
||||
sourceFile.bufferedWriter().use { writer ->
|
||||
writer.appendPreamble(library)
|
||||
|
||||
names.forEach { name ->
|
||||
val codeSnippetLines = when (library.language) {
|
||||
Language.C, Language.OBJECTIVE_C ->
|
||||
listOf("void $CODE_SNIPPET_FUNCTION_NAME_PREFIX$name() {",
|
||||
" __auto_type KNI_INDEXER_VARIABLE_$name = $name;",
|
||||
"}")
|
||||
}
|
||||
|
||||
assert(codeSnippetLines.size == CODE_SNIPPET_LINES_NUMBER)
|
||||
codeSnippetLines.forEach { writer.appendLine(it) }
|
||||
}
|
||||
}
|
||||
clang_reparseTranslationUnit(translationUnit, 0, null, 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that [functionCursor] is parsed exactly as expected for the code appended by [reparseWithCodeSnippets],
|
||||
* and returns the constant on success.
|
||||
*/
|
||||
private fun processCodeSnippet(
|
||||
functionCursor: CValue<CXCursor>,
|
||||
name: String,
|
||||
typeConverter: TypeConverter
|
||||
): MacroDef? {
|
||||
|
||||
val kindsToSkip = setOf(CXCursorKind.CXCursor_CompoundStmt)
|
||||
var state = VisitorState.EXPECT_NODES_TO_SKIP
|
||||
var evalResultOrNull: CXEvalResult? = null
|
||||
var typeOrNull: Type? = null
|
||||
|
||||
val visitor: CursorVisitor = { cursor, _ ->
|
||||
val kind = cursor.kind
|
||||
when {
|
||||
state == VisitorState.EXPECT_VARIABLE && kind == CXCursorKind.CXCursor_VarDecl -> {
|
||||
evalResultOrNull = clang_Cursor_Evaluate(cursor)
|
||||
state = VisitorState.EXPECT_VARIABLE_VALUE
|
||||
CXChildVisitResult.CXChildVisit_Recurse
|
||||
}
|
||||
|
||||
state == VisitorState.EXPECT_VARIABLE_VALUE && clang_isExpression(kind) != 0 -> {
|
||||
typeOrNull = typeConverter(clang_getCursorType(cursor))
|
||||
state = VisitorState.EXPECT_END
|
||||
CXChildVisitResult.CXChildVisit_Continue
|
||||
}
|
||||
|
||||
// Skip auxiliary elements.
|
||||
state == VisitorState.EXPECT_NODES_TO_SKIP && kind in kindsToSkip ->
|
||||
CXChildVisitResult.CXChildVisit_Recurse
|
||||
|
||||
state == VisitorState.EXPECT_NODES_TO_SKIP && kind == CXCursorKind.CXCursor_DeclStmt -> {
|
||||
state = VisitorState.EXPECT_VARIABLE
|
||||
CXChildVisitResult.CXChildVisit_Recurse
|
||||
}
|
||||
|
||||
else -> {
|
||||
state = VisitorState.INVALID
|
||||
CXChildVisitResult.CXChildVisit_Break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
visitChildren(functionCursor, visitor)
|
||||
|
||||
if (state != VisitorState.EXPECT_END) {
|
||||
return null
|
||||
}
|
||||
|
||||
val type = typeOrNull!!
|
||||
return if (evalResultOrNull == null) {
|
||||
// The macro cannot be evaluated as a constant so we will wrap it in a bridge.
|
||||
when(type.unwrapTypedefs()) {
|
||||
is PrimitiveType,
|
||||
is PointerType,
|
||||
is ObjCPointer -> WrappedMacroDef(name, type)
|
||||
else -> null
|
||||
}
|
||||
} else {
|
||||
// Otherwise we can evaluate the expression and create a Kotlin constant for it.
|
||||
val evalResult = evalResultOrNull!!
|
||||
val evalResultKind = clang_EvalResult_getKind(evalResult)
|
||||
when (evalResultKind) {
|
||||
CXEvalResultKind.CXEval_Int ->
|
||||
IntegerConstantDef(name, type, clang_EvalResult_getAsLongLong(evalResult))
|
||||
|
||||
CXEvalResultKind.CXEval_Float ->
|
||||
FloatingConstantDef(name, type, clang_EvalResult_getAsDouble(evalResult))
|
||||
|
||||
CXEvalResultKind.CXEval_CFStr,
|
||||
CXEvalResultKind.CXEval_ObjCStrLiteral,
|
||||
CXEvalResultKind.CXEval_StrLiteral ->
|
||||
if (evalResultKind == CXEvalResultKind.CXEval_StrLiteral && !type.canonicalIsPointerToChar()) {
|
||||
// libclang doesn't seem to support wide string literals properly in this API;
|
||||
// thus disable wide literals here:
|
||||
null
|
||||
} else {
|
||||
StringConstantDef(name, type, clang_EvalResult_getAsStr(evalResult)!!.toKString())
|
||||
}
|
||||
|
||||
CXEvalResultKind.CXEval_Other,
|
||||
CXEvalResultKind.CXEval_UnExposed -> null
|
||||
}
|
||||
}
|
||||
|
||||
} finally {
|
||||
evalResultOrNull?.let { clang_EvalResult_dispose(it) }
|
||||
}
|
||||
}
|
||||
|
||||
enum class VisitorState {
|
||||
EXPECT_NODES_TO_SKIP,
|
||||
EXPECT_VARIABLE, EXPECT_VARIABLE_VALUE,
|
||||
EXPECT_END, INVALID
|
||||
}
|
||||
|
||||
private fun collectMacroNames(nativeIndex: NativeIndexImpl, translationUnit: CXTranslationUnit, headers: Set<CXFile?>): List<String> {
|
||||
val result = mutableSetOf<String>()
|
||||
|
||||
visitChildren(translationUnit) { cursor, _ ->
|
||||
val file = memScoped {
|
||||
val fileVar = alloc<CXFileVar>()
|
||||
clang_getFileLocation(clang_getCursorLocation(cursor), fileVar.ptr, null, null, null)
|
||||
fileVar.value
|
||||
}
|
||||
|
||||
if (cursor.kind == CXCursorKind.CXCursor_MacroDefinition &&
|
||||
nativeIndex.library.includesDeclaration(cursor) &&
|
||||
file != null && // Builtin macros mostly seem to be useless.
|
||||
file in headers &&
|
||||
canMacroBeConstant(cursor))
|
||||
{
|
||||
val spelling = getCursorSpelling(cursor)
|
||||
result.add(spelling)
|
||||
}
|
||||
CXChildVisitResult.CXChildVisit_Continue
|
||||
}
|
||||
|
||||
return result.toList()
|
||||
}
|
||||
|
||||
private fun canMacroBeConstant(cursor: CValue<CXCursor>): Boolean {
|
||||
|
||||
if (clang_Cursor_isMacroFunctionLike(cursor) != 0) {
|
||||
return false
|
||||
}
|
||||
|
||||
// TODO: check number of tokens and filter out empty definitions;
|
||||
// Requires updating to 3.9.1 due to https://bugs.llvm.org//show_bug.cgi?id=9069
|
||||
|
||||
return true
|
||||
}
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
package org.jetbrains.kotlin.native.interop.indexer
|
||||
|
||||
import clang.*
|
||||
import kotlinx.cinterop.*
|
||||
import java.nio.file.Files
|
||||
|
||||
data class ModulesInfo(val topLevelHeaders: List<String>, val ownHeaders: Set<String>)
|
||||
|
||||
fun getModulesInfo(compilation: Compilation, modules: List<String>): ModulesInfo {
|
||||
if (modules.isEmpty()) return ModulesInfo(emptyList(), emptySet())
|
||||
|
||||
withIndex { index ->
|
||||
ModularCompilation(compilation).use {
|
||||
val modulesASTFiles = getModulesASTFiles(index, it, modules)
|
||||
return buildModulesInfo(index, modules, modulesASTFiles)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildModulesInfo(index: CXIndex, modules: List<String>, modulesASTFiles: List<String>): ModulesInfo {
|
||||
val ownHeaders = mutableSetOf<String>()
|
||||
val topLevelHeaders = linkedSetOf<String>()
|
||||
modulesASTFiles.forEach {
|
||||
val moduleTranslationUnit = clang_createTranslationUnit(index, it)!!
|
||||
try {
|
||||
val modulesHeaders = getModulesHeaders(index, moduleTranslationUnit, modules.toSet(), topLevelHeaders)
|
||||
modulesHeaders.mapTo(ownHeaders) { it.canonicalPath }
|
||||
} finally {
|
||||
clang_disposeTranslationUnit(moduleTranslationUnit)
|
||||
}
|
||||
}
|
||||
|
||||
return ModulesInfo(topLevelHeaders.toList(), ownHeaders)
|
||||
}
|
||||
|
||||
internal open class ModularCompilation(compilation: Compilation): Compilation by compilation, Disposable {
|
||||
|
||||
companion object {
|
||||
private const val moduleCacheFlag = "-fmodules-cache-path="
|
||||
}
|
||||
|
||||
private val moduleCacheDirectory = if (compilation.compilerArgs.none { it.startsWith(moduleCacheFlag) }) {
|
||||
Files.createTempDirectory("ModuleCache").toAbsolutePath().toFile()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
override val compilerArgs: List<String> = compilation.compilerArgs +
|
||||
listOfNotNull("-fmodules", moduleCacheDirectory?.let { "$moduleCacheFlag${it}" })
|
||||
|
||||
override fun dispose() {
|
||||
moduleCacheDirectory?.deleteRecursively()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getModulesASTFiles(index: CXIndex, compilation: ModularCompilation, modules: List<String>): List<String> {
|
||||
val compilationWithImports = compilation.copy(
|
||||
additionalPreambleLines = modules.map { "@import $it;" } + compilation.additionalPreambleLines
|
||||
)
|
||||
|
||||
val result = linkedSetOf<String>()
|
||||
|
||||
val translationUnit = compilationWithImports.parse(
|
||||
index,
|
||||
options = CXTranslationUnit_DetailedPreprocessingRecord
|
||||
)
|
||||
try {
|
||||
translationUnit.ensureNoCompileErrors()
|
||||
|
||||
indexTranslationUnit(index, translationUnit, 0, object : Indexer {
|
||||
override fun importedASTFile(info: CXIdxImportedASTFileInfo) {
|
||||
result += info.file!!.canonicalPath
|
||||
}
|
||||
})
|
||||
} finally {
|
||||
clang_disposeTranslationUnit(translationUnit)
|
||||
}
|
||||
return result.toList()
|
||||
}
|
||||
|
||||
private fun getModulesHeaders(
|
||||
index: CXIndex,
|
||||
translationUnit: CXTranslationUnit,
|
||||
modules: Set<String>,
|
||||
topLevelHeaders: LinkedHashSet<String>
|
||||
): Set<CXFile> {
|
||||
val nonModularIncludes = mutableMapOf<CXFile, MutableSet<CXFile>>()
|
||||
val result = mutableSetOf<CXFile>()
|
||||
|
||||
indexTranslationUnit(index, translationUnit, 0, object : Indexer {
|
||||
override fun ppIncludedFile(info: CXIdxIncludedFileInfo) {
|
||||
val file = info.file!!
|
||||
val includer = clang_indexLoc_getCXSourceLocation(info.hashLoc.readValue()).getContainingFile()
|
||||
|
||||
if (includer == null) {
|
||||
// i.e. the header is included by the module itself.
|
||||
topLevelHeaders += file.path
|
||||
}
|
||||
|
||||
val module = clang_getModuleForFile(translationUnit, file)
|
||||
|
||||
if (module != null) {
|
||||
val moduleWithParents = generateSequence(module, { clang_Module_getParent(it) }).map {
|
||||
clang_Module_getFullName(it).convertAndDispose()
|
||||
}
|
||||
|
||||
if (moduleWithParents.any { it in modules }) {
|
||||
result += file
|
||||
}
|
||||
} else if (includer != null) {
|
||||
nonModularIncludes.getOrPut(includer, { mutableSetOf() }) += file
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
// There are cases when non-modular includes should also be considered as a part of module. For example:
|
||||
// 1. Some module maps are broken,
|
||||
// e.g. system header `IOKit/hid/IOHIDProperties.h` isn't included to framework module map at all.
|
||||
// 2. Textual headers are reported as non-modular by libclang.
|
||||
//
|
||||
// Find and include non-modular headers too:
|
||||
result += findReachable(roots = result, arcs = nonModularIncludes)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private fun <T> findReachable(roots: Set<T>, arcs: Map<T, Set<T>>): Set<T> {
|
||||
val visited = mutableSetOf<T>()
|
||||
|
||||
fun dfs(vertex: T) {
|
||||
if (!visited.add(vertex)) return
|
||||
arcs[vertex].orEmpty().forEach { dfs(it) }
|
||||
}
|
||||
|
||||
roots.forEach { dfs(it) }
|
||||
|
||||
return visited
|
||||
}
|
||||
+331
@@ -0,0 +1,331 @@
|
||||
/*
|
||||
* 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.native.interop.indexer
|
||||
|
||||
enum class Language(val sourceFileExtension: String) {
|
||||
C("c"),
|
||||
OBJECTIVE_C("m")
|
||||
}
|
||||
|
||||
interface HeaderInclusionPolicy {
|
||||
/**
|
||||
* Whether unused declarations from given header should be excluded.
|
||||
*
|
||||
* @param headerName header path relative to the appropriate include path element (e.g. `time.h` or `curl/curl.h`),
|
||||
* or `null` for builtin declarations.
|
||||
*/
|
||||
fun excludeUnused(headerName: String?): Boolean
|
||||
}
|
||||
|
||||
interface HeaderExclusionPolicy {
|
||||
/**
|
||||
* Whether all declarations from this header should be excluded.
|
||||
*
|
||||
* Note: the declarations from such headers can be actually present in the internal representation,
|
||||
* but not included into the root collections.
|
||||
*/
|
||||
fun excludeAll(headerId: HeaderId): Boolean
|
||||
}
|
||||
|
||||
sealed class NativeLibraryHeaderFilter {
|
||||
class NameBased(
|
||||
val policy: HeaderInclusionPolicy,
|
||||
val excludeDepdendentModules: Boolean
|
||||
) : NativeLibraryHeaderFilter()
|
||||
|
||||
class Predefined(val headers: Set<String>) : NativeLibraryHeaderFilter()
|
||||
}
|
||||
|
||||
interface Compilation {
|
||||
val includes: List<String>
|
||||
val additionalPreambleLines: List<String>
|
||||
val compilerArgs: List<String>
|
||||
val language: Language
|
||||
}
|
||||
|
||||
data class CompilationWithPCH(
|
||||
override val compilerArgs: List<String>,
|
||||
override val language: Language
|
||||
) : Compilation {
|
||||
|
||||
constructor(compilerArgs: List<String>, precompiledHeader: String, language: Language)
|
||||
: this(compilerArgs + listOf("-include-pch", precompiledHeader), language)
|
||||
|
||||
override val includes: List<String>
|
||||
get() = emptyList()
|
||||
|
||||
override val additionalPreambleLines: List<String>
|
||||
get() = emptyList()
|
||||
}
|
||||
|
||||
// TODO: Compilation hierarchy seems to require some refactoring.
|
||||
|
||||
data class NativeLibrary(override val includes: List<String>,
|
||||
override val additionalPreambleLines: List<String>,
|
||||
override val compilerArgs: List<String>,
|
||||
val headerToIdMapper: HeaderToIdMapper,
|
||||
override val language: Language,
|
||||
val excludeSystemLibs: Boolean, // TODO: drop?
|
||||
val headerExclusionPolicy: HeaderExclusionPolicy,
|
||||
val headerFilter: NativeLibraryHeaderFilter) : Compilation
|
||||
|
||||
data class IndexerResult(val index: NativeIndex, val compilation: CompilationWithPCH)
|
||||
|
||||
/**
|
||||
* Retrieves the definitions from given C header file using given compiler arguments (e.g. defines).
|
||||
*/
|
||||
fun buildNativeIndex(library: NativeLibrary, verbose: Boolean): IndexerResult = buildNativeIndexImpl(library, verbose)
|
||||
|
||||
/**
|
||||
* This class describes the IR of definitions from C header file(s).
|
||||
*/
|
||||
abstract class NativeIndex {
|
||||
abstract val structs: Collection<StructDecl>
|
||||
abstract val enums: Collection<EnumDef>
|
||||
abstract val objCClasses: Collection<ObjCClass>
|
||||
abstract val objCProtocols: Collection<ObjCProtocol>
|
||||
abstract val objCCategories: Collection<ObjCCategory>
|
||||
abstract val typedefs: Collection<TypedefDef>
|
||||
abstract val functions: Collection<FunctionDecl>
|
||||
abstract val macroConstants: Collection<ConstantDef>
|
||||
abstract val wrappedMacros: Collection<WrappedMacroDef>
|
||||
abstract val globals: Collection<GlobalDecl>
|
||||
abstract val includedHeaders: Collection<HeaderId>
|
||||
}
|
||||
|
||||
/**
|
||||
* The (contents-based) header id.
|
||||
* Its [value] remains valid across different runs of the indexer and the process,
|
||||
* and thus can be used to 'serialize' the id.
|
||||
*/
|
||||
data class HeaderId(val value: String)
|
||||
|
||||
data class Location(val headerId: HeaderId)
|
||||
|
||||
interface TypeDeclaration {
|
||||
val location: Location
|
||||
}
|
||||
|
||||
sealed class StructMember(val name: String, val type: Type) {
|
||||
abstract val offset: Long?
|
||||
}
|
||||
|
||||
/**
|
||||
* C struct field.
|
||||
*/
|
||||
class Field(name: String, type: Type, override val offset: Long, val typeSize: Long, val typeAlign: Long)
|
||||
: StructMember(name, type)
|
||||
|
||||
val Field.isAligned: Boolean
|
||||
get() = offset % (typeAlign * 8) == 0L
|
||||
|
||||
class BitField(name: String, type: Type, override val offset: Long, val size: Int) : StructMember(name, type)
|
||||
|
||||
class IncompleteField(name: String, type: Type) : StructMember(name, type) {
|
||||
override val offset: Long? get() = null
|
||||
}
|
||||
|
||||
/**
|
||||
* C struct declaration.
|
||||
*/
|
||||
abstract class StructDecl(val spelling: String) : TypeDeclaration {
|
||||
|
||||
abstract val def: StructDef?
|
||||
}
|
||||
|
||||
/**
|
||||
* C struct definition.
|
||||
*
|
||||
* @param hasNaturalLayout must be `false` if the struct has unnatural layout, e.g. it is `packed`.
|
||||
* May be `false` even if the struct has natural layout.
|
||||
*/
|
||||
abstract class StructDef(val size: Long, val align: Int, val decl: StructDecl) {
|
||||
|
||||
enum class Kind {
|
||||
STRUCT, UNION
|
||||
}
|
||||
|
||||
abstract val members: List<StructMember>
|
||||
abstract val kind: Kind
|
||||
|
||||
val fields: List<Field> get() = members.filterIsInstance<Field>()
|
||||
val bitFields: List<BitField> get() = members.filterIsInstance<BitField>()
|
||||
}
|
||||
|
||||
/**
|
||||
* C enum value.
|
||||
*/
|
||||
class EnumConstant(val name: String, val value: Long, val isExplicitlyDefined: Boolean)
|
||||
|
||||
/**
|
||||
* C enum definition.
|
||||
*/
|
||||
abstract class EnumDef(val spelling: String, val baseType: Type) : TypeDeclaration {
|
||||
|
||||
abstract val constants: List<EnumConstant>
|
||||
}
|
||||
|
||||
sealed class ObjCContainer {
|
||||
abstract val protocols: List<ObjCProtocol>
|
||||
abstract val methods: List<ObjCMethod>
|
||||
abstract val properties: List<ObjCProperty>
|
||||
}
|
||||
|
||||
sealed class ObjCClassOrProtocol(val name: String) : ObjCContainer(), TypeDeclaration {
|
||||
abstract val isForwardDeclaration: Boolean
|
||||
}
|
||||
|
||||
data class ObjCMethod(
|
||||
val selector: String, val encoding: String, val parameters: List<Parameter>, private val returnType: Type,
|
||||
val isVariadic: Boolean, val isClass: Boolean, val nsConsumesSelf: Boolean, val nsReturnsRetained: Boolean,
|
||||
val isOptional: Boolean, val isInit: Boolean, val isExplicitlyDesignatedInitializer: Boolean
|
||||
) {
|
||||
|
||||
fun returnsInstancetype(): Boolean = returnType is ObjCInstanceType
|
||||
|
||||
fun getReturnType(container: ObjCClassOrProtocol): Type = if (returnType is ObjCInstanceType) {
|
||||
when (container) {
|
||||
is ObjCClass -> ObjCObjectPointer(container, returnType.nullability, protocols = emptyList())
|
||||
is ObjCProtocol -> ObjCIdType(returnType.nullability, protocols = listOf(container))
|
||||
}
|
||||
} else {
|
||||
returnType
|
||||
}
|
||||
}
|
||||
|
||||
data class ObjCProperty(val name: String, val getter: ObjCMethod, val setter: ObjCMethod?) {
|
||||
fun getType(container: ObjCClassOrProtocol): Type = getter.getReturnType(container)
|
||||
}
|
||||
|
||||
abstract class ObjCClass(name: String) : ObjCClassOrProtocol(name) {
|
||||
abstract val binaryName: String?
|
||||
abstract val baseClass: ObjCClass?
|
||||
}
|
||||
abstract class ObjCProtocol(name: String) : ObjCClassOrProtocol(name)
|
||||
|
||||
abstract class ObjCCategory(val name: String, val clazz: ObjCClass) : ObjCContainer()
|
||||
|
||||
/**
|
||||
* C function parameter.
|
||||
*/
|
||||
data class Parameter(val name: String?, val type: Type, val nsConsumed: Boolean)
|
||||
|
||||
/**
|
||||
* C function declaration.
|
||||
*/
|
||||
class FunctionDecl(val name: String, val parameters: List<Parameter>, val returnType: Type, val binaryName: String,
|
||||
val isDefined: Boolean, val isVararg: Boolean)
|
||||
|
||||
/**
|
||||
* C typedef definition.
|
||||
*
|
||||
* ```
|
||||
* typedef $aliased $name;
|
||||
* ```
|
||||
*/
|
||||
class TypedefDef(val aliased: Type, val name: String, override val location: Location) : TypeDeclaration
|
||||
|
||||
abstract class MacroDef(val name: String)
|
||||
|
||||
abstract class ConstantDef(name: String, val type: Type): MacroDef(name)
|
||||
class IntegerConstantDef(name: String, type: Type, val value: Long) : ConstantDef(name, type)
|
||||
class FloatingConstantDef(name: String, type: Type, val value: Double) : ConstantDef(name, type)
|
||||
class StringConstantDef(name: String, type: Type, val value: String) : ConstantDef(name, type)
|
||||
|
||||
class WrappedMacroDef(name: String, val type: Type) : MacroDef(name)
|
||||
|
||||
class GlobalDecl(val name: String, val type: Type, val isConst: Boolean)
|
||||
|
||||
/**
|
||||
* C type.
|
||||
*/
|
||||
interface Type
|
||||
|
||||
interface PrimitiveType : Type
|
||||
|
||||
object CharType : PrimitiveType
|
||||
|
||||
open class BoolType: PrimitiveType
|
||||
|
||||
object CBoolType : BoolType()
|
||||
|
||||
object ObjCBoolType : BoolType()
|
||||
// We omit `const` qualifier for IntegerType and FloatingType to make `CBridgeGen` simpler.
|
||||
// See KT-28102.
|
||||
data class IntegerType(val size: Int, val isSigned: Boolean, val spelling: String) : PrimitiveType
|
||||
|
||||
// TODO: floating type is not actually defined entirely by its size.
|
||||
data class FloatingType(val size: Int, val spelling: String) : PrimitiveType
|
||||
|
||||
data class VectorType(val elementType: Type, val elementCount: Int, val spelling: String) : PrimitiveType
|
||||
|
||||
object VoidType : Type
|
||||
|
||||
data class RecordType(val decl: StructDecl) : Type
|
||||
|
||||
data class EnumType(val def: EnumDef) : Type
|
||||
|
||||
data class PointerType(val pointeeType: Type, val pointeeIsConst: Boolean = false) : Type
|
||||
// TODO: refactor type representation and support type modifiers more generally.
|
||||
|
||||
data class FunctionType(val parameterTypes: List<Type>, val returnType: Type) : Type
|
||||
|
||||
interface ArrayType : Type {
|
||||
val elemType: Type
|
||||
}
|
||||
|
||||
data class ConstArrayType(override val elemType: Type, val length: Long) : ArrayType
|
||||
data class IncompleteArrayType(override val elemType: Type) : ArrayType
|
||||
data class VariableArrayType(override val elemType: Type) : ArrayType
|
||||
|
||||
data class Typedef(val def: TypedefDef) : Type
|
||||
|
||||
sealed class ObjCPointer : Type {
|
||||
enum class Nullability {
|
||||
Nullable, NonNull, Unspecified
|
||||
}
|
||||
|
||||
abstract val nullability: Nullability
|
||||
}
|
||||
|
||||
sealed class ObjCQualifiedPointer : ObjCPointer() {
|
||||
abstract val protocols: List<ObjCProtocol>
|
||||
}
|
||||
|
||||
data class ObjCObjectPointer(
|
||||
val def: ObjCClass,
|
||||
override val nullability: Nullability,
|
||||
override val protocols: List<ObjCProtocol>
|
||||
) : ObjCQualifiedPointer()
|
||||
|
||||
data class ObjCClassPointer(
|
||||
override val nullability: Nullability,
|
||||
override val protocols: List<ObjCProtocol>
|
||||
) : ObjCQualifiedPointer()
|
||||
|
||||
data class ObjCIdType(
|
||||
override val nullability: Nullability,
|
||||
override val protocols: List<ObjCProtocol>
|
||||
) : ObjCQualifiedPointer()
|
||||
|
||||
data class ObjCInstanceType(override val nullability: Nullability) : ObjCPointer()
|
||||
data class ObjCBlockPointer(
|
||||
override val nullability: Nullability,
|
||||
val parameterTypes: List<Type>, val returnType: Type
|
||||
) : ObjCPointer()
|
||||
|
||||
object UnsupportedType : Type
|
||||
+768
@@ -0,0 +1,768 @@
|
||||
/*
|
||||
* 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.native.interop.indexer
|
||||
|
||||
import clang.*
|
||||
import kotlinx.cinterop.*
|
||||
import java.io.Closeable
|
||||
import java.io.File
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.security.DigestInputStream
|
||||
import java.security.MessageDigest
|
||||
|
||||
internal val CValue<CXType>.kind: CXTypeKind get() = this.useContents { kind }
|
||||
|
||||
internal val CValue<CXCursor>.kind: CXCursorKind get() = this.useContents { kind }
|
||||
|
||||
internal val CValue<CXCursor>.type: CValue<CXType> get() = clang_getCursorType(this)
|
||||
internal val CValue<CXCursor>.spelling: String get() = clang_getCursorSpelling(this).convertAndDispose()
|
||||
internal val CValue<CXType>.name: String get() = clang_getTypeSpelling(this).convertAndDispose()
|
||||
internal val CXTypeKind.spelling: String get() = clang_getTypeKindSpelling(this).convertAndDispose()
|
||||
internal val CXCursorKind.spelling: String get() = clang_getCursorKindSpelling(this).convertAndDispose()
|
||||
|
||||
internal fun CValue<CXString>.convertAndDispose(): String {
|
||||
try {
|
||||
return clang_getCString(this)!!.toKString()
|
||||
} finally {
|
||||
clang_disposeString(this)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun CPointer<CXStringSet>.convertAndDispose(): Set<String> = try {
|
||||
(0 until this.pointed.Count).mapTo(mutableSetOf()) {
|
||||
clang_getCString(this.pointed.Strings!![it].readValue())!!.toKString()
|
||||
}
|
||||
} finally {
|
||||
clang_disposeStringSet(this)
|
||||
}
|
||||
|
||||
internal fun getCursorSpelling(cursor: CValue<CXCursor>) =
|
||||
clang_getCursorSpelling(cursor).convertAndDispose()
|
||||
|
||||
internal fun CValue<CXType>.getSize(): Long {
|
||||
val size = clang_Type_getSizeOf(this)
|
||||
if (size < 0) {
|
||||
throw Error(size.toString())
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
internal inline fun <R> withIndex(
|
||||
excludeDeclarationsFromPCH: Boolean = false,
|
||||
displayDiagnostics: Boolean = false,
|
||||
block: (index: CXIndex) -> R
|
||||
): R {
|
||||
val index = clang_createIndex(
|
||||
excludeDeclarationsFromPCH = if (excludeDeclarationsFromPCH) 1 else 0,
|
||||
displayDiagnostics = if (displayDiagnostics) 1 else 0
|
||||
)!!
|
||||
|
||||
return try {
|
||||
block(index)
|
||||
} finally {
|
||||
clang_disposeIndex(index)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun parseTranslationUnit(
|
||||
index: CXIndex,
|
||||
sourceFile: File,
|
||||
compilerArgs: List<String>,
|
||||
options: Int
|
||||
): CXTranslationUnit {
|
||||
|
||||
memScoped {
|
||||
val resultVar = alloc<CXTranslationUnitVar>()
|
||||
|
||||
val errorCode = clang_parseTranslationUnit2(
|
||||
index,
|
||||
sourceFile.absolutePath,
|
||||
compilerArgs.toNativeStringArray(memScope), compilerArgs.size,
|
||||
null, 0,
|
||||
options,
|
||||
resultVar.ptr
|
||||
)
|
||||
|
||||
if (errorCode != CXErrorCode.CXError_Success) {
|
||||
val copiedSourceFile = sourceFile.copyTo(Files.createTempFile(null, sourceFile.name).toFile(), overwrite = true)
|
||||
|
||||
error("""
|
||||
clang_parseTranslationUnit2 failed with $errorCode;
|
||||
sourceFile = ${copiedSourceFile.absolutePath}
|
||||
arguments = ${compilerArgs.joinToString(" ")}
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
return resultVar.value!!
|
||||
}
|
||||
}
|
||||
|
||||
internal fun Compilation.parse(index: CXIndex, options: Int = 0): CXTranslationUnit =
|
||||
parseTranslationUnit(index, this.createTempSource(), this.compilerArgs, options)
|
||||
|
||||
internal data class Diagnostic(val severity: CXDiagnosticSeverity, val format: String,
|
||||
val location: CValue<CXSourceLocation>)
|
||||
|
||||
internal fun CXTranslationUnit.getDiagnostics(): Sequence<Diagnostic> {
|
||||
val numDiagnostics = clang_getNumDiagnostics(this)
|
||||
return (0 until numDiagnostics).asSequence()
|
||||
.map { index ->
|
||||
val diagnostic = clang_getDiagnostic(this, index)
|
||||
try {
|
||||
val severity = clang_getDiagnosticSeverity(diagnostic)
|
||||
|
||||
val format = clang_formatDiagnostic(diagnostic, clang_defaultDiagnosticDisplayOptions())
|
||||
.convertAndDispose()
|
||||
|
||||
val location = clang_getDiagnosticLocation(diagnostic)
|
||||
|
||||
Diagnostic(severity, format, location)
|
||||
} finally {
|
||||
clang_disposeDiagnostic(diagnostic)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun CXTranslationUnit.getCompileErrors(): Sequence<String> =
|
||||
getDiagnostics().filter { it.isError() }.map { it.format }
|
||||
|
||||
private fun Diagnostic.isError() = (severity == CXDiagnosticSeverity.CXDiagnostic_Error) ||
|
||||
(severity == CXDiagnosticSeverity.CXDiagnostic_Fatal)
|
||||
|
||||
internal fun CXTranslationUnit.hasCompileErrors() = (this.getCompileErrors().firstOrNull() != null)
|
||||
|
||||
internal fun CXTranslationUnit.ensureNoCompileErrors(): CXTranslationUnit {
|
||||
val firstError = this.getCompileErrors().firstOrNull() ?: return this
|
||||
throw Error(firstError)
|
||||
}
|
||||
|
||||
internal typealias CursorVisitor = (cursor: CValue<CXCursor>, parent: CValue<CXCursor>) -> CXChildVisitResult
|
||||
|
||||
internal fun visitChildren(parent: CValue<CXCursor>, visitor: CursorVisitor) {
|
||||
val visitorStableRef = StableRef.create(visitor)
|
||||
try {
|
||||
val clientData = visitorStableRef.asCPointer()
|
||||
clang_visitChildren(parent, staticCFunction { cursorIt, parentIt, clientDataIt ->
|
||||
val visitorIt = clientDataIt!!.asStableRef<CursorVisitor>().get()
|
||||
visitorIt(cursorIt, parentIt)
|
||||
}, clientData)
|
||||
} finally {
|
||||
visitorStableRef.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
internal fun visitChildren(translationUnit: CXTranslationUnit, visitor: CursorVisitor) =
|
||||
visitChildren(clang_getTranslationUnitCursor(translationUnit), visitor)
|
||||
|
||||
internal fun getFields(type: CValue<CXType>): List<CValue<CXCursor>> {
|
||||
val result = mutableListOf<CValue<CXCursor>>()
|
||||
val resultStableRef = StableRef.create(result)
|
||||
try {
|
||||
val clientData = resultStableRef.asCPointer()
|
||||
|
||||
@Suppress("NAME_SHADOWING")
|
||||
clang_Type_visitFields(type, staticCFunction { cursor, clientData ->
|
||||
val result = clientData!!.asStableRef<MutableList<CValue<CXCursor>>>().get()
|
||||
result.add(cursor)
|
||||
CXVisitorResult.CXVisit_Continue
|
||||
}, clientData)
|
||||
|
||||
} finally {
|
||||
resultStableRef.dispose()
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
fun StructDef.fieldsHaveDefaultAlignment(): Boolean {
|
||||
fun alignUp(x: Long, alignment: Long): Long = (x + alignment - 1) and (alignment - 1).inv()
|
||||
|
||||
var offset = 0L
|
||||
this.members.forEach {
|
||||
when (it) {
|
||||
is Field -> {
|
||||
if (alignUp(offset, it.typeAlign) * 8 != it.offset) return false
|
||||
offset = it.offset / 8 + it.typeSize
|
||||
}
|
||||
is BitField -> return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
internal fun CValue<CXCursor>.hasExpressionChild(): Boolean {
|
||||
var result = false
|
||||
|
||||
visitChildren(this) { cursor, _ ->
|
||||
if (clang_isExpression(cursor.kind) != 0) {
|
||||
result = true
|
||||
CXChildVisitResult.CXChildVisit_Break
|
||||
} else {
|
||||
CXChildVisitResult.CXChildVisit_Continue
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
internal fun List<String>.toNativeStringArray(scope: AutofreeScope): CArrayPointer<CPointerVar<ByteVar>> {
|
||||
return scope.allocArray(this.size) { index ->
|
||||
this.value = this@toNativeStringArray[index].cstr.getPointer(scope)
|
||||
}
|
||||
}
|
||||
|
||||
val Compilation.preambleLines: List<String>
|
||||
get() = this.includes.map { "#include <$it>" } + this.additionalPreambleLines
|
||||
|
||||
internal fun Appendable.appendPreamble(compilation: Compilation) = this.apply {
|
||||
compilation.preambleLines.forEach {
|
||||
this.appendLine(it)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates temporary source file which includes the library.
|
||||
*/
|
||||
internal fun Compilation.createTempSource(): File {
|
||||
val result = Files.createTempFile(null, ".${language.sourceFileExtension}").toFile()
|
||||
result.deleteOnExit()
|
||||
|
||||
result.bufferedWriter().use { writer ->
|
||||
writer.appendPreamble(this)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
fun Compilation.copy(
|
||||
includes: List<String> = this.includes,
|
||||
additionalPreambleLines: List<String> = this.additionalPreambleLines,
|
||||
compilerArgs: List<String> = this.compilerArgs,
|
||||
language: Language = this.language
|
||||
): Compilation = CompilationImpl(
|
||||
includes = includes,
|
||||
additionalPreambleLines = additionalPreambleLines,
|
||||
compilerArgs = compilerArgs,
|
||||
language = language
|
||||
)
|
||||
|
||||
// Clang-8 crashes when consuming a precompiled header built with -fmodule-map-file argument (see KT-34467).
|
||||
// We ignore this argument when building a pch to workaround this crash.
|
||||
fun Compilation.copyWithArgsForPCH(): Compilation =
|
||||
copy(compilerArgs = compilerArgs.filterNot { it.startsWith("-fmodule-map-file") })
|
||||
|
||||
data class CompilationImpl(
|
||||
override val includes: List<String>,
|
||||
override val additionalPreambleLines: List<String>,
|
||||
override val compilerArgs: List<String>,
|
||||
override val language: Language
|
||||
) : Compilation
|
||||
|
||||
/**
|
||||
* Precompiles the headers of this library.
|
||||
*
|
||||
* @return the library which includes the precompiled header instead of original ones.
|
||||
*/
|
||||
fun Compilation.precompileHeaders(): CompilationWithPCH = withIndex { index ->
|
||||
val options = CXTranslationUnit_ForSerialization
|
||||
val translationUnit = copyWithArgsForPCH().parse(index, options)
|
||||
try {
|
||||
translationUnit.ensureNoCompileErrors()
|
||||
withPrecompiledHeader(translationUnit)
|
||||
} finally {
|
||||
clang_disposeTranslationUnit(translationUnit)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun Compilation.withPrecompiledHeader(translationUnit: CXTranslationUnit): CompilationWithPCH {
|
||||
val precompiledHeader = Files.createTempFile(null, ".pch").toFile().apply { this.deleteOnExit() }
|
||||
clang_saveTranslationUnit(translationUnit, precompiledHeader.absolutePath, 0)
|
||||
|
||||
return CompilationWithPCH(this.compilerArgs, precompiledHeader.absolutePath, this.language)
|
||||
}
|
||||
|
||||
internal fun NativeLibrary.includesDeclaration(cursor: CValue<CXCursor>): Boolean {
|
||||
return if (this.excludeSystemLibs) {
|
||||
clang_Location_isInSystemHeader(clang_getCursorLocation(cursor)) == 0
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
internal fun CXTranslationUnit.getErrorLineNumbers(): Sequence<Int> =
|
||||
getDiagnostics().filter {
|
||||
it.isError()
|
||||
}.map {
|
||||
memScoped {
|
||||
val lineNumberVar = alloc<IntVar>()
|
||||
clang_getFileLocation(it.location, null, lineNumberVar.ptr, null, null)
|
||||
lineNumberVar.value
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For each list of lines, checks if the code fragment composed from these lines is compilable against given library.
|
||||
*/
|
||||
fun List<List<String>>.mapFragmentIsCompilable(originalLibrary: CompilationWithPCH): List<Boolean> {
|
||||
val library: CompilationWithPCH = originalLibrary
|
||||
.copy(compilerArgs = originalLibrary.compilerArgs + "-ferror-limit=0")
|
||||
|
||||
val indicesOfNonCompilable = mutableSetOf<Int>()
|
||||
|
||||
val fragmentsToCheck = this.withIndex().toMutableList()
|
||||
|
||||
withIndex(excludeDeclarationsFromPCH = true) { index ->
|
||||
val sourceFile = library.createTempSource()
|
||||
val translationUnit = parseTranslationUnit(index, sourceFile, library.compilerArgs, options = 0)
|
||||
try {
|
||||
translationUnit.ensureNoCompileErrors()
|
||||
while (fragmentsToCheck.isNotEmpty()) {
|
||||
// Combine all fragments to be checked in a single file:
|
||||
sourceFile.bufferedWriter().use { writer ->
|
||||
writer.appendPreamble(library)
|
||||
fragmentsToCheck.forEach {
|
||||
it.value.forEach {
|
||||
assert(!it.contains('\n'))
|
||||
writer.appendLine(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clang_reparseTranslationUnit(translationUnit, 0, null, 0)
|
||||
val errorLineNumbers = translationUnit.getErrorLineNumbers().toSet()
|
||||
|
||||
// Retain only those fragments that contain compilation error locations:
|
||||
var lastLineNumber = library.preambleLines.size
|
||||
fragmentsToCheck.retainAll {
|
||||
val firstLineNumber = lastLineNumber + 1
|
||||
lastLineNumber += it.value.size
|
||||
(firstLineNumber .. lastLineNumber).any { it in errorLineNumbers }
|
||||
}
|
||||
|
||||
if (fragmentsToCheck.isNotEmpty()) {
|
||||
// The first fragment is now known to be non-compilable.
|
||||
val firstFragment = fragmentsToCheck.removeAt(0)
|
||||
indicesOfNonCompilable.add(firstFragment.index)
|
||||
}
|
||||
|
||||
// The remaining fragments was potentially influenced by the first one,
|
||||
// and thus require to be checked again.
|
||||
}
|
||||
} finally {
|
||||
clang_disposeTranslationUnit(translationUnit)
|
||||
}
|
||||
}
|
||||
|
||||
return this.indices.map { it !in indicesOfNonCompilable }
|
||||
}
|
||||
|
||||
internal interface Indexer {
|
||||
/**
|
||||
* Called when entered main file.
|
||||
*/
|
||||
fun enteredMainFile(file: CXFile) {}
|
||||
|
||||
/**
|
||||
* Called when a file gets #included/#imported.
|
||||
*/
|
||||
fun ppIncludedFile(info: CXIdxIncludedFileInfo) {}
|
||||
|
||||
/**
|
||||
* Called when a AST file (PCH or module) gets imported.
|
||||
*/
|
||||
fun importedASTFile(info: CXIdxImportedASTFileInfo) {}
|
||||
|
||||
/**
|
||||
* Called to index a declaration.
|
||||
*/
|
||||
fun indexDeclaration(info: CXIdxDeclInfo) {}
|
||||
}
|
||||
|
||||
internal fun indexTranslationUnit(index: CXIndex, translationUnit: CXTranslationUnit, options: Int, indexer: Indexer) {
|
||||
val indexerStableRef = StableRef.create(indexer)
|
||||
try {
|
||||
val clientData = indexerStableRef.asCPointer()
|
||||
memScoped {
|
||||
val indexerCallbacks = alloc<IndexerCallbacks>().apply {
|
||||
abortQuery = null
|
||||
diagnostic = null
|
||||
enteredMainFile = staticCFunction { clientData, mainFile, _ ->
|
||||
@Suppress("NAME_SHADOWING")
|
||||
val indexer = clientData!!.asStableRef<Indexer>().get()
|
||||
indexer.enteredMainFile(mainFile!!)
|
||||
// We must ensure only interop types exist in function signature.
|
||||
@Suppress("USELESS_CAST")
|
||||
null as CXIdxClientFile?
|
||||
}
|
||||
ppIncludedFile = staticCFunction { clientData, info ->
|
||||
@Suppress("NAME_SHADOWING")
|
||||
val indexer = clientData!!.asStableRef<Indexer>().get()
|
||||
indexer.ppIncludedFile(info!!.pointed)
|
||||
// We must ensure only interop types exist in function signature.
|
||||
@Suppress("USELESS_CAST")
|
||||
null as CXIdxClientFile?
|
||||
}
|
||||
importedASTFile = staticCFunction { clientData, info ->
|
||||
@Suppress("NAME_SHADOWING")
|
||||
val indexer = clientData!!.asStableRef<Indexer>().get()
|
||||
indexer.importedASTFile(info!!.pointed)
|
||||
// We must ensure only interop types exist in function signature.
|
||||
@Suppress("USELESS_CAST")
|
||||
null as CXIdxClientFile?
|
||||
}
|
||||
startedTranslationUnit = null
|
||||
indexDeclaration = staticCFunction { clientData, info ->
|
||||
@Suppress("NAME_SHADOWING")
|
||||
val nativeIndex = clientData!!.asStableRef<Indexer>().get()
|
||||
nativeIndex.indexDeclaration(info!!.pointed)
|
||||
}
|
||||
indexEntityReference = null
|
||||
}
|
||||
|
||||
val indexAction = clang_IndexAction_create(index)
|
||||
try {
|
||||
val result = clang_indexTranslationUnit(indexAction, clientData,
|
||||
indexerCallbacks.ptr, sizeOf<IndexerCallbacks>().toInt(), options, translationUnit)
|
||||
|
||||
if (result != 0) {
|
||||
throw Error("clang_indexTranslationUnit returned $result")
|
||||
}
|
||||
} finally {
|
||||
clang_IndexAction_dispose(indexAction)
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
indexerStableRef.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
internal class ModulesMap(
|
||||
val compilation: Compilation,
|
||||
val translationUnit: CXTranslationUnit
|
||||
) : Closeable {
|
||||
|
||||
private val modularCompilation: ModularCompilation
|
||||
private val index: CXIndex
|
||||
private val translationUnitWithModules: CXTranslationUnit
|
||||
|
||||
private val arena = Arena()
|
||||
|
||||
private inline fun <T> T.toBeDisposedWith(crossinline block: (T) -> Unit): T = apply {
|
||||
arena.defer { block(this) }
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
arena.clear()
|
||||
}
|
||||
|
||||
init {
|
||||
try {
|
||||
modularCompilation = ModularCompilation(compilation)
|
||||
.toBeDisposedWith { it.dispose() }
|
||||
|
||||
index = clang_createIndex(0, 0)!!
|
||||
.toBeDisposedWith { clang_disposeIndex(it) }
|
||||
|
||||
translationUnitWithModules = modularCompilation.parse(index)
|
||||
.toBeDisposedWith { clang_disposeTranslationUnit(it) }
|
||||
|
||||
translationUnitWithModules.ensureNoCompileErrors()
|
||||
|
||||
} catch (e: Throwable) {
|
||||
this.close()
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
data class Module(private val cxModule: CXModule)
|
||||
|
||||
fun getModule(file: CXFile): Module? {
|
||||
// `file` is bound to `translationUnit`, however `translationUnitWithModules` is used to access modules.
|
||||
// Find the corresponding file in `translationUnitWithModules`:
|
||||
val fileInTuWithModules =
|
||||
clang_getFile(translationUnitWithModules, clang_getFileName(file).convertAndDispose())!!
|
||||
|
||||
return clang_getModuleForFile(translationUnitWithModules, fileInTuWithModules)?.let { Module(it) }
|
||||
}
|
||||
}
|
||||
|
||||
internal fun getHeaderId(library: NativeLibrary, header: CXFile?): HeaderId {
|
||||
if (header == null) {
|
||||
return HeaderId("builtins")
|
||||
}
|
||||
|
||||
val filePath = header.path
|
||||
return library.headerToIdMapper.getHeaderId(filePath)
|
||||
}
|
||||
|
||||
internal fun getFilteredHeaders(
|
||||
nativeIndex: NativeIndexImpl,
|
||||
index: CXIndex,
|
||||
translationUnit: CXTranslationUnit
|
||||
): Set<CXFile?> = getHeaders(nativeIndex.library, index, translationUnit).ownHeaders
|
||||
|
||||
class NativeLibraryHeaders<Header>(val ownHeaders: Set<Header>, val importedHeaders: Set<Header>)
|
||||
|
||||
internal fun getHeaders(
|
||||
library: NativeLibrary,
|
||||
index: CXIndex,
|
||||
translationUnit: CXTranslationUnit
|
||||
): NativeLibraryHeaders<CXFile?> {
|
||||
val ownHeaders = mutableSetOf<CXFile?>()
|
||||
val allHeaders = mutableSetOf<CXFile?>(null)
|
||||
|
||||
val filter = library.headerFilter
|
||||
|
||||
when (filter) {
|
||||
is NativeLibraryHeaderFilter.NameBased ->
|
||||
filterHeadersByName(library, filter, index, translationUnit, ownHeaders, allHeaders)
|
||||
|
||||
is NativeLibraryHeaderFilter.Predefined ->
|
||||
filterHeadersByPredefined(filter, index, translationUnit, ownHeaders, allHeaders)
|
||||
}
|
||||
|
||||
ownHeaders.removeAll { library.headerExclusionPolicy.excludeAll(getHeaderId(library, it)) }
|
||||
|
||||
return NativeLibraryHeaders(ownHeaders, allHeaders - ownHeaders)
|
||||
}
|
||||
|
||||
private fun filterHeadersByName(
|
||||
compilation: Compilation,
|
||||
filter: NativeLibraryHeaderFilter.NameBased,
|
||||
index: CXIndex,
|
||||
translationUnit: CXTranslationUnit,
|
||||
ownHeaders: MutableSet<CXFile?>,
|
||||
allHeaders: MutableSet<CXFile?>
|
||||
) {
|
||||
val topLevelFiles = mutableListOf<CXFile>()
|
||||
var mainFile: CXFile? = null
|
||||
|
||||
indexTranslationUnit(index, translationUnit, 0, object : Indexer {
|
||||
val headerToName = mutableMapOf<CXFile, String>()
|
||||
// The *name* of the header here is the path relative to the include path element., e.g. `curl/curl.h`.
|
||||
|
||||
override fun enteredMainFile(file: CXFile) {
|
||||
mainFile = file
|
||||
allHeaders += file
|
||||
}
|
||||
|
||||
override fun ppIncludedFile(info: CXIdxIncludedFileInfo) {
|
||||
val includeLocation = clang_indexLoc_getCXSourceLocation(info.hashLoc.readValue())
|
||||
val file = info.file!!
|
||||
|
||||
allHeaders += file
|
||||
|
||||
if (clang_Location_isFromMainFile(includeLocation) != 0) {
|
||||
topLevelFiles.add(file)
|
||||
}
|
||||
|
||||
val name = info.filename!!.toKString()
|
||||
val headerName = if (info.isAngled != 0) {
|
||||
// If the header is included with `#include <$name>`, then `name` is probably
|
||||
// the path relative to the include path element.
|
||||
name
|
||||
} else {
|
||||
// If it is included with `#include "$name"`, then `name` can also be the path relative to the includer.
|
||||
val includerFile = includeLocation.getContainingFile()!!
|
||||
val includerName = headerToName[includerFile] ?: ""
|
||||
val includerPath = includerFile.path
|
||||
|
||||
if (clang_getFile(translationUnit, Paths.get(includerPath).resolveSibling(name).toString()) == file) {
|
||||
// included file is accessible from the includer by `name` used as relative path, so
|
||||
// `name` seems to be relative to the includer:
|
||||
Paths.get(includerName).resolveSibling(name).normalize().toString()
|
||||
} else {
|
||||
name
|
||||
}
|
||||
}
|
||||
|
||||
headerToName[file] = headerName
|
||||
if (!filter.policy.excludeUnused(headerName)) {
|
||||
ownHeaders.add(file)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (filter.excludeDepdendentModules) {
|
||||
ModulesMap(compilation, translationUnit).use { modulesMap ->
|
||||
val topLevelModules = topLevelFiles.map { modulesMap.getModule(it) }.toSet()
|
||||
ownHeaders.removeAll {
|
||||
val module = modulesMap.getModule(it!!)
|
||||
module !in topLevelModules
|
||||
}
|
||||
// Note: if some of the top-level headers don't belong to modules,
|
||||
// then all non-modular headers are included.
|
||||
}
|
||||
} else {
|
||||
if (!filter.policy.excludeUnused(headerName = null)) {
|
||||
// Builtins.
|
||||
ownHeaders.add(null)
|
||||
}
|
||||
}
|
||||
|
||||
ownHeaders.add(mainFile!!)
|
||||
}
|
||||
|
||||
private fun filterHeadersByPredefined(
|
||||
filter: NativeLibraryHeaderFilter.Predefined,
|
||||
index: CXIndex,
|
||||
translationUnit: CXTranslationUnit,
|
||||
ownHeaders: MutableSet<CXFile?>,
|
||||
allHeaders: MutableSet<CXFile?>
|
||||
) {
|
||||
// Note: suboptimal but simple.
|
||||
indexTranslationUnit(index, translationUnit, 0, object : Indexer {
|
||||
override fun ppIncludedFile(info: CXIdxIncludedFileInfo) {
|
||||
val file = info.file
|
||||
allHeaders += file
|
||||
if (file?.canonicalPath in filter.headers) {
|
||||
ownHeaders += file
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun NativeLibrary.getHeaderPaths(): NativeLibraryHeaders<String> {
|
||||
withIndex { index ->
|
||||
val translationUnit =
|
||||
this.parse(index, options = CXTranslationUnit_DetailedPreprocessingRecord).ensureNoCompileErrors()
|
||||
try {
|
||||
|
||||
fun getPath(file: CXFile?) = if (file == null) "<builtins>" else file.canonicalPath
|
||||
|
||||
val headers = getHeaders(this, index, translationUnit)
|
||||
return NativeLibraryHeaders(
|
||||
headers.ownHeaders.map(::getPath).toSet(),
|
||||
headers.importedHeaders.map(::getPath).toSet()
|
||||
)
|
||||
} finally {
|
||||
clang_disposeTranslationUnit(translationUnit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun ObjCMethod.replaces(other: ObjCMethod): Boolean =
|
||||
this.isClass == other.isClass && this.selector == other.selector
|
||||
|
||||
fun ObjCProperty.replaces(other: ObjCProperty): Boolean =
|
||||
this.getter.replaces(other.getter)
|
||||
|
||||
fun File.sha256(): String {
|
||||
val digest = MessageDigest.getInstance("SHA-256")
|
||||
DigestInputStream(this.inputStream(), digest).use { dis ->
|
||||
val buffer = ByteArray(8192)
|
||||
// Read all bytes:
|
||||
while (dis.read(buffer, 0, buffer.size) != -1) {}
|
||||
}
|
||||
// Convert to hex:
|
||||
return digest.digest().joinToString("") {
|
||||
Integer.toHexString((it.toInt() and 0xff) + 0x100).substring(1)
|
||||
}
|
||||
}
|
||||
|
||||
fun headerContentsHash(filePath: String) = File(filePath).sha256()
|
||||
|
||||
internal fun CValue<CXSourceLocation>.getContainingFile(): CXFile? = memScoped {
|
||||
val fileVar = alloc<CXFileVar>()
|
||||
clang_getFileLocation(this@getContainingFile, fileVar.ptr, null, null, null)
|
||||
fileVar.value
|
||||
}
|
||||
|
||||
@JvmName("getFileContainingCursor")
|
||||
internal fun getContainingFile(cursor: CValue<CXCursor>): CXFile? {
|
||||
return clang_getCursorLocation(cursor).getContainingFile()
|
||||
}
|
||||
|
||||
internal val CXFile.path: String get() = clang_getFileName(this).convertAndDispose()
|
||||
internal val CXFile.canonicalPath: String get() = File(this.path).canonicalPath
|
||||
|
||||
private fun createVfsOverlayFileContents(virtualPathToReal: Map<Path, Path>): ByteArray {
|
||||
val overlay = clang_VirtualFileOverlay_create(0)
|
||||
|
||||
try {
|
||||
fun addFileMapping(realPath: Path, virtualPath: Path) {
|
||||
clang_VirtualFileOverlay_addFileMapping(
|
||||
overlay,
|
||||
virtualPath = virtualPath.toAbsolutePath().toString(),
|
||||
realPath = realPath.toAbsolutePath().toString()
|
||||
)
|
||||
}
|
||||
|
||||
virtualPathToReal.forEach { virtualPath, realPath ->
|
||||
if (Files.isDirectory(realPath)) {
|
||||
realPath.toFile().walkTopDown().forEach {
|
||||
if (!it.isDirectory) {
|
||||
addFileMapping(
|
||||
realPath = it.toPath(),
|
||||
virtualPath = virtualPath.resolve(realPath.relativize(it.toPath()))
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
addFileMapping(realPath = realPath, virtualPath = virtualPath)
|
||||
}
|
||||
}
|
||||
|
||||
memScoped {
|
||||
val bufferVar = alloc<CPointerVar<ByteVar>>().apply { value = null }
|
||||
val bufferSizeVar = alloc<IntVar>()
|
||||
|
||||
val res = clang_VirtualFileOverlay_writeToBuffer(overlay, 0, bufferVar.ptr, bufferSizeVar.ptr)
|
||||
if (res != CXErrorCode.CXError_Success) {
|
||||
// TODO: shall we free the buffer in this case?
|
||||
error(res)
|
||||
}
|
||||
|
||||
return bufferVar.value!!.readBytes(bufferSizeVar.value)
|
||||
}
|
||||
} finally {
|
||||
clang_VirtualFileOverlay_dispose(overlay)
|
||||
}
|
||||
}
|
||||
|
||||
fun createVfsOverlayFile(virtualPathToReal: Map<Path, Path>): Path {
|
||||
val bytes = createVfsOverlayFileContents(virtualPathToReal)
|
||||
|
||||
return Files.createTempFile("konan", ".vfsoverlay").also {
|
||||
Files.write(it, bytes)
|
||||
it.toFile().deleteOnExit()
|
||||
}
|
||||
}
|
||||
|
||||
tailrec fun Type.unwrapTypedefs(): Type = if (this is Typedef) {
|
||||
this.def.aliased.unwrapTypedefs()
|
||||
} else {
|
||||
this
|
||||
}
|
||||
|
||||
fun Type.canonicalIsPointerToChar(): Boolean {
|
||||
val unwrappedType = this.unwrapTypedefs()
|
||||
return unwrappedType is PointerType && unwrappedType.pointeeType.unwrapTypedefs() == CharType
|
||||
}
|
||||
|
||||
internal interface Disposable {
|
||||
fun dispose()
|
||||
}
|
||||
|
||||
internal inline fun <T : Disposable, R> T.use(block: (T) -> R): R = try {
|
||||
block(this)
|
||||
} finally {
|
||||
this.dispose()
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#ifdef __linux__
|
||||
namespace llvm {
|
||||
/**
|
||||
* http://lists.llvm.org/pipermail/llvm-dev/2017-January/109621.html
|
||||
* We can't rebuild llvm, but we can define symbol missed in llvm build.
|
||||
*/
|
||||
int DisableABIBreakingChecks = 1;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,110 @@
|
||||
#if defined(__linux__) || defined(__APPLE__)
|
||||
#include <dlfcn.h>
|
||||
#if defined(__linux__)
|
||||
#include <link.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
|
||||
extern "C" void clang_toggleCrashRecovery(unsigned isEnabled);
|
||||
|
||||
constexpr int signalsToCover[] = {
|
||||
SIGILL, SIGFPE, SIGSEGV, SIGBUS, SIGUSR1, SIGUSR2
|
||||
};
|
||||
|
||||
struct {
|
||||
void* handler;
|
||||
bool isSigaction;
|
||||
} oldSignalHandlers[sizeof(signalsToCover)/sizeof(signalsToCover[0])] = { 0 };
|
||||
|
||||
static int mySigaction(int sig, const struct sigaction *act, struct sigaction * oact) {
|
||||
for (int i = 0; i < sizeof(signalsToCover)/sizeof(signalsToCover[0]); i++) {
|
||||
if (sig == signalsToCover[i]) return 0;
|
||||
}
|
||||
return sigaction(sig, act, oact);
|
||||
}
|
||||
|
||||
static void checkSignalChaining() {
|
||||
struct sigaction oact;
|
||||
clang_toggleCrashRecovery(1);
|
||||
for (int i = 0; i < sizeof(signalsToCover)/sizeof(signalsToCover[0]); i++) {
|
||||
int sig = signalsToCover[i];
|
||||
if (sigaction(sig, nullptr, &oact) != 0) continue;
|
||||
if ((oact.sa_flags & SA_SIGINFO) == 0) {
|
||||
if (oldSignalHandlers[i].isSigaction) {
|
||||
fprintf(stderr, "ERROR: improperly changed signal flag for %d\n", sig);
|
||||
continue;
|
||||
}
|
||||
if (oldSignalHandlers[i].handler != (void*)oact.sa_handler) {
|
||||
fprintf(stderr, "ERROR: improperly changed signal handler for %d\n", sig);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (!oldSignalHandlers[i].isSigaction) {
|
||||
fprintf(stderr, "ERROR: improperly changed signal flag for %d\n", sig);
|
||||
continue;
|
||||
}
|
||||
void* action = (void*)oact.sa_sigaction;
|
||||
if (oldSignalHandlers[i].handler != action) {
|
||||
Dl_info info;
|
||||
const char* soname = "<unknown>";
|
||||
if (dladdr(action, &info) != 0) {
|
||||
soname = info.dli_fname;
|
||||
}
|
||||
fprintf(stderr, "ERROR: changed signal handler for %d from %p to %p: coming from %s\n",
|
||||
sig, oldSignalHandlers[i].handler, action, soname);
|
||||
}
|
||||
}
|
||||
}
|
||||
clang_toggleCrashRecovery(0);
|
||||
}
|
||||
|
||||
__attribute__((constructor))
|
||||
static void initSignalChaining() {
|
||||
void** base = 0;
|
||||
Dl_info info;
|
||||
|
||||
for (int i = 0; i < sizeof(signalsToCover)/sizeof(signalsToCover[0]); i++) {
|
||||
struct sigaction oact;
|
||||
int sig = signalsToCover[i];
|
||||
if (sigaction(signalsToCover[i], nullptr, &oact) == 0) {
|
||||
if ((oact.sa_flags & SA_SIGINFO) == 0) {
|
||||
oldSignalHandlers[i] = {(void*)oact.sa_handler, false};
|
||||
} else {
|
||||
oldSignalHandlers[i] = {(void*)oact.sa_sigaction, true};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (dladdr((void*)&clang_toggleCrashRecovery, &info) == 0) return;
|
||||
base = (void**)info.dli_fbase;
|
||||
|
||||
// Force resolving of lazy symbols.
|
||||
clang_toggleCrashRecovery(1);
|
||||
clang_toggleCrashRecovery(0);
|
||||
|
||||
// And then patch GOT.
|
||||
#if defined(__linux__)
|
||||
{
|
||||
// On Linux we have to be a bit tricky, as there's unmapped gap between code and GOT.
|
||||
struct link_map* linkmap = 0;
|
||||
if (dladdr1((void*)&clang_toggleCrashRecovery, &info, (void**)&linkmap, RTLD_DL_LINKMAP) == 0) return;
|
||||
base = (void**)linkmap->l_ld;
|
||||
}
|
||||
#endif
|
||||
for (int index = 0, patched = 0; patched < 1; index++) {
|
||||
void* value = base[index];
|
||||
if (value == &sigaction) {
|
||||
base[index] = (void*)mySigaction;
|
||||
patched++;
|
||||
}
|
||||
if (value == mySigaction) {
|
||||
patched++;
|
||||
}
|
||||
}
|
||||
|
||||
checkSignalChaining();
|
||||
}
|
||||
|
||||
#endif // defined(__linux__) || defined(__APPLE__)
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.
|
||||
*/
|
||||
|
||||
buildscript {
|
||||
apply from: "$rootDir/kotlin-native/gradle/kotlinGradlePlugin.gradle"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.
|
||||
*/
|
||||
|
||||
konan.libraries.push ({
|
||||
arenas: new Map(),
|
||||
nextArena: 0,
|
||||
Konan_js_allocateArena: function (array) {
|
||||
var index = konan_dependencies.env.nextArena++;
|
||||
konan_dependencies.env.arenas.set(index, array || []);
|
||||
return index;
|
||||
|
||||
},
|
||||
Konan_js_freeArena: function(arenaIndex) {
|
||||
var arena = konan_dependencies.env.arenas.get(arenaIndex);
|
||||
arena.forEach(function(element, index) {
|
||||
arena[index] = null;
|
||||
});
|
||||
konan_dependencies.env.arenas.delete(arenaIndex);
|
||||
},
|
||||
Konan_js_pushIntToArena: function (arenaIndex, value) {
|
||||
var arena = konan_dependencies.env.arenas.get(arenaIndex);
|
||||
arena.push(value);
|
||||
return arena.length - 1;
|
||||
},
|
||||
Konan_js_addObjectToArena: function (arenaIndex, object) {
|
||||
var arena = konan_dependencies.env.arenas.get(arenaIndex);
|
||||
arena.push(object);
|
||||
return arena.length - 1;
|
||||
},
|
||||
Konan_js_wrapLambda: function (functionArenaIndex, index) {
|
||||
return (function () {
|
||||
var functionArena = konan_dependencies.env.arenas.get(functionArenaIndex);
|
||||
|
||||
// convert Arguments to an array
|
||||
// to be provided by launcher.js
|
||||
var argumentArenaIndex = konan_dependencies.env.Konan_js_allocateArena(Array.prototype.slice.call(arguments));
|
||||
|
||||
var resultIndex = instance.exports.Konan_js_runLambda(index, argumentArenaIndex, arguments.length);
|
||||
var result = kotlinObject(argumentArenaIndex, resultIndex);
|
||||
konan_dependencies.env.Konan_js_freeArena(argumentArenaIndex);
|
||||
|
||||
return result;
|
||||
});
|
||||
},
|
||||
Konan_js_getInt: function(arenaIndex, objIndex, propertyNamePtr, propertyNameLength) {
|
||||
// TODO: The toUTF16String() is to be resolved by launcher.js runtime.
|
||||
var property = toUTF16String(propertyNamePtr, propertyNameLength);
|
||||
var value = kotlinObject(arenaIndex, objIndex)[property];
|
||||
return value;
|
||||
},
|
||||
Konan_js_getProperty: function(arenaIndex, objIndex, propertyNamePtr, propertyNameLength) {
|
||||
// TODO: The toUTF16String() is to be resolved by launcher.js runtime.
|
||||
var property = toUTF16String(propertyNamePtr, propertyNameLength);
|
||||
var arena = konan_dependencies.env.arenas.get(arenaIndex);
|
||||
var value = arena[objIndex][property];
|
||||
arena.push(value);
|
||||
return arena.length - 1;
|
||||
},
|
||||
Konan_js_setFunction: function (arena, obj, propertyName, propertyNameLength, func) {
|
||||
var name = toUTF16String(propertyName, propertyNameLength);
|
||||
kotlinObject(arena, obj)[name] = konan_dependencies.env.Konan_js_wrapLambda(arena, func);
|
||||
},
|
||||
|
||||
Konan_js_setString: function (arena, obj, propertyName, propertyNameLength, stringPtr, stringLength) {
|
||||
var name = toUTF16String(propertyName, propertyNameLength);
|
||||
var string = toUTF16String(stringPtr, stringLength);
|
||||
kotlinObject(arena, obj)[name] = string;
|
||||
},
|
||||
});
|
||||
|
||||
// TODO: This is just a shorthand notation.
|
||||
function kotlinObject(arenaIndex, objectIndex) {
|
||||
var arena = konan_dependencies.env.arenas.get(arenaIndex);
|
||||
if (typeof arena == "undefined") {
|
||||
console.log("No arena index " + arenaIndex + "for object" + objectIndex);
|
||||
console.trace()
|
||||
}
|
||||
return arena[objectIndex]
|
||||
}
|
||||
|
||||
function toArena(arenaIndex, object) {
|
||||
return konan_dependencies.env.Konan_js_addObjectToArena(arenaIndex, object);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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 kotlinx.wasm.jsinterop
|
||||
|
||||
import kotlin.native.*
|
||||
import kotlin.native.internal.ExportForCppRuntime
|
||||
import kotlinx.cinterop.*
|
||||
|
||||
typealias Arena = Int
|
||||
typealias Object = Int
|
||||
typealias Pointer = Int
|
||||
|
||||
/**
|
||||
* @Retain annotation is required to preserve functions from internalization and DCE.
|
||||
*/
|
||||
@RetainForTarget("wasm32")
|
||||
@SymbolName("Konan_js_allocateArena")
|
||||
external public fun allocateArena(): Arena
|
||||
|
||||
@RetainForTarget("wasm32")
|
||||
@SymbolName("Konan_js_freeArena")
|
||||
external public fun freeArena(arena: Arena)
|
||||
|
||||
@RetainForTarget("wasm32")
|
||||
@SymbolName("Konan_js_pushIntToArena")
|
||||
external public fun pushIntToArena(arena: Arena, value: Int)
|
||||
|
||||
const val upperWord = 0xffffffff.toLong() shl 32
|
||||
|
||||
@ExportForCppRuntime
|
||||
fun doubleUpper(value: Double): Int =
|
||||
((value.toBits() and upperWord) ushr 32) .toInt()
|
||||
|
||||
@ExportForCppRuntime
|
||||
fun doubleLower(value: Double): Int =
|
||||
(value.toBits() and 0x00000000ffffffff) .toInt()
|
||||
|
||||
@RetainForTarget("wasm32")
|
||||
@SymbolName("ReturnSlot_getDouble")
|
||||
external public fun ReturnSlot_getDouble(): Double
|
||||
|
||||
@RetainForTarget("wasm32")
|
||||
@SymbolName("Kotlin_String_utf16pointer")
|
||||
external public fun stringPointer(message: String): Pointer
|
||||
|
||||
@RetainForTarget("wasm32")
|
||||
@SymbolName("Kotlin_String_utf16length")
|
||||
external public fun stringLengthBytes(message: String): Int
|
||||
|
||||
typealias KtFunction <R> = ((ArrayList<JsValue>)->R)
|
||||
|
||||
fun <R> wrapFunction(func: KtFunction<R>): Int {
|
||||
val ptr: Long = StableRef.create(func).asCPointer().toLong()
|
||||
return ptr.toInt() // TODO: LP64 unsafe.
|
||||
}
|
||||
|
||||
@RetainForTarget("wasm32")
|
||||
@ExportForCppRuntime("Konan_js_runLambda")
|
||||
fun runLambda(pointer: Int, argumentsArena: Arena, argumentsArenaSize: Int): Int {
|
||||
val arguments = arrayListOf<JsValue>()
|
||||
for (i in 0 until argumentsArenaSize) {
|
||||
arguments.add(JsValue(argumentsArena, i));
|
||||
}
|
||||
val previousArena = ArenaManager.currentArena
|
||||
ArenaManager.currentArena = argumentsArena
|
||||
// TODO: LP64 unsafe: wasm32 passes Int, not Long.
|
||||
val func = pointer.toLong().toCPointer<CPointed>()!!.asStableRef<KtFunction<JsValue>>().get()
|
||||
val result = func(arguments)
|
||||
|
||||
ArenaManager.currentArena = previousArena
|
||||
return result.index
|
||||
}
|
||||
|
||||
open class JsValue(val arena: Arena, val index: Object) {
|
||||
fun getInt(property: String): Int {
|
||||
return getInt(ArenaManager.currentArena, index, stringPointer(property), stringLengthBytes(property))
|
||||
}
|
||||
fun getProperty(property: String): JsValue {
|
||||
return JsValue(ArenaManager.currentArena, Konan_js_getProperty(ArenaManager.currentArena, index, stringPointer(property), stringLengthBytes(property)))
|
||||
}
|
||||
}
|
||||
|
||||
open class JsArray(arena: Arena, index: Object): JsValue(arena, index) {
|
||||
constructor(jsValue: JsValue): this(jsValue.arena, jsValue.index)
|
||||
operator fun get(index: Int): JsValue {
|
||||
// TODO: we could pass an integer index to index arrays.
|
||||
return getProperty(index.toString())
|
||||
}
|
||||
val size: Int
|
||||
get() = this.getInt("length")
|
||||
}
|
||||
|
||||
@RetainForTarget("wasm32")
|
||||
@SymbolName("Konan_js_getInt")
|
||||
external public fun getInt(arena: Arena, obj: Object, propertyPtr: Pointer, propertyLen: Int): Int;
|
||||
|
||||
@RetainForTarget("wasm32")
|
||||
@SymbolName("Konan_js_getProperty")
|
||||
external public fun Konan_js_getProperty(arena: Arena, obj: Object, propertyPtr: Pointer, propertyLen: Int): Int;
|
||||
|
||||
@RetainForTarget("wasm32")
|
||||
@SymbolName("Konan_js_setFunction")
|
||||
external public fun setFunction(arena: Arena, obj: Object, propertyName: Pointer, propertyLength: Int , function: Int)
|
||||
|
||||
@RetainForTarget("wasm32")
|
||||
@SymbolName("Konan_js_setString")
|
||||
external public fun setString(arena: Arena, obj: Object, propertyName: Pointer, propertyLength: Int, stringPtr: Pointer, stringLength: Int )
|
||||
|
||||
fun setter(obj: JsValue, property: String, string: String) {
|
||||
setString(obj.arena, obj.index, stringPointer(property), stringLengthBytes(property), stringPointer(string), stringLengthBytes(string))
|
||||
}
|
||||
|
||||
fun setter(obj: JsValue, property: String, lambda: KtFunction<Unit>) {
|
||||
val pointer = wrapFunction(lambda);
|
||||
setFunction(obj.arena, obj.index, stringPointer(property), stringLengthBytes(property), pointer)
|
||||
}
|
||||
|
||||
fun JsValue.setter(property: String, lambda: KtFunction<Unit>) {
|
||||
setter(this, property, lambda)
|
||||
}
|
||||
|
||||
fun JsValue.setter(property: String, string: String) {
|
||||
setter(this, property, string)
|
||||
}
|
||||
|
||||
object ArenaManager {
|
||||
val globalArena = allocateArena()
|
||||
var currentArena = globalArena
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
# Kotlin-native interop
|
||||
|
||||
## Usage
|
||||
|
||||
Create file `../gradle.properties` with contents:
|
||||
|
||||
llvmInstallPath=/path/to/llvm
|
||||
|
||||
Create a Gradle subproject somewhere under `../`, using `../InteropExample` as a template.
|
||||
|
||||
To generate the interop stubs and libraries and build all sources you can run
|
||||
the following command from `../`:
|
||||
|
||||
./gradlew InteropExample:build
|
||||
|
||||
To run the example (if 'application' plugin is enabled):
|
||||
|
||||
./gradlew InteropExample:run
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
import org.jetbrains.gradle.plugins.tools.lib
|
||||
import org.jetbrains.gradle.plugins.tools.solib
|
||||
import org.jetbrains.kotlin.*
|
||||
import org.jetbrains.kotlin.konan.target.HostManager
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
||||
val kotlinVersion = project.bootstrapKotlinVersion
|
||||
plugins {
|
||||
`native`
|
||||
`kotlin`
|
||||
}
|
||||
//apply plugin: 'c'
|
||||
|
||||
|
||||
|
||||
|
||||
native {
|
||||
val isWindows = PlatformInfo.isWindows()
|
||||
val obj = if (isWindows) "obj" else "o"
|
||||
val host = rootProject.project(":kotlin-native").extra["hostName"]
|
||||
val hostLibffiDir = rootProject.project(":kotlin-native").extra["${host}LibffiDir"]
|
||||
val cflags = mutableListOf("-I$hostLibffiDir/include",
|
||||
*platformManager.hostPlatform.clang.hostCompilerArgsForJni)
|
||||
if (!HostManager.hostIsMingw) {
|
||||
cflags += "-fPIC"
|
||||
}
|
||||
suffixes {
|
||||
(".c" to ".$obj") {
|
||||
tool(*platformManager.hostPlatform.clang.clangC("").toTypedArray())
|
||||
flags( *cflags.toTypedArray(), "-c", "-o", ruleOut(), ruleInFirst())
|
||||
}
|
||||
}
|
||||
sourceSet {
|
||||
"callbacks" {
|
||||
dir("src/callbacks/c")
|
||||
}
|
||||
}
|
||||
val objSet = sourceSets["callbacks"]!!.transform(".c" to ".$obj")
|
||||
|
||||
target(solib("callbacks"), objSet) {
|
||||
tool(*platformManager.hostPlatform.clang.clangCXX("").toTypedArray())
|
||||
flags("-shared",
|
||||
"-o",ruleOut(), *ruleInAll(),
|
||||
"-L${project(":kotlin-native:libclangext").buildDir}",
|
||||
"$hostLibffiDir/lib/libffi.a",
|
||||
"-lclangext")
|
||||
}
|
||||
tasks.named(solib("callbacks")).configure {
|
||||
dependsOn(":kotlin-native:libclangext:${lib("clangext")}")
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":kotlin-native:utilities:basic-utils"))
|
||||
implementation(project(":kotlin-stdlib"))
|
||||
implementation(project(":kotlin-reflect"))
|
||||
}
|
||||
|
||||
sourceSets.main.get().java.srcDir("src/jvm/kotlin")
|
||||
|
||||
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
|
||||
kotlinOptions {
|
||||
freeCompilerArgs = listOf("-Xuse-experimental=kotlin.ExperimentalUnsignedTypes",
|
||||
"-Xuse-experimental=kotlin.Experimental",
|
||||
"-Xopt-in=kotlin.RequiresOptIn",
|
||||
"-XXLanguage:+InlineClasses",
|
||||
"-Xskip-prerelease-check")
|
||||
allWarningsAsErrors = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
val nativelibs = project.tasks.create<Copy>("nativelibs") {
|
||||
dependsOn(solib("callbacks"))
|
||||
|
||||
from("$buildDir/")
|
||||
into("$buildDir/nativelibs/")
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <jni.h>
|
||||
#include <ffi.h>
|
||||
|
||||
/*
|
||||
* Class: kotlinx_cinterop_JvmCallbacksKt
|
||||
* Method: ffiTypeVoid
|
||||
* Signature: ()J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_kotlinx_cinterop_JvmCallbacksKt_ffiTypeVoid(JNIEnv *env, jclass cls) {
|
||||
return (jlong) &ffi_type_void;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: kotlinx_cinterop_JvmCallbacksKt
|
||||
* Method: ffiTypeUInt8
|
||||
* Signature: ()J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_kotlinx_cinterop_JvmCallbacksKt_ffiTypeUInt8(JNIEnv *env, jclass cls) {
|
||||
return (jlong) &ffi_type_uint8;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: kotlinx_cinterop_JvmCallbacksKt
|
||||
* Method: ffiTypeSInt8
|
||||
* Signature: ()J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_kotlinx_cinterop_JvmCallbacksKt_ffiTypeSInt8(JNIEnv *env, jclass cls) {
|
||||
return (jlong) &ffi_type_sint8;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: kotlinx_cinterop_JvmCallbacksKt
|
||||
* Method: ffiTypeUInt16
|
||||
* Signature: ()J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_kotlinx_cinterop_JvmCallbacksKt_ffiTypeUInt16(JNIEnv *env, jclass cls) {
|
||||
return (jlong) &ffi_type_uint16;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: kotlinx_cinterop_JvmCallbacksKt
|
||||
* Method: ffiTypeSInt16
|
||||
* Signature: ()J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_kotlinx_cinterop_JvmCallbacksKt_ffiTypeSInt16(JNIEnv *env, jclass cls) {
|
||||
return (jlong) &ffi_type_sint16;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: kotlinx_cinterop_JvmCallbacksKt
|
||||
* Method: ffiTypeUInt32
|
||||
* Signature: ()J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_kotlinx_cinterop_JvmCallbacksKt_ffiTypeUInt32(JNIEnv *env, jclass cls) {
|
||||
return (jlong) &ffi_type_uint32;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: kotlinx_cinterop_JvmCallbacksKt
|
||||
* Method: ffiTypeSInt32
|
||||
* Signature: ()J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_kotlinx_cinterop_JvmCallbacksKt_ffiTypeSInt32(JNIEnv *env, jclass cls) {
|
||||
return (jlong) &ffi_type_sint32;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: kotlinx_cinterop_JvmCallbacksKt
|
||||
* Method: ffiTypeUInt64
|
||||
* Signature: ()J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_kotlinx_cinterop_JvmCallbacksKt_ffiTypeUInt64(JNIEnv *env, jclass cls) {
|
||||
return (jlong) &ffi_type_uint64;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: kotlinx_cinterop_JvmCallbacksKt
|
||||
* Method: ffiTypeSInt64
|
||||
* Signature: ()J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_kotlinx_cinterop_JvmCallbacksKt_ffiTypeSInt64(JNIEnv *env, jclass cls) {
|
||||
return (jlong) &ffi_type_sint64;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: kotlinx_cinterop_JvmCallbacksKt
|
||||
* Method: ffiTypePointer
|
||||
* Signature: ()J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_kotlinx_cinterop_JvmCallbacksKt_ffiTypePointer(JNIEnv *env, jclass cls) {
|
||||
return (jlong) &ffi_type_pointer;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: kotlinx_cinterop_JvmCallbacksKt
|
||||
* Method: ffiTypeStruct0
|
||||
* Signature: (J)J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_kotlinx_cinterop_JvmCallbacksKt_ffiTypeStruct0(JNIEnv *env, jclass cls, jlong elements) {
|
||||
ffi_type* res = malloc(sizeof(ffi_type));
|
||||
if (res != NULL) {
|
||||
res->size = 0;
|
||||
res->alignment = 0;
|
||||
res->elements = (ffi_type**) elements;
|
||||
res->type = FFI_TYPE_STRUCT;
|
||||
}
|
||||
return (jlong) res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: kotlinx_cinterop_JvmCallbacksKt
|
||||
* Method: ffiCreateCif0
|
||||
* Signature: (IJJ)J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_kotlinx_cinterop_JvmCallbacksKt_ffiCreateCif0(JNIEnv *env, jclass cls, jint nArgs, jlong rType, jlong argTypes) {
|
||||
ffi_cif* res = malloc(sizeof(ffi_cif));
|
||||
if (res != NULL) {
|
||||
ffi_status status = ffi_prep_cif(res, FFI_DEFAULT_ABI, nArgs, (ffi_type*)rType, (ffi_type**)argTypes);
|
||||
if (status != FFI_OK) {
|
||||
if (status == FFI_BAD_TYPEDEF) {
|
||||
return -(jlong)1;
|
||||
} else if (status == FFI_BAD_ABI) {
|
||||
return -(jlong)2;
|
||||
} else {
|
||||
return -(jlong)3;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (jlong) res;
|
||||
}
|
||||
|
||||
static JavaVM *vm = NULL;
|
||||
|
||||
// Returns the JNI env which can be used by the caller.
|
||||
// If current thread is not attached to JVM, then it gets attached as daemon.
|
||||
static JNIEnv* getCurrentEnv() {
|
||||
JNIEnv* env;
|
||||
assert(vm != NULL);
|
||||
jint res = (*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_1);
|
||||
if (res != JNI_OK) {
|
||||
assert(res == JNI_EDETACHED);
|
||||
res = (*vm)->AttachCurrentThreadAsDaemon(vm, (void**)&env, NULL);
|
||||
assert(res == JNI_OK);
|
||||
}
|
||||
return env;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm_, void *reserved) {
|
||||
vm = vm_;
|
||||
return JNI_VERSION_1_1;
|
||||
}
|
||||
|
||||
// Checks for pending exception. If there is one, describes it and terminates the process.
|
||||
static void checkException(JNIEnv *env) {
|
||||
if ((*env)->ExceptionCheck(env)) {
|
||||
(*env)->ExceptionDescribe(env);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static void ffi_fun(ffi_cif *cif, void *ret, void **args, void *user_data) {
|
||||
JNIEnv* env = getCurrentEnv();
|
||||
|
||||
static jmethodID acceptFun = NULL;
|
||||
static jclass cls = NULL;
|
||||
if (acceptFun == NULL) {
|
||||
// Note: in some cases [FindClass] below may use a classloader different from the one loaded interop classes,
|
||||
// so stick to JVM-provided class:
|
||||
jclass clsLocal = (*env)->FindClass(env, "java/util/function/LongConsumer");
|
||||
checkException(env);
|
||||
assert(clsLocal != NULL);
|
||||
|
||||
cls = (jclass) (*env)->NewGlobalRef(env, clsLocal);
|
||||
checkException(env);
|
||||
assert(cls != NULL);
|
||||
|
||||
acceptFun = (*env)->GetMethodID(env, cls, "accept", "(J)V");
|
||||
checkException(env);
|
||||
assert(acceptFun != NULL);
|
||||
}
|
||||
|
||||
jlong retAndArgs[2] = { (jlong)ret, (jlong)args }; // Unpacked in [ffiClosureImpl].
|
||||
(*env)->CallVoidMethod(env, (jobject) user_data, acceptFun, (jlong)(intptr_t)&retAndArgs[0]);
|
||||
checkException(env);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: kotlinx_cinterop_JvmCallbacksKt
|
||||
* Method: ffiCreateClosure0
|
||||
* Signature: (JLjava/lang/Object;)J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_kotlinx_cinterop_JvmCallbacksKt_ffiCreateClosure0(JNIEnv *env, jclass cls, jlong ffiCif, jobject userData) {
|
||||
jobject userDataGlobalRef = (*env)->NewGlobalRef(env, userData);
|
||||
if (userDataGlobalRef == NULL) {
|
||||
return (jlong)0;
|
||||
}
|
||||
|
||||
assert(sizeof(jobject) == sizeof(void*)); // TODO: check statically
|
||||
void* userDataPtr = (void*) userDataGlobalRef;
|
||||
|
||||
void* res;
|
||||
ffi_closure *closure = ffi_closure_alloc(sizeof(ffi_closure), &res);
|
||||
if (closure == NULL) {
|
||||
return (jlong)0;
|
||||
}
|
||||
ffi_status status = ffi_prep_closure_loc(closure, (ffi_cif*)ffiCif, ffi_fun, userDataPtr, res);
|
||||
if (status != FFI_OK) {
|
||||
return -(jlong)1;
|
||||
}
|
||||
|
||||
return (jlong) res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: kotlinx_cinterop_JvmCallbacksKt
|
||||
* Method: newGlobalRef
|
||||
* Signature: (Ljava/lang/Object;)J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_kotlinx_cinterop_JvmCallbacksKt_newGlobalRef(JNIEnv *env, jclass cls, jobject obj) {
|
||||
jobject res = (*env)->NewGlobalRef(env, obj);
|
||||
return (jlong) res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: kotlinx_cinterop_JvmCallbacksKt
|
||||
* Method: derefGlobalRef
|
||||
* Signature: (J)Ljava/lang/Object;
|
||||
*/
|
||||
JNIEXPORT jobject JNICALL Java_kotlinx_cinterop_JvmCallbacksKt_derefGlobalRef(JNIEnv *env, jclass cls, jlong ref) {
|
||||
return (jobject) ref;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: kotlinx_cinterop_JvmCallbacksKt
|
||||
* Method: deleteGlobalRef
|
||||
* Signature: (J)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_kotlinx_cinterop_JvmCallbacksKt_deleteGlobalRef(JNIEnv *env, jclass cls, jlong ref) {
|
||||
(*env)->DeleteGlobalRef(env, (jobject) ref);
|
||||
}
|
||||
@@ -0,0 +1,461 @@
|
||||
/*
|
||||
* 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 kotlinx.cinterop
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.function.LongConsumer
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KFunction
|
||||
import kotlin.reflect.KType
|
||||
import kotlin.reflect.full.companionObjectInstance
|
||||
import kotlin.reflect.full.declaredMemberProperties
|
||||
import kotlin.reflect.full.isSubclassOf
|
||||
import kotlin.reflect.jvm.reflect
|
||||
|
||||
internal fun createStablePointer(any: Any): COpaquePointer = newGlobalRef(any).toCPointer()!!
|
||||
|
||||
internal fun disposeStablePointer(pointer: COpaquePointer) = deleteGlobalRef(pointer.toLong())
|
||||
|
||||
@PublishedApi
|
||||
internal fun derefStablePointer(pointer: COpaquePointer): Any = derefGlobalRef(pointer.toLong())
|
||||
|
||||
private fun getFieldCType(type: KType): CType<*> {
|
||||
val classifier = type.classifier
|
||||
if (classifier is KClass<*> && classifier.isSubclassOf(CStructVar::class)) {
|
||||
return getStructCType(classifier)
|
||||
}
|
||||
|
||||
return getArgOrRetValCType(type)
|
||||
}
|
||||
|
||||
private fun getVariableCType(type: KType): CType<*>? {
|
||||
val classifier = type.classifier
|
||||
return when (classifier) {
|
||||
!is KClass<*> -> null
|
||||
ByteVarOf::class -> SInt8
|
||||
ShortVarOf::class -> SInt16
|
||||
IntVarOf::class -> SInt32
|
||||
LongVarOf::class -> SInt64
|
||||
CPointerVarOf::class -> Pointer
|
||||
// TODO: floats, enums.
|
||||
else -> if (classifier.isSubclassOf(CStructVar::class)) {
|
||||
getStructCType(classifier)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val structTypeCache = ConcurrentHashMap<Class<*>, CType<*>>()
|
||||
|
||||
private fun getStructCType(structClass: KClass<*>): CType<*> = structTypeCache.computeIfAbsent(structClass.java) {
|
||||
// Note that struct classes are not supposed to be user-defined,
|
||||
// so they don't require to be checked strictly.
|
||||
|
||||
val annotations = structClass.annotations
|
||||
val cNaturalStruct = annotations.filterIsInstance<CNaturalStruct>().firstOrNull() ?:
|
||||
error("struct ${structClass.simpleName} has custom layout")
|
||||
|
||||
val propertiesByName = structClass.declaredMemberProperties.groupBy { it.name }
|
||||
|
||||
val fields = cNaturalStruct.fieldNames.map {
|
||||
propertiesByName[it]!!.single()
|
||||
}
|
||||
|
||||
val fieldCTypes = mutableListOf<CType<*>>()
|
||||
|
||||
for (field in fields) {
|
||||
val lengthAnnotation = field.annotations.filterIsInstance<CLength>().firstOrNull()
|
||||
if (lengthAnnotation == null) {
|
||||
val fieldType = getFieldCType(field.returnType)
|
||||
fieldCTypes.add(fieldType)
|
||||
} else {
|
||||
assert(field.returnType.classifier == CPointer::class)
|
||||
val length = lengthAnnotation.value
|
||||
if (length != 0) {
|
||||
val pointed = field.returnType.arguments.single().type!!
|
||||
val pointedCType = getVariableCType(pointed) ?: TODO("array element type '$pointed'")
|
||||
|
||||
// Represent array field as repeated element-typed fields:
|
||||
repeat(length) {
|
||||
fieldCTypes.add(pointedCType)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
val structType = structClass.companionObjectInstance as CVariable.Type
|
||||
|
||||
Struct(structType.size, structType.align, fieldCTypes)
|
||||
}
|
||||
|
||||
private fun getStructValueCType(type: KType): CType<*> {
|
||||
val structClass = type.arguments.singleOrNull()?.type?.classifier as? KClass<*> ?:
|
||||
error("'$type' type is incomplete")
|
||||
|
||||
return getStructCType(structClass)
|
||||
}
|
||||
|
||||
private fun getEnumCType(classifier: KClass<*>): CEnumType? {
|
||||
val rawValueType = classifier.declaredMemberProperties.single().returnType
|
||||
|
||||
val rawValueCType = when (rawValueType.classifier) {
|
||||
Byte::class -> SInt8
|
||||
Short::class -> SInt16
|
||||
Int::class -> SInt32
|
||||
Long::class -> SInt64
|
||||
else -> error("'${classifier.simpleName}' has unexpected value type '$rawValueType'")
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return CEnumType(rawValueCType as CType<Any>)
|
||||
}
|
||||
|
||||
private fun getArgOrRetValCType(type: KType): CType<*> {
|
||||
val classifier = type.classifier
|
||||
|
||||
val result = when (classifier) {
|
||||
!is KClass<*> -> null
|
||||
Unit::class -> Void
|
||||
Byte::class -> SInt8
|
||||
Short::class -> SInt16
|
||||
Int::class -> SInt32
|
||||
Long::class -> SInt64
|
||||
CPointer::class -> Pointer
|
||||
// TODO: floats
|
||||
CValue::class -> getStructValueCType(type)
|
||||
else -> if (classifier.isSubclassOf(@Suppress("DEPRECATION") CEnum::class)) {
|
||||
getEnumCType(classifier)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
} ?: error("$type is not supported in callback signature")
|
||||
|
||||
if (type.isMarkedNullable != (classifier == CPointer::class)) {
|
||||
if (type.isMarkedNullable) {
|
||||
error("$type must not be nullable when used in callback signature")
|
||||
} else {
|
||||
error("$type must be nullable when used in callback signature")
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private fun createStaticCFunction(function: Function<*>): CPointer<CFunction<*>> {
|
||||
val errorMessage = "staticCFunction must take an unbound, non-capturing function"
|
||||
|
||||
if (!isStatic(function)) {
|
||||
throw IllegalArgumentException(errorMessage)
|
||||
}
|
||||
|
||||
val kFunction = function as? KFunction<*> ?: function.reflect() ?:
|
||||
throw IllegalArgumentException(errorMessage)
|
||||
|
||||
val returnType = getArgOrRetValCType(kFunction.returnType)
|
||||
val paramTypes = kFunction.parameters.map { getArgOrRetValCType(it.type) }
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return interpretCPointer(createStaticCFunctionImpl(returnType as CType<Any?>, paramTypes, function))!!
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if given function is *static* as defined in [staticCFunction].
|
||||
*/
|
||||
private fun isStatic(function: Function<*>): Boolean {
|
||||
// TODO: revise
|
||||
try {
|
||||
with(function.javaClass.getDeclaredField("INSTANCE")) {
|
||||
if (!java.lang.reflect.Modifier.isStatic(modifiers) || !java.lang.reflect.Modifier.isFinal(modifiers)) {
|
||||
return false
|
||||
}
|
||||
|
||||
isAccessible = true // TODO: undo
|
||||
|
||||
return get(null) == function
|
||||
|
||||
// If the class has static final "INSTANCE" field, and only the value of this field is accepted,
|
||||
// then each class is handled at most once, so these checks prevent memory leaks.
|
||||
}
|
||||
} catch (e: NoSuchFieldException) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
private val createdStaticFunctions = ConcurrentHashMap<Class<*>, CPointer<CFunction<*>>>()
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
internal fun <F : Function<*>> staticCFunctionImpl(function: F) =
|
||||
createdStaticFunctions.computeIfAbsent(function.javaClass) {
|
||||
createStaticCFunction(function)
|
||||
} as CPointer<CFunction<F>>
|
||||
|
||||
private val invokeMethods = (0 .. 22).map { arity ->
|
||||
Class.forName("kotlin.jvm.functions.Function$arity").getMethod("invoke",
|
||||
*Array<Class<*>>(arity) { java.lang.Object::class.java })
|
||||
}
|
||||
|
||||
private fun createStaticCFunctionImpl(
|
||||
returnType: CType<Any?>,
|
||||
paramTypes: List<CType<*>>,
|
||||
function: Function<*>
|
||||
): NativePtr {
|
||||
val ffiCif = ffiCreateCif(returnType.ffiType, paramTypes.map { it.ffiType })
|
||||
|
||||
val arity = paramTypes.size
|
||||
val pt = paramTypes.toTypedArray()
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val impl: FfiClosureImpl = when (arity) {
|
||||
0 -> {
|
||||
val f = function as () -> Any?
|
||||
ffiClosureImpl(returnType) { _ ->
|
||||
f()
|
||||
}
|
||||
}
|
||||
1 -> {
|
||||
val f = function as (Any?) -> Any?
|
||||
ffiClosureImpl(returnType) { args ->
|
||||
f(pt.read(args, 0))
|
||||
}
|
||||
}
|
||||
2 -> {
|
||||
val f = function as (Any?, Any?) -> Any?
|
||||
ffiClosureImpl(returnType) { args ->
|
||||
f(pt.read(args, 0), pt.read(args, 1))
|
||||
}
|
||||
}
|
||||
3 -> {
|
||||
val f = function as (Any?, Any?, Any?) -> Any?
|
||||
ffiClosureImpl(returnType) { args ->
|
||||
f(pt.read(args, 0), pt.read(args, 1), pt.read(args, 2))
|
||||
}
|
||||
}
|
||||
4 -> {
|
||||
val f = function as (Any?, Any?, Any?, Any?) -> Any?
|
||||
ffiClosureImpl(returnType) { args ->
|
||||
f(pt.read(args, 0), pt.read(args, 1), pt.read(args, 2), pt.read(args, 3))
|
||||
}
|
||||
}
|
||||
5 -> {
|
||||
val f = function as (Any?, Any?, Any?, Any?, Any?) -> Any?
|
||||
ffiClosureImpl(returnType) { args ->
|
||||
f(pt.read(args, 0), pt.read(args, 1), pt.read(args, 2), pt.read(args, 3), pt.read(args, 4))
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
val invokeMethod = invokeMethods[arity]
|
||||
ffiClosureImpl(returnType) { args ->
|
||||
val arguments = Array(arity) { pt.read(args, it) }
|
||||
invokeMethod.invoke(function, *arguments)
|
||||
}
|
||||
}
|
||||
}
|
||||
return ffiCreateClosure(ffiCif, impl)
|
||||
}
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
private inline fun Array<CType<*>>.read(args: CArrayPointer<COpaquePointerVar>, index: Int) =
|
||||
this[index].read(args[index].rawValue)
|
||||
|
||||
private inline fun ffiClosureImpl(
|
||||
returnType: CType<Any?>,
|
||||
crossinline invoke: (args: CArrayPointer<COpaquePointerVar>) -> Any?
|
||||
): FfiClosureImpl {
|
||||
// Called through [ffi_fun] when a native function created with [ffiCreateClosure] is invoked.
|
||||
return LongConsumer { retAndArgsRaw ->
|
||||
val retAndArgs = retAndArgsRaw.toCPointer<CPointerVar<*>>()!!
|
||||
|
||||
// Pointer to memory to be filled with return value of the invoked native function:
|
||||
val ret = retAndArgs[0]!!
|
||||
|
||||
// Pointer to array of pointers to arguments passed to the invoked native function:
|
||||
val args = retAndArgs[1]!!.reinterpret<COpaquePointerVar>()
|
||||
|
||||
val result = invoke(args)
|
||||
|
||||
returnType.write(ret.rawValue, result)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes the bridge between Kotlin type `T` and the corresponding C type of a function's parameter or return value.
|
||||
* It is supposed to be constructed using the primitive types (such as [SInt32]), the [Struct] combinator
|
||||
* and the [CEnumType] wrapper.
|
||||
*
|
||||
* This description omits the details that are irrelevant for the ABI.
|
||||
*/
|
||||
private abstract class CType<T> internal constructor(val ffiType: ffi_type) {
|
||||
internal constructor(ffiTypePtr: Long) : this(interpretPointed<ffi_type>(ffiTypePtr))
|
||||
abstract fun read(location: NativePtr): T
|
||||
abstract fun write(location: NativePtr, value: T): Unit
|
||||
}
|
||||
|
||||
private object Void : CType<Any?>(ffiTypeVoid()) {
|
||||
override fun read(location: NativePtr) = throw UnsupportedOperationException()
|
||||
|
||||
override fun write(location: NativePtr, value: Any?) {
|
||||
// nothing to do.
|
||||
}
|
||||
}
|
||||
|
||||
private object SInt8 : CType<Byte>(ffiTypeSInt8()) {
|
||||
override fun read(location: NativePtr) = interpretPointed<ByteVar>(location).value
|
||||
override fun write(location: NativePtr, value: Byte) {
|
||||
interpretPointed<ByteVar>(location).value = value
|
||||
}
|
||||
}
|
||||
|
||||
private object SInt16 : CType<Short>(ffiTypeSInt16()) {
|
||||
override fun read(location: NativePtr) = interpretPointed<ShortVar>(location).value
|
||||
override fun write(location: NativePtr, value: Short) {
|
||||
interpretPointed<ShortVar>(location).value = value
|
||||
}
|
||||
}
|
||||
|
||||
private object SInt32 : CType<Int>(ffiTypeSInt32()) {
|
||||
override fun read(location: NativePtr) = interpretPointed<IntVar>(location).value
|
||||
override fun write(location: NativePtr, value: Int) {
|
||||
interpretPointed<IntVar>(location).value = value
|
||||
}
|
||||
}
|
||||
|
||||
private object SInt64 : CType<Long>(ffiTypeSInt64()) {
|
||||
override fun read(location: NativePtr) = interpretPointed<LongVar>(location).value
|
||||
override fun write(location: NativePtr, value: Long) {
|
||||
interpretPointed<LongVar>(location).value = value
|
||||
}
|
||||
}
|
||||
private object Pointer : CType<CPointer<*>?>(ffiTypePointer()) {
|
||||
override fun read(location: NativePtr) = interpretPointed<CPointerVar<*>>(location).value
|
||||
override fun write(location: NativePtr, value: CPointer<*>?) {
|
||||
interpretPointed<CPointerVar<*>>(location).value = value
|
||||
}
|
||||
}
|
||||
|
||||
private class Struct(val size: Long, val align: Int, elementTypes: List<CType<*>>) : CType<CValue<*>>(
|
||||
ffiTypeStruct(
|
||||
elementTypes.map { it.ffiType }
|
||||
)
|
||||
) {
|
||||
override fun read(location: NativePtr) = interpretPointed<ByteVar>(location).readValue<CStructVar>(size, align)
|
||||
|
||||
override fun write(location: NativePtr, value: CValue<*>) = value.write(location)
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
private class CEnumType(private val rawValueCType: CType<Any>) : CType<CEnum>(rawValueCType.ffiType) {
|
||||
|
||||
override fun read(location: NativePtr): CEnum {
|
||||
TODO("enum-typed callback parameters")
|
||||
}
|
||||
|
||||
override fun write(location: NativePtr, value: CEnum) {
|
||||
rawValueCType.write(location, value.value)
|
||||
}
|
||||
}
|
||||
|
||||
private typealias FfiClosureImpl = LongConsumer
|
||||
private typealias UserData = FfiClosureImpl
|
||||
|
||||
private val topLevelInitializer = loadKonanLibrary("callbacks")
|
||||
|
||||
|
||||
/**
|
||||
* Reference to `ffi_type` struct instance.
|
||||
*/
|
||||
internal class ffi_type(rawPtr: NativePtr) : COpaque(rawPtr)
|
||||
|
||||
/**
|
||||
* Reference to `ffi_cif` struct instance.
|
||||
*/
|
||||
internal class ffi_cif(rawPtr: NativePtr) : COpaque(rawPtr)
|
||||
|
||||
private external fun ffiTypeVoid(): Long
|
||||
private external fun ffiTypeUInt8(): Long
|
||||
private external fun ffiTypeSInt8(): Long
|
||||
private external fun ffiTypeUInt16(): Long
|
||||
private external fun ffiTypeSInt16(): Long
|
||||
private external fun ffiTypeUInt32(): Long
|
||||
private external fun ffiTypeSInt32(): Long
|
||||
private external fun ffiTypeUInt64(): Long
|
||||
private external fun ffiTypeSInt64(): Long
|
||||
private external fun ffiTypePointer(): Long
|
||||
|
||||
private external fun ffiTypeStruct0(elements: Long): Long
|
||||
|
||||
/**
|
||||
* Allocates and initializes `ffi_type` describing the struct.
|
||||
*
|
||||
* @param elements types of the struct elements
|
||||
*/
|
||||
private fun ffiTypeStruct(elementTypes: List<ffi_type>): ffi_type {
|
||||
val elements = nativeHeap.allocArrayOfPointersTo(*elementTypes.toTypedArray(), null)
|
||||
val res = ffiTypeStruct0(elements.rawValue)
|
||||
if (res == 0L) {
|
||||
throw OutOfMemoryError()
|
||||
}
|
||||
return interpretPointed(res)
|
||||
}
|
||||
|
||||
private external fun ffiCreateCif0(nArgs: Int, rType: Long, argTypes: Long): Long
|
||||
|
||||
/**
|
||||
* Creates and prepares an `ffi_cif`.
|
||||
*
|
||||
* @param returnType native function return value type
|
||||
* @param paramTypes native function parameter types
|
||||
*
|
||||
* @return the initialized `ffi_cif`
|
||||
*/
|
||||
private fun ffiCreateCif(returnType: ffi_type, paramTypes: List<ffi_type>): ffi_cif {
|
||||
val nArgs = paramTypes.size
|
||||
val argTypes = nativeHeap.allocArrayOfPointersTo(*paramTypes.toTypedArray(), null)
|
||||
val res = ffiCreateCif0(nArgs, returnType.rawPtr, argTypes.rawValue)
|
||||
|
||||
when (res) {
|
||||
0L -> throw OutOfMemoryError()
|
||||
-1L -> throw Error("FFI_BAD_TYPEDEF")
|
||||
-2L -> throw Error("FFI_BAD_ABI")
|
||||
-3L -> throw Error("libffi error occurred")
|
||||
}
|
||||
|
||||
return interpretPointed(res)
|
||||
}
|
||||
|
||||
private external fun ffiCreateClosure0(ffiCif: Long, userData: Any): Long
|
||||
|
||||
/**
|
||||
* Uses libffi to allocate a native function which will call [impl] when invoked.
|
||||
*
|
||||
* @param ffiCif describes the type of the function to create
|
||||
*/
|
||||
private fun ffiCreateClosure(ffiCif: ffi_cif, impl: FfiClosureImpl): NativePtr {
|
||||
val res = ffiCreateClosure0(ffiCif.rawPtr, userData = impl)
|
||||
|
||||
when (res) {
|
||||
0L -> throw OutOfMemoryError()
|
||||
-1L -> throw Error("libffi error occurred")
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
private external fun newGlobalRef(any: Any): Long
|
||||
private external fun derefGlobalRef(ref: Long): Any
|
||||
private external fun deleteGlobalRef(ref: Long)
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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 kotlinx.cinterop
|
||||
|
||||
import org.jetbrains.kotlin.konan.util.nativeMemoryAllocator
|
||||
import sun.misc.Unsafe
|
||||
|
||||
private val NativePointed.address: Long
|
||||
get() = this.rawPtr
|
||||
|
||||
private enum class DataModel(val pointerSize: Long) {
|
||||
_32BIT(4),
|
||||
_64BIT(8)
|
||||
}
|
||||
|
||||
private val dataModel: DataModel = when (System.getProperty("sun.arch.data.model")) {
|
||||
null -> TODO()
|
||||
"32" -> DataModel._32BIT
|
||||
"64" -> DataModel._64BIT
|
||||
else -> throw IllegalStateException()
|
||||
}
|
||||
|
||||
// Must be only used in interop, contains host pointer size, not target!
|
||||
@PublishedApi
|
||||
internal val pointerSize: Int = dataModel.pointerSize.toInt()
|
||||
|
||||
@PublishedApi
|
||||
internal object nativeMemUtils {
|
||||
fun getByte(mem: NativePointed) = unsafe.getByte(mem.address)
|
||||
fun putByte(mem: NativePointed, value: Byte) = unsafe.putByte(mem.address, value)
|
||||
|
||||
fun getShort(mem: NativePointed) = unsafe.getShort(mem.address)
|
||||
fun putShort(mem: NativePointed, value: Short) = unsafe.putShort(mem.address, value)
|
||||
|
||||
fun getInt(mem: NativePointed) = unsafe.getInt(mem.address)
|
||||
fun putInt(mem: NativePointed, value: Int) = unsafe.putInt(mem.address, value)
|
||||
|
||||
fun getLong(mem: NativePointed) = unsafe.getLong(mem.address)
|
||||
fun putLong(mem: NativePointed, value: Long) = unsafe.putLong(mem.address, value)
|
||||
|
||||
fun getFloat(mem: NativePointed) = unsafe.getFloat(mem.address)
|
||||
fun putFloat(mem: NativePointed, value: Float) = unsafe.putFloat(mem.address, value)
|
||||
|
||||
fun getDouble(mem: NativePointed) = unsafe.getDouble(mem.address)
|
||||
fun putDouble(mem: NativePointed, value: Double) = unsafe.putDouble(mem.address, value)
|
||||
|
||||
fun getNativePtr(mem: NativePointed): NativePtr = when (dataModel) {
|
||||
DataModel._32BIT -> getInt(mem).toLong()
|
||||
DataModel._64BIT -> getLong(mem)
|
||||
}
|
||||
|
||||
fun putNativePtr(mem: NativePointed, value: NativePtr) = when (dataModel) {
|
||||
DataModel._32BIT -> putInt(mem, value.toInt())
|
||||
DataModel._64BIT -> putLong(mem, value)
|
||||
}
|
||||
|
||||
fun getByteArray(source: NativePointed, dest: ByteArray, length: Int) {
|
||||
unsafe.copyMemory(null, source.address, dest, byteArrayBaseOffset, length.toLong())
|
||||
}
|
||||
|
||||
fun putByteArray(source: ByteArray, dest: NativePointed, length: Int) {
|
||||
unsafe.copyMemory(source, byteArrayBaseOffset, null, dest.address, length.toLong())
|
||||
}
|
||||
|
||||
fun getCharArray(source: NativePointed, dest: CharArray, length: Int) {
|
||||
unsafe.copyMemory(null, source.address, dest, charArrayBaseOffset, length.toLong() * 2)
|
||||
}
|
||||
|
||||
fun putCharArray(source: CharArray, dest: NativePointed, length: Int) {
|
||||
unsafe.copyMemory(source, charArrayBaseOffset, null, dest.address, length.toLong() * 2)
|
||||
}
|
||||
|
||||
fun zeroMemory(dest: NativePointed, length: Int): Unit =
|
||||
unsafe.setMemory(dest.address, length.toLong(), 0)
|
||||
|
||||
fun copyMemory(dest: NativePointed, length: Int, src: NativePointed) =
|
||||
unsafe.copyMemory(src.address, dest.address, length.toLong())
|
||||
|
||||
|
||||
@Suppress("NON_PUBLIC_CALL_FROM_PUBLIC_INLINE")
|
||||
inline fun <reified T> allocateInstance(): T {
|
||||
return unsafe.allocateInstance(T::class.java) as T
|
||||
}
|
||||
|
||||
internal fun allocRaw(size: Long, align: Int): NativePtr {
|
||||
val address = unsafe.allocateMemory(size)
|
||||
if (address % align != 0L) TODO(align.toString())
|
||||
return address
|
||||
}
|
||||
|
||||
internal fun freeRaw(mem: NativePtr) {
|
||||
unsafe.freeMemory(mem)
|
||||
}
|
||||
|
||||
fun alloc(size: Long, align: Int) = interpretOpaquePointed(nativeMemoryAllocator.alloc(size, align))
|
||||
|
||||
fun free(mem: NativePtr) = nativeMemoryAllocator.free(mem)
|
||||
|
||||
private val unsafe = with(Unsafe::class.java.getDeclaredField("theUnsafe")) {
|
||||
isAccessible = true
|
||||
return@with this.get(null) as Unsafe
|
||||
}
|
||||
|
||||
private val byteArrayBaseOffset = unsafe.arrayBaseOffset(ByteArray::class.java).toLong()
|
||||
private val charArrayBaseOffset = unsafe.arrayBaseOffset(CharArray::class.java).toLong()
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* 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 kotlinx.cinterop
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import kotlin.reflect.full.companionObjectInstance
|
||||
|
||||
typealias NativePtr = Long
|
||||
internal typealias NonNullNativePtr = NativePtr
|
||||
@PublishedApi internal fun NonNullNativePtr.toNativePtr() = this
|
||||
internal fun NativePtr.toNonNull(): NonNullNativePtr = this
|
||||
|
||||
public val nativeNullPtr: NativePtr = 0L
|
||||
|
||||
// TODO: the functions below should eventually be intrinsified
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
private val typeOfCache = ConcurrentHashMap<Class<*>, CVariable.Type>()
|
||||
|
||||
@Deprecated("Use sizeOf<T>() or alignOf<T>() instead.")
|
||||
@Suppress("NON_PUBLIC_CALL_FROM_PUBLIC_INLINE")
|
||||
inline fun <reified T : CVariable> typeOf() =
|
||||
@Suppress("DEPRECATION")
|
||||
typeOfCache.computeIfAbsent(T::class.java) { T::class.companionObjectInstance as CVariable.Type }
|
||||
|
||||
/**
|
||||
* Returns interpretation of entity with given pointer, or `null` if it is null.
|
||||
*
|
||||
* @param T must not be abstract
|
||||
*/
|
||||
@Suppress("NON_PUBLIC_CALL_FROM_PUBLIC_INLINE")
|
||||
inline fun <reified T : NativePointed> interpretNullablePointed(ptr: NativePtr): T? {
|
||||
if (ptr == nativeNullPtr) {
|
||||
return null
|
||||
} else {
|
||||
val result = nativeMemUtils.allocateInstance<T>()
|
||||
result.rawPtr = ptr
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a [CPointer] from the raw pointer of [NativePtr].
|
||||
*
|
||||
* @return a [CPointer] representation, or `null` if the [rawValue] represents native `nullptr`.
|
||||
*/
|
||||
fun <T : CPointed> interpretCPointer(rawValue: NativePtr) =
|
||||
if (rawValue == nativeNullPtr) {
|
||||
null
|
||||
} else {
|
||||
CPointer<T>(rawValue)
|
||||
}
|
||||
|
||||
internal fun CPointer<*>.cPointerToString() = "CPointer(raw=0x%x)".format(rawValue)
|
||||
|
||||
@Target(AnnotationTarget.PROPERTY)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class CLength(val value: Int)
|
||||
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class CNaturalStruct(vararg val fieldNames: String)
|
||||
|
||||
fun <R> staticCFunction(function: () -> R): CPointer<CFunction<() -> R>> =
|
||||
staticCFunctionImpl(function)
|
||||
|
||||
fun <P1, R> staticCFunction(function: (P1) -> R): CPointer<CFunction<(P1) -> R>> =
|
||||
staticCFunctionImpl(function)
|
||||
|
||||
fun <P1, P2, R> staticCFunction(function: (P1, P2) -> R): CPointer<CFunction<(P1, P2) -> R>> =
|
||||
staticCFunctionImpl(function)
|
||||
|
||||
fun <P1, P2, P3, R> staticCFunction(function: (P1, P2, P3) -> R): CPointer<CFunction<(P1, P2, P3) -> R>> =
|
||||
staticCFunctionImpl(function)
|
||||
|
||||
fun <P1, P2, P3, P4, R> staticCFunction(function: (P1, P2, P3, P4) -> R): CPointer<CFunction<(P1, P2, P3, P4) -> R>> =
|
||||
staticCFunctionImpl(function)
|
||||
|
||||
fun <P1, P2, P3, P4, P5, R> staticCFunction(function: (P1, P2, P3, P4, P5) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5) -> R>> =
|
||||
staticCFunctionImpl(function)
|
||||
|
||||
fun <P1, P2, P3, P4, P5, P6, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6) -> R>> =
|
||||
staticCFunctionImpl(function)
|
||||
|
||||
fun <P1, P2, P3, P4, P5, P6, P7, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7) -> R>> =
|
||||
staticCFunctionImpl(function)
|
||||
|
||||
fun <P1, P2, P3, P4, P5, P6, P7, P8, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8) -> R>> =
|
||||
staticCFunctionImpl(function)
|
||||
|
||||
fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9) -> R>> =
|
||||
staticCFunctionImpl(function)
|
||||
|
||||
fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) -> R>> =
|
||||
staticCFunctionImpl(function)
|
||||
|
||||
fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11) -> R>> =
|
||||
staticCFunctionImpl(function)
|
||||
|
||||
fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) -> R>> =
|
||||
staticCFunctionImpl(function)
|
||||
|
||||
fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13) -> R>> =
|
||||
staticCFunctionImpl(function)
|
||||
|
||||
fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14) -> R>> =
|
||||
staticCFunctionImpl(function)
|
||||
|
||||
fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15) -> R>> =
|
||||
staticCFunctionImpl(function)
|
||||
|
||||
fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16) -> R>> =
|
||||
staticCFunctionImpl(function)
|
||||
|
||||
fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17) -> R>> =
|
||||
staticCFunctionImpl(function)
|
||||
|
||||
fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18) -> R>> =
|
||||
staticCFunctionImpl(function)
|
||||
|
||||
fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19) -> R>> =
|
||||
staticCFunctionImpl(function)
|
||||
|
||||
fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20) -> R>> =
|
||||
staticCFunctionImpl(function)
|
||||
|
||||
fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21) -> R>> =
|
||||
staticCFunctionImpl(function)
|
||||
|
||||
fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, R> staticCFunction(function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) -> R>> =
|
||||
staticCFunctionImpl(function)
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 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 kotlinx.cinterop
|
||||
|
||||
import org.jetbrains.kotlin.konan.util.KonanHomeProvider
|
||||
import java.io.File
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Paths
|
||||
|
||||
private fun decodeFromUtf8(bytes: ByteArray) = String(bytes)
|
||||
internal fun encodeToUtf8(str: String) = str.toByteArray()
|
||||
|
||||
internal fun CPointer<ByteVar>.toKStringFromUtf8Impl(): String {
|
||||
val nativeBytes = this
|
||||
|
||||
var length = 0
|
||||
while (nativeBytes[length] != 0.toByte()) {
|
||||
++length
|
||||
}
|
||||
|
||||
val bytes = ByteArray(length)
|
||||
nativeMemUtils.getByteArray(nativeBytes.pointed, bytes, length)
|
||||
return decodeFromUtf8(bytes)
|
||||
}
|
||||
|
||||
fun bitsToFloat(bits: Int): Float = java.lang.Float.intBitsToFloat(bits)
|
||||
fun bitsToDouble(bits: Long): Double = java.lang.Double.longBitsToDouble(bits)
|
||||
|
||||
// TODO: the functions below should eventually be intrinsified
|
||||
|
||||
inline fun <reified R : Number> Byte.signExtend(): R = when (R::class.java) {
|
||||
java.lang.Byte::class.java -> this.toByte() as R
|
||||
java.lang.Short::class.java -> this.toShort() as R
|
||||
java.lang.Integer::class.java -> this.toInt() as R
|
||||
java.lang.Long::class.java -> this.toLong() as R
|
||||
else -> this.invalidSignExtension()
|
||||
}
|
||||
|
||||
inline fun <reified R : Number> Short.signExtend(): R = when (R::class.java) {
|
||||
java.lang.Short::class.java -> this.toShort() as R
|
||||
java.lang.Integer::class.java -> this.toInt() as R
|
||||
java.lang.Long::class.java -> this.toLong() as R
|
||||
else -> this.invalidSignExtension()
|
||||
}
|
||||
|
||||
inline fun <reified R : Number> Int.signExtend(): R = when (R::class.java) {
|
||||
java.lang.Integer::class.java -> this.toInt() as R
|
||||
java.lang.Long::class.java -> this.toLong() as R
|
||||
else -> this.invalidSignExtension()
|
||||
}
|
||||
|
||||
inline fun <reified R : Number> Long.signExtend(): R = when (R::class.java) {
|
||||
java.lang.Long::class.java -> this.toLong() as R
|
||||
else -> this.invalidSignExtension()
|
||||
}
|
||||
|
||||
inline fun <reified R : Number> Number.invalidSignExtension(): R {
|
||||
throw Error("unable to sign extend ${this.javaClass.simpleName} \"${this}\" to ${R::class.java.simpleName}")
|
||||
}
|
||||
|
||||
inline fun <reified R : Number> Byte.narrow(): R = when (R::class.java) {
|
||||
java.lang.Byte::class.java -> this.toByte() as R
|
||||
else -> this.invalidNarrowing()
|
||||
}
|
||||
|
||||
inline fun <reified R : Number> Short.narrow(): R = when (R::class.java) {
|
||||
java.lang.Byte::class.java -> this.toByte() as R
|
||||
java.lang.Short::class.java -> this.toShort() as R
|
||||
else -> this.invalidNarrowing()
|
||||
}
|
||||
|
||||
inline fun <reified R : Number> Int.narrow(): R = when (R::class.java) {
|
||||
java.lang.Byte::class.java -> this.toByte() as R
|
||||
java.lang.Short::class.java -> this.toShort() as R
|
||||
java.lang.Integer::class.java -> this.toInt() as R
|
||||
else -> this.invalidNarrowing()
|
||||
}
|
||||
|
||||
inline fun <reified R : Number> Long.narrow(): R = when (R::class.java) {
|
||||
java.lang.Byte::class.java -> this.toByte() as R
|
||||
java.lang.Short::class.java -> this.toShort() as R
|
||||
java.lang.Integer::class.java -> this.toInt() as R
|
||||
java.lang.Long::class.java -> this.toLong() as R
|
||||
else -> this.invalidNarrowing()
|
||||
}
|
||||
|
||||
inline fun <reified R : Number> Number.invalidNarrowing(): R {
|
||||
throw Error("unable to narrow ${this.javaClass.simpleName} \"${this}\" to ${R::class.java.simpleName}")
|
||||
}
|
||||
|
||||
fun loadKonanLibrary(name: String) {
|
||||
try {
|
||||
System.loadLibrary(name)
|
||||
} catch (e: UnsatisfiedLinkError) {
|
||||
val fullLibraryName = System.mapLibraryName(name)
|
||||
val dir = "${KonanHomeProvider.determineKonanHome()}/konan/nativelib"
|
||||
try {
|
||||
System.load("$dir/$fullLibraryName")
|
||||
} catch (e: UnsatisfiedLinkError) {
|
||||
val tempDir = Files.createTempDirectory(Paths.get(dir), null).toAbsolutePath().toString()
|
||||
Files.createLink(Paths.get(tempDir, fullLibraryName), Paths.get(dir, fullLibraryName))
|
||||
// TODO: Does not work on Windows. May be use FILE_FLAG_DELETE_ON_CLOSE?
|
||||
File(tempDir).deleteOnExit()
|
||||
File("$tempDir/$fullLibraryName").deleteOnExit()
|
||||
System.load("$tempDir/$fullLibraryName")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,316 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@file:Suppress("FINAL_UPPER_BOUND", "NOTHING_TO_INLINE")
|
||||
|
||||
package kotlinx.cinterop
|
||||
|
||||
@JvmName("plus\$Byte")
|
||||
inline operator fun <T : ByteVarOf<*>> CPointer<T>?.plus(index: Long): CPointer<T>? =
|
||||
interpretCPointer(this.rawValue + index * 1)
|
||||
|
||||
@JvmName("plus\$Byte")
|
||||
inline operator fun <T : ByteVarOf<*>> CPointer<T>?.plus(index: Int): CPointer<T>? =
|
||||
this + index.toLong()
|
||||
|
||||
@JvmName("get\$Byte")
|
||||
inline operator fun <T : Byte> CPointer<ByteVarOf<T>>.get(index: Int): T =
|
||||
(this + index)!!.pointed.value
|
||||
|
||||
@JvmName("set\$Byte")
|
||||
inline operator fun <T : Byte> CPointer<ByteVarOf<T>>.set(index: Int, value: T) {
|
||||
(this + index)!!.pointed.value = value
|
||||
}
|
||||
|
||||
@JvmName("get\$Byte")
|
||||
inline operator fun <T : Byte> CPointer<ByteVarOf<T>>.get(index: Long): T =
|
||||
(this + index)!!.pointed.value
|
||||
|
||||
@JvmName("set\$Byte")
|
||||
inline operator fun <T : Byte> CPointer<ByteVarOf<T>>.set(index: Long, value: T) {
|
||||
(this + index)!!.pointed.value = value
|
||||
}
|
||||
|
||||
@JvmName("plus\$Short")
|
||||
inline operator fun <T : ShortVarOf<*>> CPointer<T>?.plus(index: Long): CPointer<T>? =
|
||||
interpretCPointer(this.rawValue + index * 2)
|
||||
|
||||
@JvmName("plus\$Short")
|
||||
inline operator fun <T : ShortVarOf<*>> CPointer<T>?.plus(index: Int): CPointer<T>? =
|
||||
this + index.toLong()
|
||||
|
||||
@JvmName("get\$Short")
|
||||
inline operator fun <T : Short> CPointer<ShortVarOf<T>>.get(index: Int): T =
|
||||
(this + index)!!.pointed.value
|
||||
|
||||
@JvmName("set\$Short")
|
||||
inline operator fun <T : Short> CPointer<ShortVarOf<T>>.set(index: Int, value: T) {
|
||||
(this + index)!!.pointed.value = value
|
||||
}
|
||||
|
||||
@JvmName("get\$Short")
|
||||
inline operator fun <T : Short> CPointer<ShortVarOf<T>>.get(index: Long): T =
|
||||
(this + index)!!.pointed.value
|
||||
|
||||
@JvmName("set\$Short")
|
||||
inline operator fun <T : Short> CPointer<ShortVarOf<T>>.set(index: Long, value: T) {
|
||||
(this + index)!!.pointed.value = value
|
||||
}
|
||||
|
||||
@JvmName("plus\$Int")
|
||||
inline operator fun <T : IntVarOf<*>> CPointer<T>?.plus(index: Long): CPointer<T>? =
|
||||
interpretCPointer(this.rawValue + index * 4)
|
||||
|
||||
@JvmName("plus\$Int")
|
||||
inline operator fun <T : IntVarOf<*>> CPointer<T>?.plus(index: Int): CPointer<T>? =
|
||||
this + index.toLong()
|
||||
|
||||
@JvmName("get\$Int")
|
||||
inline operator fun <T : Int> CPointer<IntVarOf<T>>.get(index: Int): T =
|
||||
(this + index)!!.pointed.value
|
||||
|
||||
@JvmName("set\$Int")
|
||||
inline operator fun <T : Int> CPointer<IntVarOf<T>>.set(index: Int, value: T) {
|
||||
(this + index)!!.pointed.value = value
|
||||
}
|
||||
|
||||
@JvmName("get\$Int")
|
||||
inline operator fun <T : Int> CPointer<IntVarOf<T>>.get(index: Long): T =
|
||||
(this + index)!!.pointed.value
|
||||
|
||||
@JvmName("set\$Int")
|
||||
inline operator fun <T : Int> CPointer<IntVarOf<T>>.set(index: Long, value: T) {
|
||||
(this + index)!!.pointed.value = value
|
||||
}
|
||||
|
||||
@JvmName("plus\$Long")
|
||||
inline operator fun <T : LongVarOf<*>> CPointer<T>?.plus(index: Long): CPointer<T>? =
|
||||
interpretCPointer(this.rawValue + index * 8)
|
||||
|
||||
@JvmName("plus\$Long")
|
||||
inline operator fun <T : LongVarOf<*>> CPointer<T>?.plus(index: Int): CPointer<T>? =
|
||||
this + index.toLong()
|
||||
|
||||
@JvmName("get\$Long")
|
||||
inline operator fun <T : Long> CPointer<LongVarOf<T>>.get(index: Int): T =
|
||||
(this + index)!!.pointed.value
|
||||
|
||||
@JvmName("set\$Long")
|
||||
inline operator fun <T : Long> CPointer<LongVarOf<T>>.set(index: Int, value: T) {
|
||||
(this + index)!!.pointed.value = value
|
||||
}
|
||||
|
||||
@JvmName("get\$Long")
|
||||
inline operator fun <T : Long> CPointer<LongVarOf<T>>.get(index: Long): T =
|
||||
(this + index)!!.pointed.value
|
||||
|
||||
@JvmName("set\$Long")
|
||||
inline operator fun <T : Long> CPointer<LongVarOf<T>>.set(index: Long, value: T) {
|
||||
(this + index)!!.pointed.value = value
|
||||
}
|
||||
|
||||
@JvmName("plus\$UByte")
|
||||
inline operator fun <T : UByteVarOf<*>> CPointer<T>?.plus(index: Long): CPointer<T>? =
|
||||
interpretCPointer(this.rawValue + index * 1)
|
||||
|
||||
@JvmName("plus\$UByte")
|
||||
inline operator fun <T : UByteVarOf<*>> CPointer<T>?.plus(index: Int): CPointer<T>? =
|
||||
this + index.toLong()
|
||||
|
||||
@JvmName("get\$UByte")
|
||||
inline operator fun <T : UByte> CPointer<UByteVarOf<T>>.get(index: Int): T =
|
||||
(this + index)!!.pointed.value
|
||||
|
||||
inline operator fun <T : UByte> CPointer<UByteVarOf<T>>.set(index: Int, value: T) {
|
||||
(this + index)!!.pointed.value = value
|
||||
}
|
||||
|
||||
inline operator fun <T : UByte> CPointer<UByteVarOf<T>>.get(index: Long): T =
|
||||
(this + index)!!.pointed.value
|
||||
|
||||
inline operator fun <T : UByte> CPointer<UByteVarOf<T>>.set(index: Long, value: T) {
|
||||
(this + index)!!.pointed.value = value
|
||||
}
|
||||
|
||||
@JvmName("plus\$UShort")
|
||||
inline operator fun <T : UShortVarOf<*>> CPointer<T>?.plus(index: Long): CPointer<T>? =
|
||||
interpretCPointer(this.rawValue + index * 2)
|
||||
|
||||
@JvmName("plus\$UShort")
|
||||
inline operator fun <T : UShortVarOf<*>> CPointer<T>?.plus(index: Int): CPointer<T>? =
|
||||
this + index.toLong()
|
||||
|
||||
@JvmName("get\$UShort")
|
||||
inline operator fun <T : UShort> CPointer<UShortVarOf<T>>.get(index: Int): T =
|
||||
(this + index)!!.pointed.value
|
||||
|
||||
inline operator fun <T : UShort> CPointer<UShortVarOf<T>>.set(index: Int, value: T) {
|
||||
(this + index)!!.pointed.value = value
|
||||
}
|
||||
|
||||
@JvmName("get\$UShort")
|
||||
inline operator fun <T : UShort> CPointer<UShortVarOf<T>>.get(index: Long): T =
|
||||
(this + index)!!.pointed.value
|
||||
|
||||
inline operator fun <T : UShort> CPointer<UShortVarOf<T>>.set(index: Long, value: T) {
|
||||
(this + index)!!.pointed.value = value
|
||||
}
|
||||
|
||||
@JvmName("plus\$UInt")
|
||||
inline operator fun <T : UIntVarOf<*>> CPointer<T>?.plus(index: Long): CPointer<T>? =
|
||||
interpretCPointer(this.rawValue + index * 4)
|
||||
|
||||
@JvmName("plus\$UInt")
|
||||
inline operator fun <T : UIntVarOf<*>> CPointer<T>?.plus(index: Int): CPointer<T>? =
|
||||
this + index.toLong()
|
||||
|
||||
@JvmName("get\$UInt")
|
||||
inline operator fun <T : UInt> CPointer<UIntVarOf<T>>.get(index: Int): T =
|
||||
(this + index)!!.pointed.value
|
||||
|
||||
inline operator fun <T : UInt> CPointer<UIntVarOf<T>>.set(index: Int, value: T) {
|
||||
(this + index)!!.pointed.value = value
|
||||
}
|
||||
|
||||
@JvmName("get\$UInt")
|
||||
inline operator fun <T : UInt> CPointer<UIntVarOf<T>>.get(index: Long): T =
|
||||
(this + index)!!.pointed.value
|
||||
|
||||
inline operator fun <T : UInt> CPointer<UIntVarOf<T>>.set(index: Long, value: T) {
|
||||
(this + index)!!.pointed.value = value
|
||||
}
|
||||
|
||||
@JvmName("plus\$ULong")
|
||||
inline operator fun <T : ULongVarOf<*>> CPointer<T>?.plus(index: Long): CPointer<T>? =
|
||||
interpretCPointer(this.rawValue + index * 8)
|
||||
|
||||
@JvmName("plus\$ULong")
|
||||
inline operator fun <T : ULongVarOf<*>> CPointer<T>?.plus(index: Int): CPointer<T>? =
|
||||
this + index.toLong()
|
||||
|
||||
@JvmName("get\$ULong")
|
||||
inline operator fun <T : ULong> CPointer<ULongVarOf<T>>.get(index: Int): T =
|
||||
(this + index)!!.pointed.value
|
||||
|
||||
inline operator fun <T : ULong> CPointer<ULongVarOf<T>>.set(index: Int, value: T) {
|
||||
(this + index)!!.pointed.value = value
|
||||
}
|
||||
|
||||
@JvmName("get\$ULong")
|
||||
inline operator fun <T : ULong> CPointer<ULongVarOf<T>>.get(index: Long): T =
|
||||
(this + index)!!.pointed.value
|
||||
|
||||
inline operator fun <T : ULong> CPointer<ULongVarOf<T>>.set(index: Long, value: T) {
|
||||
(this + index)!!.pointed.value = value
|
||||
}
|
||||
|
||||
@JvmName("plus\$Float")
|
||||
inline operator fun <T : FloatVarOf<*>> CPointer<T>?.plus(index: Long): CPointer<T>? =
|
||||
interpretCPointer(this.rawValue + index * 4)
|
||||
|
||||
@JvmName("plus\$Float")
|
||||
inline operator fun <T : FloatVarOf<*>> CPointer<T>?.plus(index: Int): CPointer<T>? =
|
||||
this + index.toLong()
|
||||
|
||||
@JvmName("get\$Float")
|
||||
inline operator fun <T : Float> CPointer<FloatVarOf<T>>.get(index: Int): T =
|
||||
(this + index)!!.pointed.value
|
||||
|
||||
@JvmName("set\$Float")
|
||||
inline operator fun <T : Float> CPointer<FloatVarOf<T>>.set(index: Int, value: T) {
|
||||
(this + index)!!.pointed.value = value
|
||||
}
|
||||
|
||||
@JvmName("get\$Float")
|
||||
inline operator fun <T : Float> CPointer<FloatVarOf<T>>.get(index: Long): T =
|
||||
(this + index)!!.pointed.value
|
||||
|
||||
@JvmName("set\$Float")
|
||||
inline operator fun <T : Float> CPointer<FloatVarOf<T>>.set(index: Long, value: T) {
|
||||
(this + index)!!.pointed.value = value
|
||||
}
|
||||
|
||||
@JvmName("plus\$Double")
|
||||
inline operator fun <T : DoubleVarOf<*>> CPointer<T>?.plus(index: Long): CPointer<T>? =
|
||||
interpretCPointer(this.rawValue + index * 8)
|
||||
|
||||
@JvmName("plus\$Double")
|
||||
inline operator fun <T : DoubleVarOf<*>> CPointer<T>?.plus(index: Int): CPointer<T>? =
|
||||
this + index.toLong()
|
||||
|
||||
@JvmName("get\$Double")
|
||||
inline operator fun <T : Double> CPointer<DoubleVarOf<T>>.get(index: Int): T =
|
||||
(this + index)!!.pointed.value
|
||||
|
||||
@JvmName("set\$Double")
|
||||
inline operator fun <T : Double> CPointer<DoubleVarOf<T>>.set(index: Int, value: T) {
|
||||
(this + index)!!.pointed.value = value
|
||||
}
|
||||
|
||||
@JvmName("get\$Double")
|
||||
inline operator fun <T : Double> CPointer<DoubleVarOf<T>>.get(index: Long): T =
|
||||
(this + index)!!.pointed.value
|
||||
|
||||
@JvmName("set\$Double")
|
||||
inline operator fun <T : Double> CPointer<DoubleVarOf<T>>.set(index: Long, value: T) {
|
||||
(this + index)!!.pointed.value = value
|
||||
}
|
||||
|
||||
/* Generated by:
|
||||
|
||||
#!/bin/bash
|
||||
|
||||
function gen {
|
||||
echo "@JvmName(\"plus\\\$$1\")"
|
||||
echo "inline operator fun <T : ${1}VarOf<*>> CPointer<T>?.plus(index: Long): CPointer<T>? ="
|
||||
echo " interpretCPointer(this.rawValue + index * ${2})"
|
||||
echo
|
||||
echo "@JvmName(\"plus\\\$$1\")"
|
||||
echo "inline operator fun <T : ${1}VarOf<*>> CPointer<T>?.plus(index: Int): CPointer<T>? ="
|
||||
echo " this + index.toLong()"
|
||||
echo
|
||||
echo "@JvmName(\"get\\\$$1\")"
|
||||
echo "inline operator fun <T : $1> CPointer<${1}VarOf<T>>.get(index: Int): T ="
|
||||
echo " (this + index)!!.pointed.value"
|
||||
echo
|
||||
echo "@JvmName(\"set\\\$$1\")"
|
||||
echo "inline operator fun <T : $1> CPointer<${1}VarOf<T>>.set(index: Int, value: T) {"
|
||||
echo " (this + index)!!.pointed.value = value"
|
||||
echo '}'
|
||||
echo
|
||||
echo "@JvmName(\"get\\\$$1\")"
|
||||
echo "inline operator fun <T : $1> CPointer<${1}VarOf<T>>.get(index: Long): T ="
|
||||
echo " (this + index)!!.pointed.value"
|
||||
echo
|
||||
echo "@JvmName(\"set\\\$$1\")"
|
||||
echo "inline operator fun <T : $1> CPointer<${1}VarOf<T>>.set(index: Long, value: T) {"
|
||||
echo " (this + index)!!.pointed.value = value"
|
||||
echo '}'
|
||||
echo
|
||||
}
|
||||
|
||||
gen Byte 1
|
||||
gen Short 2
|
||||
gen Int 4
|
||||
gen Long 8
|
||||
gen UByte 1
|
||||
gen UShort 2
|
||||
gen UInt 4
|
||||
gen ULong 8
|
||||
gen Float 4
|
||||
gen Double 8
|
||||
|
||||
*/
|
||||
@@ -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 kotlinx.cinterop
|
||||
|
||||
@Deprecated("Use StableRef<T> instead", ReplaceWith("StableRef<T>"), DeprecationLevel.ERROR)
|
||||
typealias StableObjPtr = StableRef<*>
|
||||
|
||||
/**
|
||||
* This class provides a way to create a stable handle to any Kotlin object.
|
||||
* After [converting to CPointer][asCPointer] it can be safely passed to native code e.g. to be received
|
||||
* in a Kotlin callback.
|
||||
*
|
||||
* Any [StableRef] should be manually [disposed][dispose]
|
||||
*/
|
||||
@Suppress("NON_PUBLIC_PRIMARY_CONSTRUCTOR_OF_INLINE_CLASS")
|
||||
public inline class StableRef<out T : Any> @PublishedApi internal constructor(
|
||||
private val stablePtr: COpaquePointer
|
||||
) {
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* Creates a handle for given object.
|
||||
*/
|
||||
fun <T : Any> create(any: T) = StableRef<T>(createStablePointer(any))
|
||||
|
||||
/**
|
||||
* Creates [StableRef] from given raw value.
|
||||
*
|
||||
* @param value must be a [value] of some [StableRef]
|
||||
*/
|
||||
@Deprecated("Use CPointer<*>.asStableRef<T>() instead", ReplaceWith("ptr.asStableRef<T>()"),
|
||||
DeprecationLevel.ERROR)
|
||||
fun fromValue(value: COpaquePointer) = value.asStableRef<Any>()
|
||||
}
|
||||
@Deprecated("Use .asCPointer() instead", ReplaceWith("this.asCPointer()"), DeprecationLevel.ERROR)
|
||||
val value: COpaquePointer get() = this.asCPointer()
|
||||
|
||||
/**
|
||||
* Converts the handle to C pointer.
|
||||
* @see [asStableRef]
|
||||
*/
|
||||
fun asCPointer(): COpaquePointer = this.stablePtr
|
||||
|
||||
/**
|
||||
* Disposes the handle. It must not be used after that.
|
||||
*/
|
||||
fun dispose() {
|
||||
disposeStablePointer(this.stablePtr)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the object this handle was [created][StableRef.create] for.
|
||||
*/
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun get() = derefStablePointer(this.stablePtr) as T
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts to [StableRef] this opaque pointer produced by [StableRef.asCPointer].
|
||||
*/
|
||||
inline fun <reified T : Any> CPointer<*>.asStableRef(): StableRef<T> = StableRef<T>(this).also { it.get() }
|
||||
@@ -0,0 +1,506 @@
|
||||
/*
|
||||
* Copyright 2010-2019 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 kotlinx.cinterop
|
||||
|
||||
/**
|
||||
* The entity which has an associated native pointer.
|
||||
* Subtypes are supposed to represent interpretations of the pointed data or code.
|
||||
*
|
||||
* This interface is likely to be handled by compiler magic and shouldn't be subtyped by arbitrary classes.
|
||||
*
|
||||
* TODO: the behavior of [equals], [hashCode] and [toString] differs on Native and JVM backends.
|
||||
*/
|
||||
public open class NativePointed internal constructor(rawPtr: NonNullNativePtr) {
|
||||
var rawPtr = rawPtr.toNativePtr()
|
||||
internal set
|
||||
}
|
||||
|
||||
// `null` value of `NativePointed?` is mapped to `nativeNullPtr`.
|
||||
public val NativePointed?.rawPtr: NativePtr
|
||||
get() = if (this != null) this.rawPtr else nativeNullPtr
|
||||
|
||||
/**
|
||||
* Returns interpretation of entity with given pointer.
|
||||
*
|
||||
* @param T must not be abstract
|
||||
*/
|
||||
public inline fun <reified T : NativePointed> interpretPointed(ptr: NativePtr): T = interpretNullablePointed<T>(ptr)!!
|
||||
|
||||
private class OpaqueNativePointed(rawPtr: NativePtr) : NativePointed(rawPtr.toNonNull())
|
||||
|
||||
public fun interpretOpaquePointed(ptr: NativePtr): NativePointed = interpretPointed<OpaqueNativePointed>(ptr)
|
||||
public fun interpretNullableOpaquePointed(ptr: NativePtr): NativePointed? = interpretNullablePointed<OpaqueNativePointed>(ptr)
|
||||
|
||||
/**
|
||||
* Changes the interpretation of the pointed data or code.
|
||||
*/
|
||||
public inline fun <reified T : NativePointed> NativePointed.reinterpret(): T = interpretPointed(this.rawPtr)
|
||||
|
||||
/**
|
||||
* C data or code.
|
||||
*/
|
||||
public abstract class CPointed(rawPtr: NativePtr) : NativePointed(rawPtr.toNonNull())
|
||||
|
||||
/**
|
||||
* Represents a reference to (possibly empty) sequence of C values.
|
||||
* It can be either a stable pointer [CPointer] or a sequence of immutable values [CValues].
|
||||
*
|
||||
* [CValuesRef] is designed to be used as Kotlin representation of pointer-typed parameters of C functions.
|
||||
* When passing [CPointer] as [CValuesRef] to the Kotlin binding method, the C function receives exactly this pointer.
|
||||
* Passing [CValues] has nearly the same semantics as passing by value: the C function receives
|
||||
* the pointer to the temporary copy of these values, and the caller can't observe the modifications to this copy.
|
||||
* The copy is valid until the C function returns.
|
||||
* There are also other implementations of [CValuesRef] that provide temporary pointer,
|
||||
* e.g. Kotlin Native specific [refTo] functions to pass primitive arrays directly to native.
|
||||
*/
|
||||
public abstract class CValuesRef<T : CPointed> {
|
||||
/**
|
||||
* If this reference is [CPointer], returns this pointer, otherwise
|
||||
* allocate storage value in the scope and return it.
|
||||
*/
|
||||
public abstract fun getPointer(scope: AutofreeScope): CPointer<T>
|
||||
}
|
||||
|
||||
/**
|
||||
* The (possibly empty) sequence of immutable C values.
|
||||
* It is self-contained and doesn't depend on native memory.
|
||||
*/
|
||||
public abstract class CValues<T : CVariable> : CValuesRef<T>() {
|
||||
/**
|
||||
* Copies the values to [placement] and returns the pointer to the copy.
|
||||
*/
|
||||
public override fun getPointer(scope: AutofreeScope): CPointer<T> {
|
||||
return place(interpretCPointer(scope.alloc(size, align).rawPtr)!!)
|
||||
}
|
||||
|
||||
// TODO: optimize
|
||||
public override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is CValues<*>) return false
|
||||
|
||||
val thisBytes = this.getBytes()
|
||||
val otherBytes = other.getBytes()
|
||||
|
||||
if (thisBytes.size != otherBytes.size) {
|
||||
return false
|
||||
}
|
||||
|
||||
for (index in 0 .. thisBytes.size - 1) {
|
||||
if (thisBytes[index] != otherBytes[index]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
public override fun hashCode(): Int {
|
||||
var result = 0
|
||||
for (byte in this.getBytes()) {
|
||||
result = result * 31 + byte
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
public abstract val size: Int
|
||||
|
||||
public abstract val align: Int
|
||||
|
||||
/**
|
||||
* Copy the referenced values to [placement] and return placement pointer.
|
||||
*/
|
||||
public abstract fun place(placement: CPointer<T>): CPointer<T>
|
||||
}
|
||||
|
||||
public fun <T : CVariable> CValues<T>.placeTo(scope: AutofreeScope) = this.getPointer(scope)
|
||||
|
||||
/**
|
||||
* The single immutable C value.
|
||||
* It is self-contained and doesn't depend on native memory.
|
||||
*
|
||||
* TODO: consider providing an adapter instead of subtyping [CValues].
|
||||
*/
|
||||
public abstract class CValue<T : CVariable> : CValues<T>()
|
||||
|
||||
/**
|
||||
* C pointer.
|
||||
*/
|
||||
public class CPointer<T : CPointed> internal constructor(@PublishedApi internal val value: NonNullNativePtr) : CValuesRef<T>() {
|
||||
|
||||
// TODO: replace by [value].
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
public inline val rawValue: NativePtr get() = value.toNativePtr()
|
||||
|
||||
public override fun equals(other: Any?): Boolean {
|
||||
if (this === other) {
|
||||
return true // fast path
|
||||
}
|
||||
|
||||
return (other is CPointer<*>) && (rawValue == other.rawValue)
|
||||
}
|
||||
|
||||
public override fun hashCode(): Int {
|
||||
return rawValue.hashCode()
|
||||
}
|
||||
|
||||
public override fun toString() = this.cPointerToString()
|
||||
|
||||
public override fun getPointer(scope: AutofreeScope) = this
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the pointer to this data or code.
|
||||
*/
|
||||
public val <T : CPointed> T.ptr: CPointer<T>
|
||||
get() = interpretCPointer(this.rawPtr)!!
|
||||
|
||||
/**
|
||||
* Returns the corresponding [CPointed].
|
||||
*
|
||||
* @param T must not be abstract
|
||||
*/
|
||||
public inline val <reified T : CPointed> CPointer<T>.pointed: T
|
||||
get() = interpretPointed<T>(this.rawValue)
|
||||
|
||||
// `null` value of `CPointer?` is mapped to `nativeNullPtr`
|
||||
public val CPointer<*>?.rawValue: NativePtr
|
||||
get() = if (this != null) this.rawValue else nativeNullPtr
|
||||
|
||||
public fun <T : CPointed> CPointer<*>.reinterpret(): CPointer<T> = interpretCPointer(this.rawValue)!!
|
||||
|
||||
public fun <T : CPointed> CPointer<T>?.toLong() = this.rawValue.toLong()
|
||||
|
||||
public fun <T : CPointed> Long.toCPointer(): CPointer<T>? = interpretCPointer(nativeNullPtr + this)
|
||||
|
||||
/**
|
||||
* The [CPointed] without any specified interpretation.
|
||||
*/
|
||||
public abstract class COpaque(rawPtr: NativePtr) : CPointed(rawPtr) // TODO: should it correspond to COpaquePointer?
|
||||
|
||||
/**
|
||||
* The pointer with an opaque type.
|
||||
*/
|
||||
public typealias COpaquePointer = CPointer<out CPointed> // FIXME
|
||||
|
||||
/**
|
||||
* The variable containing a [COpaquePointer].
|
||||
*/
|
||||
public typealias COpaquePointerVar = CPointerVarOf<COpaquePointer>
|
||||
|
||||
/**
|
||||
* The C data variable located in memory.
|
||||
*
|
||||
* The non-abstract subclasses should represent the (complete) C data type and thus specify size and alignment.
|
||||
* Each such subclass must have a companion object which is a [Type].
|
||||
*/
|
||||
public abstract class CVariable(rawPtr: NativePtr) : CPointed(rawPtr) {
|
||||
|
||||
/**
|
||||
* The (complete) C data type.
|
||||
*
|
||||
* @param size the size in bytes of data of this type
|
||||
* @param align the alignments in bytes that is enough for this data type.
|
||||
* It may be greater than actually required for simplicity.
|
||||
*/
|
||||
@Deprecated("Use sizeOf<T>() or alignOf<T>() instead.")
|
||||
public open class Type(val size: Long, val align: Int) {
|
||||
|
||||
init {
|
||||
require(size % align == 0L)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
public inline fun <reified T : CVariable> sizeOf() = typeOf<T>().size
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
public inline fun <reified T : CVariable> alignOf() = typeOf<T>().align
|
||||
|
||||
/**
|
||||
* Returns the member of this [CStructVar] which is located by given offset in bytes.
|
||||
*/
|
||||
public inline fun <reified T : CPointed> CStructVar.memberAt(offset: Long): T {
|
||||
return interpretPointed<T>(this.rawPtr + offset)
|
||||
}
|
||||
|
||||
public inline fun <reified T : CVariable> CStructVar.arrayMemberAt(offset: Long): CArrayPointer<T> {
|
||||
return interpretCPointer<T>(this.rawPtr + offset)!!
|
||||
}
|
||||
|
||||
/**
|
||||
* The C struct-typed variable located in memory.
|
||||
*/
|
||||
public abstract class CStructVar(rawPtr: NativePtr) : CVariable(rawPtr) {
|
||||
@Deprecated("Use sizeOf<T>() or alignOf<T>() instead.")
|
||||
@Suppress("DEPRECATION")
|
||||
open class Type(size: Long, align: Int) : CVariable.Type(size, align)
|
||||
}
|
||||
|
||||
/**
|
||||
* The C primitive-typed variable located in memory.
|
||||
*/
|
||||
sealed class CPrimitiveVar(rawPtr: NativePtr) : CVariable(rawPtr) {
|
||||
// aligning by size is obviously enough
|
||||
@Deprecated("Use sizeOf<T>() or alignOf<T>() instead.")
|
||||
@Suppress("DEPRECATION")
|
||||
open class Type(size: Int) : CVariable.Type(size.toLong(), align = size)
|
||||
}
|
||||
|
||||
@Deprecated("Will be removed.")
|
||||
public interface CEnum {
|
||||
public val value: Any
|
||||
}
|
||||
|
||||
public abstract class CEnumVar(rawPtr: NativePtr) : CPrimitiveVar(rawPtr)
|
||||
|
||||
// generics below are used for typedef support
|
||||
// these classes are not supposed to be used directly, instead the typealiases are provided.
|
||||
|
||||
@Suppress("FINAL_UPPER_BOUND")
|
||||
public class BooleanVarOf<T : Boolean>(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) {
|
||||
@Deprecated("Use sizeOf<T>() or alignOf<T>() instead.")
|
||||
@Suppress("DEPRECATION")
|
||||
companion object : Type(1)
|
||||
}
|
||||
|
||||
@Suppress("FINAL_UPPER_BOUND")
|
||||
public class ByteVarOf<T : Byte>(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) {
|
||||
@Deprecated("Use sizeOf<T>() or alignOf<T>() instead.")
|
||||
@Suppress("DEPRECATION")
|
||||
companion object : Type(1)
|
||||
}
|
||||
|
||||
@Suppress("FINAL_UPPER_BOUND")
|
||||
public class ShortVarOf<T : Short>(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) {
|
||||
@Deprecated("Use sizeOf<T>() or alignOf<T>() instead.")
|
||||
@Suppress("DEPRECATION")
|
||||
companion object : Type(2)
|
||||
}
|
||||
|
||||
@Suppress("FINAL_UPPER_BOUND")
|
||||
public class IntVarOf<T : Int>(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) {
|
||||
@Deprecated("Use sizeOf<T>() or alignOf<T>() instead.")
|
||||
@Suppress("DEPRECATION")
|
||||
companion object : Type(4)
|
||||
}
|
||||
|
||||
@Suppress("FINAL_UPPER_BOUND")
|
||||
public class LongVarOf<T : Long>(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) {
|
||||
@Deprecated("Use sizeOf<T>() or alignOf<T>() instead.")
|
||||
@Suppress("DEPRECATION")
|
||||
companion object : Type(8)
|
||||
}
|
||||
|
||||
@Suppress("FINAL_UPPER_BOUND")
|
||||
public class UByteVarOf<T : UByte>(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) {
|
||||
@Deprecated("Use sizeOf<T>() or alignOf<T>() instead.")
|
||||
@Suppress("DEPRECATION")
|
||||
companion object : Type(1)
|
||||
}
|
||||
|
||||
@Suppress("FINAL_UPPER_BOUND")
|
||||
public class UShortVarOf<T : UShort>(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) {
|
||||
@Deprecated("Use sizeOf<T>() or alignOf<T>() instead.")
|
||||
@Suppress("DEPRECATION")
|
||||
companion object : Type(2)
|
||||
}
|
||||
|
||||
@Suppress("FINAL_UPPER_BOUND")
|
||||
public class UIntVarOf<T : UInt>(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) {
|
||||
@Deprecated("Use sizeOf<T>() or alignOf<T>() instead.")
|
||||
@Suppress("DEPRECATION")
|
||||
companion object : Type(4)
|
||||
}
|
||||
|
||||
@Suppress("FINAL_UPPER_BOUND")
|
||||
public class ULongVarOf<T : ULong>(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) {
|
||||
@Deprecated("Use sizeOf<T>() or alignOf<T>() instead.")
|
||||
@Suppress("DEPRECATION")
|
||||
companion object : Type(8)
|
||||
}
|
||||
|
||||
@Suppress("FINAL_UPPER_BOUND")
|
||||
public class FloatVarOf<T : Float>(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) {
|
||||
@Deprecated("Use sizeOf<T>() or alignOf<T>() instead.")
|
||||
@Suppress("DEPRECATION")
|
||||
companion object : Type(4)
|
||||
}
|
||||
|
||||
@Suppress("FINAL_UPPER_BOUND")
|
||||
public class DoubleVarOf<T : Double>(rawPtr: NativePtr) : CPrimitiveVar(rawPtr) {
|
||||
@Deprecated("Use sizeOf<T>() or alignOf<T>() instead.")
|
||||
@Suppress("DEPRECATION")
|
||||
companion object : Type(8)
|
||||
}
|
||||
|
||||
public typealias BooleanVar = BooleanVarOf<Boolean>
|
||||
public typealias ByteVar = ByteVarOf<Byte>
|
||||
public typealias ShortVar = ShortVarOf<Short>
|
||||
public typealias IntVar = IntVarOf<Int>
|
||||
public typealias LongVar = LongVarOf<Long>
|
||||
public typealias UByteVar = UByteVarOf<UByte>
|
||||
public typealias UShortVar = UShortVarOf<UShort>
|
||||
public typealias UIntVar = UIntVarOf<UInt>
|
||||
public typealias ULongVar = ULongVarOf<ULong>
|
||||
public typealias FloatVar = FloatVarOf<Float>
|
||||
public typealias DoubleVar = DoubleVarOf<Double>
|
||||
|
||||
@Suppress("FINAL_UPPER_BOUND", "UNCHECKED_CAST")
|
||||
public var <T : Boolean> BooleanVarOf<T>.value: T
|
||||
get() {
|
||||
val byte = nativeMemUtils.getByte(this)
|
||||
return byte.toBoolean() as T
|
||||
}
|
||||
set(value) = nativeMemUtils.putByte(this, value.toByte())
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
public inline fun Boolean.toByte(): Byte = if (this) 1 else 0
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
public inline fun Byte.toBoolean() = (this.toInt() != 0)
|
||||
|
||||
@Suppress("FINAL_UPPER_BOUND", "UNCHECKED_CAST")
|
||||
public var <T : Byte> ByteVarOf<T>.value: T
|
||||
get() = nativeMemUtils.getByte(this) as T
|
||||
set(value) = nativeMemUtils.putByte(this, value)
|
||||
|
||||
@Suppress("FINAL_UPPER_BOUND", "UNCHECKED_CAST")
|
||||
public var <T : Short> ShortVarOf<T>.value: T
|
||||
get() = nativeMemUtils.getShort(this) as T
|
||||
set(value) = nativeMemUtils.putShort(this, value)
|
||||
|
||||
@Suppress("FINAL_UPPER_BOUND", "UNCHECKED_CAST")
|
||||
public var <T : Int> IntVarOf<T>.value: T
|
||||
get() = nativeMemUtils.getInt(this) as T
|
||||
set(value) = nativeMemUtils.putInt(this, value)
|
||||
|
||||
@Suppress("FINAL_UPPER_BOUND", "UNCHECKED_CAST")
|
||||
public var <T : Long> LongVarOf<T>.value: T
|
||||
get() = nativeMemUtils.getLong(this) as T
|
||||
set(value) = nativeMemUtils.putLong(this, value)
|
||||
|
||||
@Suppress("FINAL_UPPER_BOUND", "UNCHECKED_CAST")
|
||||
public var <T : UByte> UByteVarOf<T>.value: T
|
||||
get() = nativeMemUtils.getByte(this).toUByte() as T
|
||||
set(value) = nativeMemUtils.putByte(this, value.toByte())
|
||||
|
||||
@Suppress("FINAL_UPPER_BOUND", "UNCHECKED_CAST")
|
||||
public var <T : UShort> UShortVarOf<T>.value: T
|
||||
get() = nativeMemUtils.getShort(this).toUShort() as T
|
||||
set(value) = nativeMemUtils.putShort(this, value.toShort())
|
||||
|
||||
@Suppress("FINAL_UPPER_BOUND", "UNCHECKED_CAST")
|
||||
public var <T : UInt> UIntVarOf<T>.value: T
|
||||
get() = nativeMemUtils.getInt(this).toUInt() as T
|
||||
set(value) = nativeMemUtils.putInt(this, value.toInt())
|
||||
|
||||
@Suppress("FINAL_UPPER_BOUND", "UNCHECKED_CAST")
|
||||
public var <T : ULong> ULongVarOf<T>.value: T
|
||||
get() = nativeMemUtils.getLong(this).toULong() as T
|
||||
set(value) = nativeMemUtils.putLong(this, value.toLong())
|
||||
|
||||
// TODO: ensure native floats have the appropriate binary representation
|
||||
|
||||
@Suppress("FINAL_UPPER_BOUND", "UNCHECKED_CAST")
|
||||
public var <T : Float> FloatVarOf<T>.value: T
|
||||
get() = nativeMemUtils.getFloat(this) as T
|
||||
set(value) = nativeMemUtils.putFloat(this, value)
|
||||
|
||||
@Suppress("FINAL_UPPER_BOUND", "UNCHECKED_CAST")
|
||||
public var <T : Double> DoubleVarOf<T>.value: T
|
||||
get() = nativeMemUtils.getDouble(this) as T
|
||||
set(value) = nativeMemUtils.putDouble(this, value)
|
||||
|
||||
|
||||
public class CPointerVarOf<T : CPointer<*>>(rawPtr: NativePtr) : CVariable(rawPtr) {
|
||||
@Deprecated("Use sizeOf<T>() or alignOf<T>() instead.")
|
||||
@Suppress("DEPRECATION")
|
||||
companion object : CVariable.Type(pointerSize.toLong(), pointerSize)
|
||||
}
|
||||
|
||||
/**
|
||||
* The C data variable containing the pointer to `T`.
|
||||
*/
|
||||
public typealias CPointerVar<T> = CPointerVarOf<CPointer<T>>
|
||||
|
||||
/**
|
||||
* The value of this variable.
|
||||
*/
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
public inline var <P : CPointer<*>> CPointerVarOf<P>.value: P?
|
||||
get() = interpretCPointer<CPointed>(nativeMemUtils.getNativePtr(this)) as P?
|
||||
set(value) = nativeMemUtils.putNativePtr(this, value.rawValue)
|
||||
|
||||
/**
|
||||
* The code or data pointed by the value of this variable.
|
||||
*
|
||||
* @param T must not be abstract
|
||||
*/
|
||||
public inline var <reified T : CPointed, reified P : CPointer<T>> CPointerVarOf<P>.pointed: T?
|
||||
get() = this.value?.pointed
|
||||
set(value) {
|
||||
this.value = value?.ptr as P?
|
||||
}
|
||||
|
||||
public inline operator fun <reified T : CVariable> CPointer<T>.get(index: Long): T {
|
||||
val offset = if (index == 0L) {
|
||||
0L // optimization for JVM impl which uses reflection for now.
|
||||
} else {
|
||||
index * sizeOf<T>()
|
||||
}
|
||||
return interpretPointed(this.rawValue + offset)
|
||||
}
|
||||
|
||||
public inline operator fun <reified T : CVariable> CPointer<T>.get(index: Int): T = this.get(index.toLong())
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
@JvmName("plus\$CPointer")
|
||||
public inline operator fun <T : CPointerVarOf<*>> CPointer<T>?.plus(index: Long): CPointer<T>? =
|
||||
interpretCPointer(this.rawValue + index * pointerSize)
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
@JvmName("plus\$CPointer")
|
||||
public inline operator fun <T : CPointerVarOf<*>> CPointer<T>?.plus(index: Int): CPointer<T>? =
|
||||
this + index.toLong()
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
public inline operator fun <T : CPointer<*>> CPointer<CPointerVarOf<T>>.get(index: Int): T? =
|
||||
(this + index)!!.pointed.value
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
public inline operator fun <T : CPointer<*>> CPointer<CPointerVarOf<T>>.set(index: Int, value: T?) {
|
||||
(this + index)!!.pointed.value = value
|
||||
}
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
public inline operator fun <T : CPointer<*>> CPointer<CPointerVarOf<T>>.get(index: Long): T? =
|
||||
(this + index)!!.pointed.value
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
public inline operator fun <T : CPointer<*>> CPointer<CPointerVarOf<T>>.set(index: Long, value: T?) {
|
||||
(this + index)!!.pointed.value = value
|
||||
}
|
||||
|
||||
public typealias CArrayPointer<T> = CPointer<T>
|
||||
public typealias CArrayPointerVar<T> = CPointerVar<T>
|
||||
|
||||
/**
|
||||
* The C function.
|
||||
*/
|
||||
public class CFunction<T : Function<*>>(rawPtr: NativePtr) : CPointed(rawPtr)
|
||||
@@ -0,0 +1,650 @@
|
||||
/*
|
||||
* 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 kotlinx.cinterop
|
||||
|
||||
public interface NativePlacement {
|
||||
|
||||
public fun alloc(size: Long, align: Int): NativePointed
|
||||
|
||||
public fun alloc(size: Int, align: Int): NativePointed = alloc(size.toLong(), align)
|
||||
}
|
||||
|
||||
public interface NativeFreeablePlacement : NativePlacement {
|
||||
public fun free(mem: NativePtr)
|
||||
}
|
||||
|
||||
public fun NativeFreeablePlacement.free(pointer: CPointer<*>) = this.free(pointer.rawValue)
|
||||
public fun NativeFreeablePlacement.free(pointed: NativePointed) = this.free(pointed.rawPtr)
|
||||
|
||||
public object nativeHeap : NativeFreeablePlacement {
|
||||
override fun alloc(size: Long, align: Int) = nativeMemUtils.alloc(size, align)
|
||||
|
||||
override fun free(mem: NativePtr) = nativeMemUtils.free(mem)
|
||||
}
|
||||
|
||||
private typealias Deferred = () -> Unit
|
||||
|
||||
public open class DeferScope {
|
||||
|
||||
@PublishedApi
|
||||
internal var topDeferred: Deferred? = null
|
||||
|
||||
internal fun executeAllDeferred() {
|
||||
topDeferred?.let {
|
||||
it.invoke()
|
||||
topDeferred = null
|
||||
}
|
||||
}
|
||||
|
||||
inline fun defer(crossinline block: () -> Unit) {
|
||||
val currentTop = topDeferred
|
||||
topDeferred = {
|
||||
try {
|
||||
block()
|
||||
} finally {
|
||||
// TODO: it is possible to implement chaining without recursion,
|
||||
// but it would require using an anonymous object here
|
||||
// which is not yet supported in Kotlin Native inliner.
|
||||
currentTop?.invoke()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class AutofreeScope : DeferScope(), NativePlacement {
|
||||
abstract override fun alloc(size: Long, align: Int): NativePointed
|
||||
}
|
||||
|
||||
public open class ArenaBase(private val parent: NativeFreeablePlacement = nativeHeap) : AutofreeScope() {
|
||||
|
||||
private var lastChunk: NativePointed? = null
|
||||
|
||||
final override fun alloc(size: Long, align: Int): NativePointed {
|
||||
// Reserve space for a pointer:
|
||||
val gapForPointer = maxOf(pointerSize, align)
|
||||
|
||||
val chunk = parent.alloc(size = gapForPointer + size, align = gapForPointer)
|
||||
nativeMemUtils.putNativePtr(chunk, lastChunk.rawPtr)
|
||||
lastChunk = chunk
|
||||
return interpretOpaquePointed(chunk.rawPtr + gapForPointer.toLong())
|
||||
}
|
||||
|
||||
@PublishedApi
|
||||
internal fun clearImpl() {
|
||||
this.executeAllDeferred()
|
||||
|
||||
var chunk = lastChunk
|
||||
while (chunk != null) {
|
||||
val nextChunk = nativeMemUtils.getNativePtr(chunk)
|
||||
parent.free(chunk)
|
||||
chunk = interpretNullableOpaquePointed(nextChunk)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class Arena(parent: NativeFreeablePlacement = nativeHeap) : ArenaBase(parent) {
|
||||
fun clear() = this.clearImpl()
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocates variable of given type.
|
||||
*
|
||||
* @param T must not be abstract
|
||||
*/
|
||||
public inline fun <reified T : CVariable> NativePlacement.alloc(): T =
|
||||
@Suppress("DEPRECATION")
|
||||
alloc(typeOf<T>()).reinterpret()
|
||||
|
||||
@PublishedApi
|
||||
@Suppress("DEPRECATION")
|
||||
internal fun NativePlacement.alloc(type: CVariable.Type): NativePointed =
|
||||
alloc(type.size, type.align)
|
||||
|
||||
/**
|
||||
* Allocates variable of given type and initializes it applying given block.
|
||||
*
|
||||
* @param T must not be abstract
|
||||
*/
|
||||
public inline fun <reified T : CVariable> NativePlacement.alloc(initialize: T.() -> Unit): T =
|
||||
alloc<T>().also { it.initialize() }
|
||||
|
||||
/**
|
||||
* Allocates C array of given elements type and length.
|
||||
*
|
||||
* @param T must not be abstract
|
||||
*/
|
||||
public inline fun <reified T : CVariable> NativePlacement.allocArray(length: Long): CArrayPointer<T> =
|
||||
alloc(sizeOf<T>() * length, alignOf<T>()).reinterpret<T>().ptr
|
||||
|
||||
/**
|
||||
* Allocates C array of given elements type and length.
|
||||
*
|
||||
* @param T must not be abstract
|
||||
*/
|
||||
public inline fun <reified T : CVariable> NativePlacement.allocArray(length: Int): CArrayPointer<T> =
|
||||
allocArray(length.toLong())
|
||||
|
||||
/**
|
||||
* Allocates C array of given elements type and length, and initializes its elements applying given block.
|
||||
*
|
||||
* @param T must not be abstract
|
||||
*/
|
||||
public inline fun <reified T : CVariable> NativePlacement.allocArray(length: Long,
|
||||
initializer: T.(index: Long)->Unit): CArrayPointer<T> {
|
||||
val res = allocArray<T>(length)
|
||||
|
||||
(0 .. length - 1).forEach { index ->
|
||||
res[index].initializer(index)
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocates C array of given elements type and length, and initializes its elements applying given block.
|
||||
*
|
||||
* @param T must not be abstract
|
||||
*/
|
||||
public inline fun <reified T : CVariable> NativePlacement.allocArray(
|
||||
length: Int, initializer: T.(index: Int)->Unit): CArrayPointer<T> = allocArray(length.toLong()) { index ->
|
||||
this.initializer(index.toInt())
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Allocates C array of pointers to given elements.
|
||||
*/
|
||||
public fun <T : CPointed> NativePlacement.allocArrayOfPointersTo(elements: List<T?>): CArrayPointer<CPointerVar<T>> {
|
||||
val res = allocArray<CPointerVar<T>>(elements.size)
|
||||
elements.forEachIndexed { index, value ->
|
||||
res[index] = value?.ptr
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocates C array of pointers to given elements.
|
||||
*/
|
||||
public fun <T : CPointed> NativePlacement.allocArrayOfPointersTo(vararg elements: T?) =
|
||||
allocArrayOfPointersTo(listOf(*elements))
|
||||
|
||||
/**
|
||||
* Allocates C array of given values.
|
||||
*/
|
||||
public inline fun <reified T : CPointer<*>>
|
||||
NativePlacement.allocArrayOf(vararg elements: T?): CArrayPointer<CPointerVarOf<T>> {
|
||||
return allocArrayOf(listOf(*elements))
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocates C array of given values.
|
||||
*/
|
||||
public inline fun <reified T : CPointer<*>>
|
||||
NativePlacement.allocArrayOf(elements: List<T?>): CArrayPointer<CPointerVarOf<T>> {
|
||||
|
||||
val res = allocArray<CPointerVarOf<T>>(elements.size)
|
||||
var index = 0
|
||||
while (index < elements.size) {
|
||||
res[index] = elements[index]
|
||||
++index
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
public fun NativePlacement.allocArrayOf(elements: ByteArray): CArrayPointer<ByteVar> {
|
||||
val result = allocArray<ByteVar>(elements.size)
|
||||
nativeMemUtils.putByteArray(elements, result.pointed, elements.size)
|
||||
return result
|
||||
}
|
||||
|
||||
public fun NativePlacement.allocArrayOf(vararg elements: Float): CArrayPointer<FloatVar> {
|
||||
val res = allocArray<FloatVar>(elements.size)
|
||||
var index = 0
|
||||
while (index < elements.size) {
|
||||
res[index] = elements[index]
|
||||
++index
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
public fun <T : CPointed> NativePlacement.allocPointerTo() = alloc<CPointerVar<T>>()
|
||||
|
||||
@PublishedApi
|
||||
internal class ZeroValue<T: CVariable>(private val sizeBytes: Int, private val alignBytes: Int): CValue<T>() {
|
||||
// Optimization to avoid unneeded virtual calls in base class implementation.
|
||||
override fun getPointer(scope: AutofreeScope): CPointer<T> {
|
||||
return place(interpretCPointer(scope.alloc(size, align).rawPtr)!!)
|
||||
}
|
||||
|
||||
override fun place(placement: CPointer<T>): CPointer<T> {
|
||||
nativeMemUtils.zeroMemory(interpretPointed(placement.rawValue), sizeBytes)
|
||||
return placement
|
||||
}
|
||||
override val size get() = sizeBytes
|
||||
|
||||
override val align get() = alignBytes
|
||||
|
||||
}
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
public inline fun <T : CVariable> zeroValue(size: Int, align: Int): CValue<T> = ZeroValue(size, align)
|
||||
|
||||
public inline fun <reified T : CVariable> zeroValue(): CValue<T> = zeroValue<T>(sizeOf<T>().toInt(), alignOf<T>())
|
||||
|
||||
public inline fun <reified T : CVariable> cValue(): CValue<T> = zeroValue<T>()
|
||||
|
||||
public fun <T : CVariable> CPointed.readValues(size: Int, align: Int): CValues<T> {
|
||||
val bytes = ByteArray(size)
|
||||
nativeMemUtils.getByteArray(this, bytes, size)
|
||||
|
||||
return object : CValue<T>() {
|
||||
// Optimization to avoid unneeded virtual calls in base class implementation.
|
||||
override fun getPointer(scope: AutofreeScope): CPointer<T> {
|
||||
return place(interpretCPointer(scope.alloc(size, align).rawPtr)!!)
|
||||
}
|
||||
override fun place(placement: CPointer<T>): CPointer<T> {
|
||||
nativeMemUtils.putByteArray(bytes, interpretPointed(placement.rawValue), bytes.size)
|
||||
return placement
|
||||
}
|
||||
override val size get() = size
|
||||
override val align get() = align
|
||||
}
|
||||
}
|
||||
|
||||
public inline fun <reified T : CVariable> T.readValues(count: Int): CValues<T> =
|
||||
this.readValues<T>(size = count * sizeOf<T>().toInt(), align = alignOf<T>())
|
||||
|
||||
public fun <T : CVariable> CPointed.readValue(size: Long, align: Int): CValue<T> {
|
||||
val bytes = ByteArray(size.toInt())
|
||||
nativeMemUtils.getByteArray(this, bytes, size.toInt())
|
||||
|
||||
return object : CValue<T>() {
|
||||
override fun place(placement: CPointer<T>): CPointer<T> {
|
||||
nativeMemUtils.putByteArray(bytes, interpretPointed(placement.rawValue), bytes.size)
|
||||
return placement
|
||||
}
|
||||
// Optimization to avoid unneeded virtual calls in base class implementation.
|
||||
public override fun getPointer(scope: AutofreeScope): CPointer<T> {
|
||||
return place(interpretCPointer(scope.alloc(size, align).rawPtr)!!)
|
||||
}
|
||||
override val size get() = size.toInt()
|
||||
override val align get() = align
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
@PublishedApi internal fun <T : CVariable> CPointed.readValue(type: CVariable.Type): CValue<T> =
|
||||
readValue(type.size, type.align)
|
||||
|
||||
// Note: can't be declared as property due to possible clash with a struct field.
|
||||
// TODO: find better name.
|
||||
@Suppress("DEPRECATION")
|
||||
public inline fun <reified T : CStructVar> T.readValue(): CValue<T> = this.readValue(typeOf<T>())
|
||||
|
||||
public fun <T: CVariable> CValue<T>.write(location: NativePtr) {
|
||||
this.place(interpretCPointer(location)!!)
|
||||
}
|
||||
|
||||
// TODO: optimize
|
||||
public fun <T : CVariable> CValues<T>.getBytes(): ByteArray = memScoped {
|
||||
val result = ByteArray(size)
|
||||
nativeMemUtils.getByteArray(
|
||||
source = this@getBytes.placeTo(memScope).reinterpret<ByteVar>().pointed,
|
||||
dest = result,
|
||||
length = result.size
|
||||
)
|
||||
result
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the [block] with temporary copy of this value as receiver.
|
||||
*/
|
||||
public inline fun <reified T : CStructVar, R> CValue<T>.useContents(block: T.() -> R): R = memScoped {
|
||||
this@useContents.placeTo(memScope).pointed.block()
|
||||
}
|
||||
|
||||
public inline fun <reified T : CStructVar> CValue<T>.copy(modify: T.() -> Unit): CValue<T> = useContents {
|
||||
this.modify()
|
||||
this.readValue()
|
||||
}
|
||||
|
||||
public inline fun <reified T : CStructVar> cValue(initialize: T.() -> Unit): CValue<T> =
|
||||
zeroValue<T>().copy(modify = initialize)
|
||||
|
||||
public inline fun <reified T : CVariable> createValues(count: Int, initializer: T.(index: Int) -> Unit) = memScoped {
|
||||
val array = allocArray<T>(count, initializer)
|
||||
array[0].readValues(count)
|
||||
}
|
||||
|
||||
// TODO: optimize other [cValuesOf] methods:
|
||||
/**
|
||||
* Returns sequence of immutable values [CValues] to pass them to C code.
|
||||
*/
|
||||
fun cValuesOf(vararg elements: Byte): CValues<ByteVar> = object : CValues<ByteVar>() {
|
||||
// Optimization to avoid unneeded virtual calls in base class implementation.
|
||||
override fun getPointer(scope: AutofreeScope): CPointer<ByteVar> {
|
||||
return place(interpretCPointer(scope.alloc(size, align).rawPtr)!!)
|
||||
}
|
||||
override fun place(placement: CPointer<ByteVar>): CPointer<ByteVar> {
|
||||
nativeMemUtils.putByteArray(elements, interpretPointed(placement.rawValue), elements.size)
|
||||
return placement
|
||||
}
|
||||
|
||||
override val size get() = 1 * elements.size
|
||||
override val align get() = 1
|
||||
}
|
||||
|
||||
public fun cValuesOf(vararg elements: Short): CValues<ShortVar> =
|
||||
createValues(elements.size) { index -> this.value = elements[index] }
|
||||
|
||||
public fun cValuesOf(vararg elements: Int): CValues<IntVar> =
|
||||
createValues(elements.size) { index -> this.value = elements[index] }
|
||||
|
||||
public fun cValuesOf(vararg elements: Long): CValues<LongVar> =
|
||||
createValues(elements.size) { index -> this.value = elements[index] }
|
||||
|
||||
public fun cValuesOf(vararg elements: Float): CValues<FloatVar> =
|
||||
createValues(elements.size) { index -> this.value = elements[index] }
|
||||
|
||||
public fun cValuesOf(vararg elements: Double): CValues<DoubleVar> =
|
||||
createValues(elements.size) { index -> this.value = elements[index] }
|
||||
|
||||
public fun <T : CPointed> cValuesOf(vararg elements: CPointer<T>?): CValues<CPointerVar<T>> =
|
||||
createValues(elements.size) { index -> this.value = elements[index] }
|
||||
|
||||
public fun ByteArray.toCValues() = cValuesOf(*this)
|
||||
public fun ShortArray.toCValues() = cValuesOf(*this)
|
||||
public fun IntArray.toCValues() = cValuesOf(*this)
|
||||
public fun LongArray.toCValues() = cValuesOf(*this)
|
||||
public fun FloatArray.toCValues() = cValuesOf(*this)
|
||||
public fun DoubleArray.toCValues() = cValuesOf(*this)
|
||||
public fun <T : CPointed> Array<CPointer<T>?>.toCValues() = cValuesOf(*this)
|
||||
public fun <T : CPointed> List<CPointer<T>?>.toCValues() = this.toTypedArray().toCValues()
|
||||
|
||||
private class CString(val bytes: ByteArray): CValues<ByteVar>() {
|
||||
override val size get() = bytes.size + 1
|
||||
override val align get() = 1
|
||||
|
||||
// Optimization to avoid unneeded virtual calls in base class implementation.
|
||||
override fun getPointer(scope: AutofreeScope): CPointer<ByteVar> {
|
||||
return place(interpretCPointer(scope.alloc(size, align).rawPtr)!!)
|
||||
}
|
||||
override fun place(placement: CPointer<ByteVar>): CPointer<ByteVar> {
|
||||
nativeMemUtils.putByteArray(bytes, placement.pointed, bytes.size)
|
||||
placement[bytes.size] = 0.toByte()
|
||||
return placement
|
||||
}
|
||||
}
|
||||
|
||||
private object EmptyCString: CValues<ByteVar>() {
|
||||
override val size get() = 1
|
||||
override val align get() = 1
|
||||
|
||||
private val placement =
|
||||
interpretCPointer<ByteVar>(nativeMemUtils.allocRaw(1, 1))!!.also {
|
||||
it[0] = 0.toByte()
|
||||
}
|
||||
|
||||
override fun getPointer(scope: AutofreeScope): CPointer<ByteVar> {
|
||||
return placement
|
||||
}
|
||||
override fun place(placement: CPointer<ByteVar>): CPointer<ByteVar> {
|
||||
placement[0] = 0.toByte()
|
||||
return placement
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the value of zero-terminated UTF-8-encoded C string constructed from given [kotlin.String].
|
||||
*/
|
||||
public val String.cstr: CValues<ByteVar>
|
||||
get() = if (isEmpty()) EmptyCString else CString(encodeToUtf8(this))
|
||||
|
||||
/**
|
||||
* @return the value of zero-terminated UTF-8-encoded C string constructed from given [kotlin.String].
|
||||
*/
|
||||
public val String.utf8: CValues<ByteVar>
|
||||
get() = CString(encodeToUtf8(this))
|
||||
|
||||
/**
|
||||
* Convert this list of Kotlin strings to C array of C strings,
|
||||
* allocating memory for the array and C strings with given [AutofreeScope].
|
||||
*/
|
||||
public fun List<String>.toCStringArray(autofreeScope: AutofreeScope): CPointer<CPointerVar<ByteVar>> =
|
||||
autofreeScope.allocArrayOf(this.map { it.cstr.getPointer(autofreeScope) })
|
||||
|
||||
/**
|
||||
* Convert this array of Kotlin strings to C array of C strings,
|
||||
* allocating memory for the array and C strings with given [AutofreeScope].
|
||||
*/
|
||||
public fun Array<String>.toCStringArray(autofreeScope: AutofreeScope): CPointer<CPointerVar<ByteVar>> =
|
||||
autofreeScope.allocArrayOf(this.map { it.cstr.getPointer(autofreeScope) })
|
||||
|
||||
|
||||
private class U16CString(val chars: CharArray): CValues<UShortVar>() {
|
||||
override val size get() = 2 * (chars.size + 1)
|
||||
|
||||
override val align get() = 2
|
||||
|
||||
// Optimization to avoid unneeded virtual calls in base class implementation.
|
||||
override fun getPointer(scope: AutofreeScope): CPointer<UShortVar> {
|
||||
return place(interpretCPointer(scope.alloc(size, align).rawPtr)!!)
|
||||
}
|
||||
|
||||
override fun place(placement: CPointer<UShortVar>): CPointer<UShortVar> {
|
||||
nativeMemUtils.putCharArray(chars, placement.pointed, chars.size)
|
||||
// TODO: fix, after KT-29627 is fixed.
|
||||
nativeMemUtils.putShort((placement + chars.size)!!.pointed, 0)
|
||||
return placement
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the value of zero-terminated UTF-16-encoded C string constructed from given [kotlin.String].
|
||||
*/
|
||||
public val String.wcstr: CValues<UShortVar>
|
||||
get() = U16CString(this.toCharArray())
|
||||
|
||||
/**
|
||||
* @return the value of zero-terminated UTF-16-encoded C string constructed from given [kotlin.String].
|
||||
*/
|
||||
public val String.utf16: CValues<UShortVar>
|
||||
get() = U16CString(this.toCharArray())
|
||||
|
||||
private class U32CString(val chars: CharArray): CValues<IntVar>() {
|
||||
override val size get() = 4 * (chars.size + 1)
|
||||
|
||||
override val align get() = 4
|
||||
|
||||
// Optimization to avoid unneeded virtual calls in base class implementation.
|
||||
override fun getPointer(scope: AutofreeScope): CPointer<IntVar> {
|
||||
return place(interpretCPointer(scope.alloc(size, align).rawPtr)!!)
|
||||
}
|
||||
|
||||
override fun place(placement: CPointer<IntVar>): CPointer<IntVar> {
|
||||
var indexIn = 0
|
||||
var indexOut = 0
|
||||
while (indexIn < chars.size) {
|
||||
var value = chars[indexIn++].toInt()
|
||||
if (value >= 0xd800 && value < 0xdc00) {
|
||||
// Surrogate pair.
|
||||
if (indexIn >= chars.size - 1) throw IllegalArgumentException()
|
||||
indexIn++
|
||||
val next = chars[indexIn].toInt()
|
||||
if (next < 0xdc00 || next >= 0xe000) throw IllegalArgumentException()
|
||||
value = 0x10000 + ((value and 0x3ff) shl 10) + (next and 0x3ff)
|
||||
}
|
||||
nativeMemUtils.putInt((placement + indexOut)!!.pointed, value)
|
||||
indexOut++
|
||||
}
|
||||
nativeMemUtils.putInt((placement + indexOut)!!.pointed, 0)
|
||||
return placement
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the value of zero-terminated UTF-32-encoded C string constructed from given [kotlin.String].
|
||||
*/
|
||||
public val String.utf32: CValues<IntVar>
|
||||
get() = U32CString(this.toCharArray())
|
||||
|
||||
|
||||
// TODO: optimize
|
||||
/**
|
||||
* @return the [kotlin.String] decoded from given zero-terminated UTF-8-encoded C string.
|
||||
*/
|
||||
public fun CPointer<ByteVar>.toKStringFromUtf8(): String = this.toKStringFromUtf8Impl()
|
||||
|
||||
/**
|
||||
* @return the [kotlin.String] decoded from given zero-terminated UTF-8-encoded C string.
|
||||
*/
|
||||
public fun CPointer<ByteVar>.toKString(): String = this.toKStringFromUtf8()
|
||||
|
||||
/**
|
||||
* @return the [kotlin.String] decoded from given zero-terminated UTF-16-encoded C string.
|
||||
*/
|
||||
public fun CPointer<ShortVar>.toKStringFromUtf16(): String {
|
||||
val nativeBytes = this
|
||||
|
||||
var length = 0
|
||||
while (nativeBytes[length] != 0.toShort()) {
|
||||
++length
|
||||
}
|
||||
val chars = CharArray(length)
|
||||
var index = 0
|
||||
while (index < length) {
|
||||
chars[index] = nativeBytes[index].toChar()
|
||||
++index
|
||||
}
|
||||
return chars.concatToString()
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the [kotlin.String] decoded from given zero-terminated UTF-32-encoded C string.
|
||||
*/
|
||||
public fun CPointer<IntVar>.toKStringFromUtf32(): String {
|
||||
val nativeBytes = this
|
||||
|
||||
var fromIndex = 0
|
||||
var toIndex = 0
|
||||
while (true) {
|
||||
val value = nativeBytes[fromIndex++]
|
||||
if (value == 0) break
|
||||
toIndex++
|
||||
if (value >= 0x10000 && value <= 0x10ffff) {
|
||||
toIndex++
|
||||
}
|
||||
}
|
||||
val length = toIndex
|
||||
val chars = CharArray(length)
|
||||
fromIndex = 0
|
||||
toIndex = 0
|
||||
while (toIndex < length) {
|
||||
var value = nativeBytes[fromIndex++]
|
||||
if (value >= 0x10000 && value <= 0x10ffff) {
|
||||
chars[toIndex++] = (((value - 0x10000) shr 10) or 0xd800).toChar()
|
||||
chars[toIndex++] = (((value - 0x10000) and 0x3ff) or 0xdc00).toChar()
|
||||
} else {
|
||||
chars[toIndex++] = value.toChar()
|
||||
}
|
||||
}
|
||||
return chars.concatToString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a string from the bytes in UTF-8 encoding in this array.
|
||||
* Bytes following the first occurrence of `0` byte, if it occurs, are not decoded.
|
||||
*
|
||||
* Malformed byte sequences are replaced by the replacement char `\uFFFD`.
|
||||
*/
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
@SinceKotlin("1.3")
|
||||
public fun ByteArray.toKString() : String {
|
||||
val realEndIndex = realEndIndex(this, 0, this.size)
|
||||
return decodeToString(0, realEndIndex)
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a string from the bytes in UTF-8 encoding in this array or its subrange.
|
||||
* Bytes following the first occurrence of `0` byte, if it occurs, are not decoded.
|
||||
*
|
||||
* @param startIndex the beginning (inclusive) of the subrange to decode, 0 by default.
|
||||
* @param endIndex the end (exclusive) of the subrange to decode, size of this array by default.
|
||||
* @param throwOnInvalidSequence specifies whether to throw an exception on malformed byte sequence or replace it by the replacement char `\uFFFD`.
|
||||
*
|
||||
* @throws IndexOutOfBoundsException if [startIndex] is less than zero or [endIndex] is greater than the size of this array.
|
||||
* @throws IllegalArgumentException if [startIndex] is greater than [endIndex].
|
||||
* @throws CharacterCodingException if the byte array contains malformed UTF-8 byte sequence and [throwOnInvalidSequence] is true.
|
||||
*/
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
@SinceKotlin("1.3")
|
||||
public fun ByteArray.toKString(
|
||||
startIndex: Int = 0,
|
||||
endIndex: Int = this.size,
|
||||
throwOnInvalidSequence: Boolean = false
|
||||
) : String {
|
||||
checkBoundsIndexes(startIndex, endIndex, this.size)
|
||||
val realEndIndex = realEndIndex(this, startIndex, endIndex)
|
||||
return decodeToString(startIndex, realEndIndex, throwOnInvalidSequence)
|
||||
}
|
||||
|
||||
private fun realEndIndex(byteArray: ByteArray, startIndex: Int, endIndex: Int): Int {
|
||||
var index = startIndex
|
||||
while (index < endIndex && byteArray[index] != 0.toByte()) {
|
||||
index++
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
||||
private fun checkBoundsIndexes(startIndex: Int, endIndex: Int, size: Int) {
|
||||
if (startIndex < 0 || endIndex > size) {
|
||||
throw IndexOutOfBoundsException("startIndex: $startIndex, endIndex: $endIndex, size: $size")
|
||||
}
|
||||
if (startIndex > endIndex) {
|
||||
throw IllegalArgumentException("startIndex: $startIndex > endIndex: $endIndex")
|
||||
}
|
||||
}
|
||||
|
||||
public class MemScope : ArenaBase() {
|
||||
|
||||
val memScope: MemScope
|
||||
get() = this
|
||||
|
||||
val <T: CVariable> CValues<T>.ptr: CPointer<T>
|
||||
get() = this@ptr.getPointer(this@MemScope)
|
||||
}
|
||||
|
||||
// TODO: consider renaming `memScoped` because it now supports `defer`.
|
||||
|
||||
/**
|
||||
* Runs given [block] providing allocation of memory
|
||||
* which will be automatically disposed at the end of this scope.
|
||||
*/
|
||||
public inline fun <R> memScoped(block: MemScope.()->R): R {
|
||||
val memScope = MemScope()
|
||||
try {
|
||||
return memScope.block()
|
||||
} finally {
|
||||
memScope.clearImpl()
|
||||
}
|
||||
}
|
||||
|
||||
public fun COpaquePointer.readBytes(count: Int): ByteArray {
|
||||
val result = ByteArray(count)
|
||||
nativeMemUtils.getByteArray(this.reinterpret<ByteVar>().pointed, result, count)
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This package contains API and runtime support for calling C code from Kotlin (aka Kotlin C interop).
|
||||
*
|
||||
* TODO: decide about package location.
|
||||
*/
|
||||
package kotlinx.cinterop;
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
package kotlinx.cinterop
|
||||
|
||||
import kotlin.native.internal.ExportForCppRuntime
|
||||
|
||||
public class ForeignException internal constructor(val nativeException: Any?): Exception() {
|
||||
override val message: String = nativeException?.let {
|
||||
kotlin_ObjCExport_ExceptionDetails(nativeException)
|
||||
}?: ""
|
||||
|
||||
// Current implementation expects NSException type only, which is ensured by CodeGenerator.
|
||||
@SymbolName("Kotlin_ObjCExport_ExceptionDetails")
|
||||
private external fun kotlin_ObjCExport_ExceptionDetails(nativeException: Any): String?
|
||||
}
|
||||
|
||||
@ExportForCppRuntime
|
||||
internal fun CreateForeignException(payload: NativePtr): Throwable
|
||||
= ForeignException(interpretObjCPointerOrNull<Any?>(payload))
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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 kotlinx.cinterop
|
||||
|
||||
import kotlin.native.internal.TypedIntrinsic
|
||||
import kotlin.native.internal.IntrinsicType
|
||||
import kotlin.native.internal.ExportForCompiler
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_FUNPTR_INVOKE) external operator fun <R> CPointer<CFunction<() -> R>>.invoke(): R
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_FUNPTR_INVOKE) external operator fun <P1, R> CPointer<CFunction<(P1) -> R>>.invoke(p1: P1): R
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_FUNPTR_INVOKE) external operator fun <P1, P2, R> CPointer<CFunction<(P1, P2) -> R>>.invoke(p1: P1, p2: P2): R
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_FUNPTR_INVOKE) external operator fun <P1, P2, P3, R> CPointer<CFunction<(P1, P2, P3) -> R>>.invoke(p1: P1, p2: P2, p3: P3): R
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_FUNPTR_INVOKE) external operator fun <P1, P2, P3, P4, R> CPointer<CFunction<(P1, P2, P3, P4) -> R>>.invoke(p1: P1, p2: P2, p3: P3, p4: P4): R
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_FUNPTR_INVOKE) external operator fun <P1, P2, P3, P4, P5, R> CPointer<CFunction<(P1, P2, P3, P4, P5) -> R>>.invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5): R
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_FUNPTR_INVOKE) external operator fun <P1, P2, P3, P4, P5, P6, R> CPointer<CFunction<(P1, P2, P3, P4, P5, P6) -> R>>.invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6): R
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_FUNPTR_INVOKE) external operator fun <P1, P2, P3, P4, P5, P6, P7, R> CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7) -> R>>.invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7): R
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_FUNPTR_INVOKE) external operator fun <P1, P2, P3, P4, P5, P6, P7, P8, R> CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8) -> R>>.invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8): R
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_FUNPTR_INVOKE) external operator fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, R> CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9) -> R>>.invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9): R
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_FUNPTR_INVOKE) external operator fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, R> CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) -> R>>.invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9, p10: P10): R
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_FUNPTR_INVOKE) external operator fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, R> CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11) -> R>>.invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9, p10: P10, p11: P11): R
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_FUNPTR_INVOKE) external operator fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, R> CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) -> R>>.invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9, p10: P10, p11: P11, p12: P12): R
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_FUNPTR_INVOKE) external operator fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, R> CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13) -> R>>.invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9, p10: P10, p11: P11, p12: P12, p13: P13): R
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_FUNPTR_INVOKE) external operator fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, R> CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14) -> R>>.invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9, p10: P10, p11: P11, p12: P12, p13: P13, p14: P14): R
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_FUNPTR_INVOKE) external operator fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, R> CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15) -> R>>.invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9, p10: P10, p11: P11, p12: P12, p13: P13, p14: P14, p15: P15): R
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_FUNPTR_INVOKE) external operator fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, R> CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16) -> R>>.invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9, p10: P10, p11: P11, p12: P12, p13: P13, p14: P14, p15: P15, p16: P16): R
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_FUNPTR_INVOKE) external operator fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, R> CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17) -> R>>.invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9, p10: P10, p11: P11, p12: P12, p13: P13, p14: P14, p15: P15, p16: P16, p17: P17): R
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_FUNPTR_INVOKE) external operator fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, R> CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18) -> R>>.invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9, p10: P10, p11: P11, p12: P12, p13: P13, p14: P14, p15: P15, p16: P16, p17: P17, p18: P18): R
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_FUNPTR_INVOKE) external operator fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, R> CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19) -> R>>.invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9, p10: P10, p11: P11, p12: P12, p13: P13, p14: P14, p15: P15, p16: P16, p17: P17, p18: P18, p19: P19): R
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_FUNPTR_INVOKE) external operator fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, R> CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20) -> R>>.invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9, p10: P10, p11: P11, p12: P12, p13: P13, p14: P14, p15: P15, p16: P16, p17: P17, p18: P18, p19: P19, p20: P20): R
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_FUNPTR_INVOKE) external operator fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, R> CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21) -> R>>.invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9, p10: P10, p11: P11, p12: P12, p13: P13, p14: P14, p15: P15, p16: P16, p17: P17, p18: P18, p19: P19, p20: P20, p21: P21): R
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_FUNPTR_INVOKE) external operator fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, R> CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) -> R>>.invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9, p10: P10, p11: P11, p12: P12, p13: P13, p14: P14, p15: P15, p16: P16, p17: P17, p18: P18, p19: P19, p20: P20, p21: P21, p22: P22): R
|
||||
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* 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 kotlinx.cinterop
|
||||
|
||||
import kotlin.native.*
|
||||
import kotlin.native.internal.Intrinsic
|
||||
import kotlin.native.internal.TypedIntrinsic
|
||||
import kotlin.native.internal.IntrinsicType
|
||||
|
||||
@PublishedApi
|
||||
internal inline val pointerSize: Int
|
||||
get() = getPointerSize()
|
||||
|
||||
@PublishedApi
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_GET_POINTER_SIZE)
|
||||
internal external fun getPointerSize(): Int
|
||||
|
||||
// TODO: do not use singleton because it leads to init-check on any access.
|
||||
@PublishedApi
|
||||
internal object nativeMemUtils {
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_READ_PRIMITIVE) external fun getByte(mem: NativePointed): Byte
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_WRITE_PRIMITIVE) external fun putByte(mem: NativePointed, value: Byte)
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_READ_PRIMITIVE) external fun getShort(mem: NativePointed): Short
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_WRITE_PRIMITIVE) external fun putShort(mem: NativePointed, value: Short)
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_READ_PRIMITIVE) external fun getInt(mem: NativePointed): Int
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_WRITE_PRIMITIVE) external fun putInt(mem: NativePointed, value: Int)
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_READ_PRIMITIVE) external fun getLong(mem: NativePointed): Long
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_WRITE_PRIMITIVE) external fun putLong(mem: NativePointed, value: Long)
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_READ_PRIMITIVE) external fun getFloat(mem: NativePointed): Float
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_WRITE_PRIMITIVE) external fun putFloat(mem: NativePointed, value: Float)
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_READ_PRIMITIVE) external fun getDouble(mem: NativePointed): Double
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_WRITE_PRIMITIVE) external fun putDouble(mem: NativePointed, value: Double)
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_READ_PRIMITIVE) external fun getNativePtr(mem: NativePointed): NativePtr
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_WRITE_PRIMITIVE) external fun putNativePtr(mem: NativePointed, value: NativePtr)
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_READ_PRIMITIVE) external fun getVector(mem: NativePointed): Vector128
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_WRITE_PRIMITIVE) external fun putVector(mem: NativePointed, value: Vector128)
|
||||
|
||||
// TODO: optimize
|
||||
fun getByteArray(source: NativePointed, dest: ByteArray, length: Int) {
|
||||
val sourceArray = source.reinterpret<ByteVar>().ptr
|
||||
var index = 0
|
||||
while (index < length) {
|
||||
dest[index] = sourceArray[index]
|
||||
++index
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: optimize
|
||||
fun putByteArray(source: ByteArray, dest: NativePointed, length: Int) {
|
||||
val destArray = dest.reinterpret<ByteVar>().ptr
|
||||
var index = 0
|
||||
while (index < length) {
|
||||
destArray[index] = source[index]
|
||||
++index
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: optimize
|
||||
fun getCharArray(source: NativePointed, dest: CharArray, length: Int) {
|
||||
val sourceArray = source.reinterpret<ShortVar>().ptr
|
||||
var index = 0
|
||||
while (index < length) {
|
||||
dest[index] = sourceArray[index].toChar()
|
||||
++index
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: optimize
|
||||
fun putCharArray(source: CharArray, dest: NativePointed, length: Int) {
|
||||
val destArray = dest.reinterpret<ShortVar>().ptr
|
||||
var index = 0
|
||||
while (index < length) {
|
||||
destArray[index] = source[index].toShort()
|
||||
++index
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: optimize
|
||||
fun zeroMemory(dest: NativePointed, length: Int): Unit {
|
||||
val destArray = dest.reinterpret<ByteVar>().ptr
|
||||
var index = 0
|
||||
while (index < length) {
|
||||
destArray[index] = 0
|
||||
++index
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: optimize
|
||||
fun copyMemory(dest: NativePointed, length: Int, src: NativePointed): Unit {
|
||||
val destArray = dest.reinterpret<ByteVar>().ptr
|
||||
val srcArray = src.reinterpret<ByteVar>().ptr
|
||||
var index = 0
|
||||
while (index < length) {
|
||||
destArray[index] = srcArray[index]
|
||||
++index
|
||||
}
|
||||
}
|
||||
|
||||
fun alloc(size: Long, align: Int): NativePointed {
|
||||
return interpretOpaquePointed(allocRaw(size, align))
|
||||
}
|
||||
|
||||
fun free(mem: NativePtr) {
|
||||
freeRaw(mem)
|
||||
}
|
||||
|
||||
internal fun allocRaw(size: Long, align: Int): NativePtr {
|
||||
val ptr = malloc(size, align)
|
||||
if (ptr == nativeNullPtr) {
|
||||
throw OutOfMemoryError("unable to allocate native memory")
|
||||
}
|
||||
return ptr
|
||||
}
|
||||
|
||||
internal fun freeRaw(mem: NativePtr) {
|
||||
cfree(mem)
|
||||
}
|
||||
}
|
||||
|
||||
public fun CPointer<UShortVar>.toKStringFromUtf16(): String {
|
||||
val nativeBytes = this
|
||||
|
||||
var length = 0
|
||||
while (nativeBytes[length] != 0.toUShort()) {
|
||||
++length
|
||||
}
|
||||
val chars = kotlin.CharArray(length)
|
||||
var index = 0
|
||||
while (index < length) {
|
||||
chars[index] = nativeBytes[index].toShort().toChar()
|
||||
++index
|
||||
}
|
||||
return chars.concatToString()
|
||||
}
|
||||
|
||||
public fun CPointer<ShortVar>.toKString(): String = this.toKStringFromUtf16()
|
||||
|
||||
public fun CPointer<UShortVar>.toKString(): String = this.toKStringFromUtf16()
|
||||
|
||||
@SymbolName("Kotlin_interop_malloc")
|
||||
private external fun malloc(size: Long, align: Int): NativePtr
|
||||
|
||||
@SymbolName("Kotlin_interop_free")
|
||||
private external fun cfree(ptr: NativePtr)
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_READ_BITS)
|
||||
external fun readBits(ptr: NativePtr, offset: Long, size: Int, signed: Boolean): Long
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_WRITE_BITS)
|
||||
external fun writeBits(ptr: NativePtr, offset: Long, size: Int, value: Long)
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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 kotlinx.cinterop
|
||||
import kotlin.native.*
|
||||
|
||||
@SymbolName("Kotlin_Interop_createStablePointer")
|
||||
internal external fun createStablePointer(any: Any): COpaquePointer
|
||||
|
||||
@SymbolName("Kotlin_Interop_disposeStablePointer")
|
||||
internal external fun disposeStablePointer(pointer: COpaquePointer)
|
||||
|
||||
@PublishedApi
|
||||
@SymbolName("Kotlin_Interop_derefStablePointer")
|
||||
internal external fun derefStablePointer(pointer: COpaquePointer): Any
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* 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 kotlinx.cinterop
|
||||
|
||||
import kotlin.native.internal.getNativeNullPtr
|
||||
import kotlin.native.internal.reinterpret
|
||||
import kotlin.native.internal.Intrinsic
|
||||
import kotlin.native.internal.VolatileLambda
|
||||
import kotlin.native.internal.TypedIntrinsic
|
||||
import kotlin.native.internal.IntrinsicType
|
||||
|
||||
typealias NativePtr = kotlin.native.internal.NativePtr
|
||||
internal typealias NonNullNativePtr = kotlin.native.internal.NonNullNativePtr
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
internal inline fun NativePtr.toNonNull() = this.reinterpret<NativePtr, NonNullNativePtr>()
|
||||
|
||||
inline val nativeNullPtr: NativePtr
|
||||
get() = getNativeNullPtr()
|
||||
|
||||
@Deprecated("Use sizeOf<T>() or alignOf<T>() instead.")
|
||||
@Suppress("DEPRECATION")
|
||||
fun <T : CVariable> typeOf(): CVariable.Type = throw Error("typeOf() is called with erased argument")
|
||||
|
||||
/**
|
||||
* Performs type cast of the native pointer to given interop type, including null values.
|
||||
*
|
||||
* @param T must not be abstract
|
||||
*/
|
||||
@TypedIntrinsic(IntrinsicType.IDENTITY)
|
||||
external fun <T : NativePointed> interpretNullablePointed(ptr: NativePtr): T?
|
||||
|
||||
/**
|
||||
* Performs type cast of the [CPointer] from the given raw pointer.
|
||||
*/
|
||||
@TypedIntrinsic(IntrinsicType.IDENTITY)
|
||||
external fun <T : CPointed> interpretCPointer(rawValue: NativePtr): CPointer<T>?
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.IDENTITY)
|
||||
external fun NativePointed.getRawPointer(): NativePtr
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.IDENTITY)
|
||||
external fun CPointer<*>.getRawValue(): NativePtr
|
||||
|
||||
internal fun CPointer<*>.cPointerToString() = "CPointer(raw=$rawValue)"
|
||||
|
||||
public class Vector128VarOf<T : Vector128>(rawPtr: NativePtr) : CVariable(rawPtr) {
|
||||
@Deprecated("Use sizeOf<T>() or alignOf<T>() instead.")
|
||||
@Suppress("DEPRECATION")
|
||||
companion object : Type(size = 16, align = 16)
|
||||
}
|
||||
|
||||
public typealias Vector128Var = Vector128VarOf<Vector128>
|
||||
|
||||
public var <T : Vector128> Vector128VarOf<T>.value: T
|
||||
get() = nativeMemUtils.getVector(this) as T
|
||||
set(value) = nativeMemUtils.putVector(this, value)
|
||||
|
||||
/**
|
||||
* Returns a pointer to C function which calls given Kotlin *static* function.
|
||||
*
|
||||
* @param function must be *static*, i.e. an (unbound) reference to a Kotlin function or
|
||||
* a closure which doesn't capture any variable
|
||||
*/
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_STATIC_C_FUNCTION) external fun <R> staticCFunction(@VolatileLambda function: () -> R): CPointer<CFunction<() -> R>>
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_STATIC_C_FUNCTION) external fun <P1, R> staticCFunction(@VolatileLambda function: (P1) -> R): CPointer<CFunction<(P1) -> R>>
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_STATIC_C_FUNCTION) external fun <P1, P2, R> staticCFunction(@VolatileLambda function: (P1, P2) -> R): CPointer<CFunction<(P1, P2) -> R>>
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_STATIC_C_FUNCTION) external fun <P1, P2, P3, R> staticCFunction(@VolatileLambda function: (P1, P2, P3) -> R): CPointer<CFunction<(P1, P2, P3) -> R>>
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_STATIC_C_FUNCTION) external fun <P1, P2, P3, P4, R> staticCFunction(@VolatileLambda function: (P1, P2, P3, P4) -> R): CPointer<CFunction<(P1, P2, P3, P4) -> R>>
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_STATIC_C_FUNCTION) external fun <P1, P2, P3, P4, P5, R> staticCFunction(@VolatileLambda function: (P1, P2, P3, P4, P5) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5) -> R>>
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_STATIC_C_FUNCTION) external fun <P1, P2, P3, P4, P5, P6, R> staticCFunction(@VolatileLambda function: (P1, P2, P3, P4, P5, P6) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6) -> R>>
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_STATIC_C_FUNCTION) external fun <P1, P2, P3, P4, P5, P6, P7, R> staticCFunction(@VolatileLambda function: (P1, P2, P3, P4, P5, P6, P7) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7) -> R>>
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_STATIC_C_FUNCTION) external fun <P1, P2, P3, P4, P5, P6, P7, P8, R> staticCFunction(@VolatileLambda function: (P1, P2, P3, P4, P5, P6, P7, P8) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8) -> R>>
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_STATIC_C_FUNCTION) external fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, R> staticCFunction(@VolatileLambda function: (P1, P2, P3, P4, P5, P6, P7, P8, P9) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9) -> R>>
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_STATIC_C_FUNCTION) external fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, R> staticCFunction(@VolatileLambda function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) -> R>>
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_STATIC_C_FUNCTION) external fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, R> staticCFunction(@VolatileLambda function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11) -> R>>
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_STATIC_C_FUNCTION) external fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, R> staticCFunction(@VolatileLambda function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12) -> R>>
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_STATIC_C_FUNCTION) external fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, R> staticCFunction(@VolatileLambda function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13) -> R>>
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_STATIC_C_FUNCTION) external fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, R> staticCFunction(@VolatileLambda function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14) -> R>>
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_STATIC_C_FUNCTION) external fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, R> staticCFunction(@VolatileLambda function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15) -> R>>
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_STATIC_C_FUNCTION) external fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, R> staticCFunction(@VolatileLambda function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16) -> R>>
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_STATIC_C_FUNCTION) external fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, R> staticCFunction(@VolatileLambda function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17) -> R>>
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_STATIC_C_FUNCTION) external fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, R> staticCFunction(@VolatileLambda function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18) -> R>>
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_STATIC_C_FUNCTION) external fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, R> staticCFunction(@VolatileLambda function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19) -> R>>
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_STATIC_C_FUNCTION) external fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, R> staticCFunction(@VolatileLambda function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20) -> R>>
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_STATIC_C_FUNCTION) external fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, R> staticCFunction(@VolatileLambda function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21) -> R>>
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_STATIC_C_FUNCTION) external fun <P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22, R> staticCFunction(@VolatileLambda function: (P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) -> R): CPointer<CFunction<(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15, P16, P17, P18, P19, P20, P21, P22) -> R>>
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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 kotlinx.cinterop
|
||||
|
||||
import kotlin.native.internal.Intrinsic
|
||||
import kotlin.native.internal.TypedIntrinsic
|
||||
import kotlin.native.internal.IntrinsicType
|
||||
|
||||
internal fun encodeToUtf8(str: String): ByteArray = str.encodeToByteArray()
|
||||
|
||||
@SymbolName("Kotlin_CString_toKStringFromUtf8Impl")
|
||||
internal external fun CPointer<ByteVar>.toKStringFromUtf8Impl(): String
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_BITS_TO_FLOAT)
|
||||
external fun bitsToFloat(bits: Int): Float
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_BITS_TO_DOUBLE)
|
||||
external fun bitsToDouble(bits: Long): Double
|
||||
|
||||
// TODO: deprecate.
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_SIGN_EXTEND)
|
||||
external inline fun <reified R : Number> Number.signExtend(): R
|
||||
|
||||
// TODO: deprecate.
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_NARROW)
|
||||
external inline fun <reified R : Number> Number.narrow(): R
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_CONVERT) external inline fun <reified R : Any> Byte.convert(): R
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_CONVERT) external inline fun <reified R : Any> Short.convert(): R
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_CONVERT) external inline fun <reified R : Any> Int.convert(): R
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_CONVERT) external inline fun <reified R : Any> Long.convert(): R
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_CONVERT) external inline fun <reified R : Any> UByte.convert(): R
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_CONVERT) external inline fun <reified R : Any> UShort.convert(): R
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_CONVERT) external inline fun <reified R : Any> UInt.convert(): R
|
||||
@TypedIntrinsic(IntrinsicType.INTEROP_CONVERT) external inline fun <reified R : Any> ULong.convert(): R
|
||||
|
||||
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER, AnnotationTarget.FILE)
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
internal annotation class JvmName(val name: String)
|
||||
|
||||
fun cValuesOf(vararg elements: UByte): CValues<UByteVar> =
|
||||
createValues(elements.size) { index -> this.value = elements[index] }
|
||||
|
||||
fun cValuesOf(vararg elements: UShort): CValues<UShortVar> =
|
||||
createValues(elements.size) { index -> this.value = elements[index] }
|
||||
|
||||
fun cValuesOf(vararg elements: UInt): CValues<UIntVar> =
|
||||
createValues(elements.size) { index -> this.value = elements[index] }
|
||||
|
||||
fun cValuesOf(vararg elements: ULong): CValues<ULongVar> =
|
||||
createValues(elements.size) { index -> this.value = elements[index] }
|
||||
|
||||
fun UByteArray.toCValues() = cValuesOf(*this)
|
||||
fun UShortArray.toCValues() = cValuesOf(*this)
|
||||
fun UIntArray.toCValues() = cValuesOf(*this)
|
||||
fun ULongArray.toCValues() = cValuesOf(*this)
|
||||
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@file:Suppress("NOTHING_TO_INLINE")
|
||||
|
||||
package kotlinx.cinterop
|
||||
import kotlin.native.*
|
||||
import kotlin.native.internal.ExportTypeInfo
|
||||
import kotlin.native.internal.ExportForCppRuntime
|
||||
import kotlin.native.internal.TypedIntrinsic
|
||||
import kotlin.native.internal.IntrinsicType
|
||||
import kotlin.native.internal.FilterExceptions
|
||||
|
||||
interface ObjCObject
|
||||
interface ObjCClass : ObjCObject
|
||||
interface ObjCClassOf<T : ObjCObject> : ObjCClass // TODO: T should be added to ObjCClass and all meta-classes instead.
|
||||
typealias ObjCObjectMeta = ObjCClass
|
||||
|
||||
interface ObjCProtocol : ObjCObject
|
||||
|
||||
@ExportTypeInfo("theForeignObjCObjectTypeInfo")
|
||||
@kotlin.native.internal.Frozen
|
||||
internal open class ForeignObjCObject : kotlin.native.internal.ObjCObjectWrapper
|
||||
|
||||
abstract class ObjCObjectBase protected constructor() : ObjCObject {
|
||||
@Target(AnnotationTarget.CONSTRUCTOR)
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
annotation class OverrideInit
|
||||
}
|
||||
abstract class ObjCObjectBaseMeta protected constructor() : ObjCObjectBase(), ObjCObjectMeta {}
|
||||
|
||||
fun optional(): Nothing = throw RuntimeException("Do not call me!!!")
|
||||
|
||||
@Deprecated(
|
||||
"Add @OverrideInit to constructor to make it override Objective-C initializer",
|
||||
level = DeprecationLevel.ERROR
|
||||
)
|
||||
@TypedIntrinsic(IntrinsicType.OBJC_INIT_BY)
|
||||
external fun <T : ObjCObjectBase> T.initBy(constructorCall: T): T
|
||||
|
||||
@kotlin.native.internal.ExportForCompiler
|
||||
private fun ObjCObjectBase.superInitCheck(superInitCallResult: ObjCObject?) {
|
||||
if (superInitCallResult == null)
|
||||
throw RuntimeException("Super initialization failed")
|
||||
|
||||
if (superInitCallResult.objcPtr() != this.objcPtr())
|
||||
throw UnsupportedOperationException("Super initializer has replaced object")
|
||||
}
|
||||
|
||||
internal fun <T : Any?> Any?.uncheckedCast(): T = @Suppress("UNCHECKED_CAST") (this as T)
|
||||
|
||||
// Note: if this is called for non-frozen object on a wrong worker, the program will terminate.
|
||||
@SymbolName("Kotlin_Interop_refFromObjC")
|
||||
external fun <T> interpretObjCPointerOrNull(objcPtr: NativePtr): T?
|
||||
|
||||
@ExportForCppRuntime
|
||||
inline fun <T : Any> interpretObjCPointer(objcPtr: NativePtr): T = interpretObjCPointerOrNull<T>(objcPtr)!!
|
||||
|
||||
@SymbolName("Kotlin_Interop_refToObjC")
|
||||
external fun Any?.objcPtr(): NativePtr
|
||||
|
||||
@SymbolName("Kotlin_Interop_createKotlinObjectHolder")
|
||||
external fun createKotlinObjectHolder(any: Any?): NativePtr
|
||||
|
||||
// Note: if this is called for non-frozen underlying ref on a wrong worker, the program will terminate.
|
||||
inline fun <reified T : Any> unwrapKotlinObjectHolder(holder: Any?): T {
|
||||
return unwrapKotlinObjectHolderImpl(holder!!.objcPtr()) as T
|
||||
}
|
||||
|
||||
@PublishedApi
|
||||
@SymbolName("Kotlin_Interop_unwrapKotlinObjectHolder")
|
||||
external internal fun unwrapKotlinObjectHolderImpl(ptr: NativePtr): Any
|
||||
|
||||
class ObjCObjectVar<T>(rawPtr: NativePtr) : CVariable(rawPtr) {
|
||||
@Deprecated("Use sizeOf<T>() or alignOf<T>() instead.")
|
||||
@Suppress("DEPRECATION")
|
||||
companion object : CVariable.Type(pointerSize.toLong(), pointerSize)
|
||||
}
|
||||
|
||||
class ObjCNotImplementedVar<T : Any?>(rawPtr: NativePtr) : CVariable(rawPtr) {
|
||||
@Deprecated("Use sizeOf<T>() or alignOf<T>() instead.")
|
||||
@Suppress("DEPRECATION")
|
||||
companion object : CVariable.Type(pointerSize.toLong(), pointerSize)
|
||||
}
|
||||
|
||||
var <T : Any?> ObjCNotImplementedVar<T>.value: T
|
||||
get() = TODO()
|
||||
set(_) = TODO()
|
||||
|
||||
typealias ObjCStringVarOf<T> = ObjCNotImplementedVar<T>
|
||||
typealias ObjCBlockVar<T> = ObjCNotImplementedVar<T>
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.OBJC_CREATE_SUPER_STRUCT)
|
||||
@PublishedApi
|
||||
internal external fun createObjCSuperStruct(receiver: NativePtr, superClass: NativePtr): NativePtr
|
||||
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class ExternalObjCClass(val protocolGetter: String = "", val binaryName: String = "")
|
||||
|
||||
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class ObjCMethod(val selector: String, val encoding: String, val isStret: Boolean = false)
|
||||
|
||||
@Target(AnnotationTarget.CONSTRUCTOR)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class ObjCConstructor(val initSelector: String, val designated: Boolean)
|
||||
|
||||
@Target(AnnotationTarget.FUNCTION)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class ObjCFactory(val selector: String, val encoding: String, val isStret: Boolean = false)
|
||||
|
||||
@Target(AnnotationTarget.FILE)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class InteropStubs()
|
||||
|
||||
@PublishedApi
|
||||
@Target(AnnotationTarget.FUNCTION)
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
internal annotation class ObjCMethodImp(val selector: String, val encoding: String)
|
||||
|
||||
@PublishedApi
|
||||
@TypedIntrinsic(IntrinsicType.OBJC_GET_SELECTOR)
|
||||
internal external fun objCGetSelector(selector: String): COpaquePointer
|
||||
|
||||
@kotlin.native.internal.ExportForCppRuntime("Kotlin_Interop_getObjCClass")
|
||||
private fun getObjCClassByName(name: NativePtr): NativePtr {
|
||||
val result = objc_lookUpClass(name)
|
||||
if (result == nativeNullPtr) {
|
||||
val className = interpretCPointer<ByteVar>(name)!!.toKString()
|
||||
val message = """Objective-C class '$className' not found.
|
||||
|Ensure that the containing framework or library was linked.""".trimMargin()
|
||||
|
||||
throw RuntimeException(message)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@kotlin.native.internal.ExportForCompiler
|
||||
private fun allocObjCObject(clazz: NativePtr): NativePtr {
|
||||
val rawResult = objc_allocWithZone(clazz)
|
||||
if (rawResult == nativeNullPtr) {
|
||||
throw OutOfMemoryError("Unable to allocate Objective-C object")
|
||||
}
|
||||
|
||||
// Note: `objc_allocWithZone` returns retained pointer, and thus it must be balanced by the caller.
|
||||
|
||||
return rawResult
|
||||
}
|
||||
|
||||
@TypedIntrinsic(IntrinsicType.OBJC_GET_OBJC_CLASS)
|
||||
@kotlin.native.internal.ExportForCompiler
|
||||
private external fun <T : ObjCObject> getObjCClass(): NativePtr
|
||||
|
||||
@PublishedApi
|
||||
@TypedIntrinsic(IntrinsicType.OBJC_GET_MESSENGER)
|
||||
internal external fun getMessenger(superClass: NativePtr): COpaquePointer?
|
||||
|
||||
@PublishedApi
|
||||
@TypedIntrinsic(IntrinsicType.OBJC_GET_MESSENGER_STRET)
|
||||
internal external fun getMessengerStret(superClass: NativePtr): COpaquePointer?
|
||||
|
||||
|
||||
internal class ObjCWeakReferenceImpl : kotlin.native.ref.WeakReferenceImpl() {
|
||||
@SymbolName("Konan_ObjCInterop_getWeakReference")
|
||||
external override fun get(): Any?
|
||||
}
|
||||
|
||||
@SymbolName("Konan_ObjCInterop_initWeakReference")
|
||||
private external fun ObjCWeakReferenceImpl.init(objcPtr: NativePtr)
|
||||
|
||||
@kotlin.native.internal.ExportForCppRuntime internal fun makeObjCWeakReferenceImpl(objcPtr: NativePtr): ObjCWeakReferenceImpl {
|
||||
val result = ObjCWeakReferenceImpl()
|
||||
result.init(objcPtr)
|
||||
return result
|
||||
}
|
||||
|
||||
// Konan runtme:
|
||||
|
||||
@Deprecated("Use plain Kotlin cast of String to NSString", level = DeprecationLevel.ERROR)
|
||||
@SymbolName("Kotlin_Interop_CreateNSStringFromKString")
|
||||
external fun CreateNSStringFromKString(str: String?): NativePtr
|
||||
|
||||
@Deprecated("Use plain Kotlin cast of NSString to String", level = DeprecationLevel.ERROR)
|
||||
@SymbolName("Kotlin_Interop_CreateKStringFromNSString")
|
||||
external fun CreateKStringFromNSString(ptr: NativePtr): String?
|
||||
|
||||
@PublishedApi
|
||||
@SymbolName("Kotlin_Interop_CreateObjCObjectHolder")
|
||||
internal external fun createObjCObjectHolder(ptr: NativePtr): Any?
|
||||
|
||||
// Objective-C runtime:
|
||||
|
||||
@SymbolName("objc_retainAutoreleaseReturnValue")
|
||||
external fun objc_retainAutoreleaseReturnValue(ptr: NativePtr): NativePtr
|
||||
|
||||
@SymbolName("Kotlin_objc_autoreleasePoolPush")
|
||||
external fun objc_autoreleasePoolPush(): NativePtr
|
||||
|
||||
@SymbolName("Kotlin_objc_autoreleasePoolPop")
|
||||
external fun objc_autoreleasePoolPop(ptr: NativePtr)
|
||||
|
||||
@SymbolName("Kotlin_objc_allocWithZone")
|
||||
@FilterExceptions
|
||||
private external fun objc_allocWithZone(clazz: NativePtr): NativePtr
|
||||
|
||||
@SymbolName("Kotlin_objc_retain")
|
||||
external fun objc_retain(ptr: NativePtr): NativePtr
|
||||
|
||||
@SymbolName("Kotlin_objc_release")
|
||||
external fun objc_release(ptr: NativePtr)
|
||||
|
||||
@SymbolName("Kotlin_objc_lookUpClass")
|
||||
external fun objc_lookUpClass(name: NativePtr): NativePtr
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
package kotlinx.cinterop
|
||||
|
||||
import kotlin.native.internal.KClassImpl
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
* If [objCClass] is a class generated to Objective-C header for Kotlin class,
|
||||
* returns [KClass] for that original Kotlin class.
|
||||
*
|
||||
* Otherwise returns `null`.
|
||||
*/
|
||||
fun getOriginalKotlinClass(objCClass: ObjCClass): KClass<*>? {
|
||||
val typeInfo = getTypeInfoForClass(objCClass.objcPtr())
|
||||
if (typeInfo.isNull()) return null
|
||||
|
||||
return KClassImpl<Any>(typeInfo)
|
||||
}
|
||||
|
||||
/**
|
||||
* If [objCProtocol] is a protocol generated to Objective-C header for Kotlin class,
|
||||
* returns [KClass] for that original Kotlin class.
|
||||
*
|
||||
* Otherwise returns `null`.
|
||||
*/
|
||||
fun getOriginalKotlinClass(objCProtocol: ObjCProtocol): KClass<*>? {
|
||||
val typeInfo = getTypeInfoForProtocol(objCProtocol.objcPtr())
|
||||
if (typeInfo.isNull()) return null
|
||||
|
||||
return KClassImpl<Any>(typeInfo)
|
||||
}
|
||||
|
||||
@SymbolName("Kotlin_ObjCInterop_getTypeInfoForClass")
|
||||
private external fun getTypeInfoForClass(ptr: NativePtr): NativePtr
|
||||
|
||||
@SymbolName("Kotlin_ObjCInterop_getTypeInfoForProtocol")
|
||||
private external fun getTypeInfoForProtocol(ptr: NativePtr): NativePtr
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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 kotlinx.cinterop
|
||||
|
||||
inline fun <R> autoreleasepool(block: () -> R): R {
|
||||
val pool = objc_autoreleasePoolPush()
|
||||
return try {
|
||||
block()
|
||||
} finally {
|
||||
objc_autoreleasePoolPop(pool)
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated("Use plain Kotlin cast", ReplaceWith("this as T"), DeprecationLevel.ERROR)
|
||||
fun <T : ObjCObject> ObjCObject.reinterpret() = @Suppress("DEPRECATION") this.uncheckedCast<T>()
|
||||
|
||||
// TODO: null checks
|
||||
var <T> ObjCObjectVar<T>.value: T
|
||||
@Suppress("DEPRECATION") get() =
|
||||
interpretObjCPointerOrNull<T>(nativeMemUtils.getNativePtr(this)).uncheckedCast<T>()
|
||||
|
||||
set(value) = nativeMemUtils.putNativePtr(this, value.objcPtr())
|
||||
|
||||
/**
|
||||
* Makes Kotlin method in Objective-C class accessible through Objective-C dispatch
|
||||
* to be used as action sent by control in UIKit or AppKit.
|
||||
*/
|
||||
@Target(AnnotationTarget.FUNCTION)
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
annotation class ObjCAction
|
||||
|
||||
/**
|
||||
* Makes Kotlin property in Objective-C class settable through Objective-C dispatch
|
||||
* to be used as IB outlet.
|
||||
*/
|
||||
@Target(AnnotationTarget.PROPERTY)
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
annotation class ObjCOutlet
|
||||
|
||||
/**
|
||||
* Makes Kotlin subclass of Objective-C class visible for runtime lookup
|
||||
* after Kotlin `main` function gets invoked.
|
||||
*
|
||||
* Note: runtime lookup can be forced even when the class is referenced statically from
|
||||
* Objective-C source code by adding `__attribute__((objc_runtime_visible))` to its `@interface`.
|
||||
*/
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
annotation class ExportObjCClass(val name: String = "")
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* 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 kotlinx.cinterop
|
||||
import kotlin.native.*
|
||||
|
||||
data class Pinned<out T : Any> internal constructor(private val stablePtr: COpaquePointer) {
|
||||
|
||||
/**
|
||||
* Disposes the handle. It must not be [used][get] after that.
|
||||
*/
|
||||
fun unpin() {
|
||||
disposeStablePointer(this.stablePtr)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying pinned object.
|
||||
*/
|
||||
fun get(): T = @Suppress("UNCHECKED_CAST") (derefStablePointer(stablePtr) as T)
|
||||
|
||||
}
|
||||
|
||||
fun <T : Any> T.pin() = Pinned<T>(createStablePointer(this))
|
||||
|
||||
inline fun <T : Any, R> T.usePinned(block: (Pinned<T>) -> R): R {
|
||||
val pinned = this.pin()
|
||||
return try {
|
||||
block(pinned)
|
||||
} finally {
|
||||
pinned.unpin()
|
||||
}
|
||||
}
|
||||
|
||||
fun Pinned<ByteArray>.addressOf(index: Int): CPointer<ByteVar> = this.get().addressOfElement(index)
|
||||
fun ByteArray.refTo(index: Int): CValuesRef<ByteVar> = this.usingPinned { addressOf(index) }
|
||||
|
||||
fun Pinned<String>.addressOf(index: Int): CPointer<COpaque> = this.get().addressOfElement(index)
|
||||
fun String.refTo(index: Int): CValuesRef<COpaque> = this.usingPinned { addressOf(index) }
|
||||
|
||||
fun Pinned<CharArray>.addressOf(index: Int): CPointer<COpaque> = this.get().addressOfElement(index)
|
||||
fun CharArray.refTo(index: Int): CValuesRef<COpaque> = this.usingPinned { addressOf(index) }
|
||||
|
||||
fun Pinned<ShortArray>.addressOf(index: Int): CPointer<ShortVar> = this.get().addressOfElement(index)
|
||||
fun ShortArray.refTo(index: Int): CValuesRef<ShortVar> = this.usingPinned { addressOf(index) }
|
||||
|
||||
fun Pinned<IntArray>.addressOf(index: Int): CPointer<IntVar> = this.get().addressOfElement(index)
|
||||
fun IntArray.refTo(index: Int): CValuesRef<IntVar> = this.usingPinned { addressOf(index) }
|
||||
|
||||
fun Pinned<LongArray>.addressOf(index: Int): CPointer<LongVar> = this.get().addressOfElement(index)
|
||||
fun LongArray.refTo(index: Int): CValuesRef<LongVar> = this.usingPinned { addressOf(index) }
|
||||
|
||||
// TODO: pinning of unsigned arrays involves boxing as they are inline classes wrapping signed arrays.
|
||||
fun Pinned<UByteArray>.addressOf(index: Int): CPointer<UByteVar> = this.get().addressOfElement(index)
|
||||
fun UByteArray.refTo(index: Int): CValuesRef<UByteVar> = this.usingPinned { addressOf(index) }
|
||||
|
||||
fun Pinned<UShortArray>.addressOf(index: Int): CPointer<UShortVar> = this.get().addressOfElement(index)
|
||||
fun UShortArray.refTo(index: Int): CValuesRef<UShortVar> = this.usingPinned { addressOf(index) }
|
||||
|
||||
fun Pinned<UIntArray>.addressOf(index: Int): CPointer<UIntVar> = this.get().addressOfElement(index)
|
||||
fun UIntArray.refTo(index: Int): CValuesRef<UIntVar> = this.usingPinned { addressOf(index) }
|
||||
|
||||
fun Pinned<ULongArray>.addressOf(index: Int): CPointer<ULongVar> = this.get().addressOfElement(index)
|
||||
fun ULongArray.refTo(index: Int): CValuesRef<ULongVar> = this.usingPinned { addressOf(index) }
|
||||
|
||||
fun Pinned<FloatArray>.addressOf(index: Int): CPointer<FloatVar> = this.get().addressOfElement(index)
|
||||
fun FloatArray.refTo(index: Int): CValuesRef<FloatVar> = this.usingPinned { addressOf(index) }
|
||||
|
||||
fun Pinned<DoubleArray>.addressOf(index: Int): CPointer<DoubleVar> = this.get().addressOfElement(index)
|
||||
fun DoubleArray.refTo(index: Int): CValuesRef<DoubleVar> = this.usingPinned { addressOf(index) }
|
||||
|
||||
private inline fun <T : Any, P : CPointed> T.usingPinned(
|
||||
crossinline block: Pinned<T>.() -> CPointer<P>
|
||||
) = object : CValuesRef<P>() {
|
||||
|
||||
override fun getPointer(scope: AutofreeScope): CPointer<P> {
|
||||
val pinned = this@usingPinned.pin()
|
||||
scope.defer { pinned.unpin() }
|
||||
return pinned.block()
|
||||
}
|
||||
}
|
||||
|
||||
@SymbolName("Kotlin_Arrays_getByteArrayAddressOfElement")
|
||||
private external fun ByteArray.addressOfElement(index: Int): CPointer<ByteVar>
|
||||
|
||||
@SymbolName("Kotlin_Arrays_getStringAddressOfElement")
|
||||
private external fun String.addressOfElement(index: Int): CPointer<COpaque>
|
||||
|
||||
@SymbolName("Kotlin_Arrays_getCharArrayAddressOfElement")
|
||||
private external fun CharArray.addressOfElement(index: Int): CPointer<COpaque>
|
||||
|
||||
@SymbolName("Kotlin_Arrays_getShortArrayAddressOfElement")
|
||||
private external fun ShortArray.addressOfElement(index: Int): CPointer<ShortVar>
|
||||
|
||||
@SymbolName("Kotlin_Arrays_getIntArrayAddressOfElement")
|
||||
private external fun IntArray.addressOfElement(index: Int): CPointer<IntVar>
|
||||
|
||||
@SymbolName("Kotlin_Arrays_getLongArrayAddressOfElement")
|
||||
private external fun LongArray.addressOfElement(index: Int): CPointer<LongVar>
|
||||
|
||||
@SymbolName("Kotlin_Arrays_getByteArrayAddressOfElement")
|
||||
private external fun UByteArray.addressOfElement(index: Int): CPointer<UByteVar>
|
||||
|
||||
@SymbolName("Kotlin_Arrays_getShortArrayAddressOfElement")
|
||||
private external fun UShortArray.addressOfElement(index: Int): CPointer<UShortVar>
|
||||
|
||||
@SymbolName("Kotlin_Arrays_getIntArrayAddressOfElement")
|
||||
private external fun UIntArray.addressOfElement(index: Int): CPointer<UIntVar>
|
||||
|
||||
@SymbolName("Kotlin_Arrays_getLongArrayAddressOfElement")
|
||||
private external fun ULongArray.addressOfElement(index: Int): CPointer<ULongVar>
|
||||
|
||||
@SymbolName("Kotlin_Arrays_getFloatArrayAddressOfElement")
|
||||
private external fun FloatArray.addressOfElement(index: Int): CPointer<FloatVar>
|
||||
|
||||
@SymbolName("Kotlin_Arrays_getDoubleArrayAddressOfElement")
|
||||
private external fun DoubleArray.addressOfElement(index: Int): CPointer<DoubleVar>
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
package kotlinx.cinterop.internal
|
||||
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class CStruct(val spelling: String) {
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
@Target(
|
||||
AnnotationTarget.PROPERTY_GETTER,
|
||||
AnnotationTarget.PROPERTY_SETTER
|
||||
)
|
||||
annotation class MemberAt(val offset: Long)
|
||||
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
@Target(AnnotationTarget.PROPERTY_GETTER)
|
||||
annotation class ArrayMemberAt(val offset: Long)
|
||||
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
@Target(
|
||||
AnnotationTarget.PROPERTY_GETTER,
|
||||
AnnotationTarget.PROPERTY_SETTER
|
||||
)
|
||||
annotation class BitField(val offset: Long, val size: Int)
|
||||
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class VarType(val size: Long, val align: Int)
|
||||
}
|
||||
|
||||
@Target(
|
||||
AnnotationTarget.FUNCTION,
|
||||
AnnotationTarget.PROPERTY_GETTER,
|
||||
AnnotationTarget.PROPERTY_SETTER
|
||||
)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
public annotation class CCall(val id: String) {
|
||||
@Target(AnnotationTarget.VALUE_PARAMETER)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class CString
|
||||
|
||||
@Target(AnnotationTarget.VALUE_PARAMETER)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class WCString
|
||||
|
||||
@Target(AnnotationTarget.FUNCTION)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class ReturnsRetained
|
||||
|
||||
@Target(AnnotationTarget.FUNCTION)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class ConsumesReceiver
|
||||
|
||||
@Target(AnnotationTarget.VALUE_PARAMETER)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class Consumed
|
||||
}
|
||||
|
||||
/**
|
||||
* Collection of annotations that allow to store
|
||||
* constant values.
|
||||
*/
|
||||
public object ConstantValue {
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class Byte(val value: kotlin.Byte)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class Short(val value: kotlin.Short)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class Int(val value: kotlin.Int)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class Long(val value: kotlin.Long)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class UByte(val value: kotlin.UByte)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class UShort(val value: kotlin.UShort)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class UInt(val value: kotlin.UInt)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class ULong(val value: kotlin.ULong)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class Float(val value: kotlin.Float)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class Double(val value: kotlin.Double)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class String(val value: kotlin.String)
|
||||
}
|
||||
|
||||
/**
|
||||
* Denotes property that is an alias to some enum entry.
|
||||
*/
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
public annotation class CEnumEntryAlias(val entryName: String)
|
||||
|
||||
/**
|
||||
* Stores instance size of the type T: CEnumVar.
|
||||
*/
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
public annotation class CEnumVarTypeSize(val size: Int)
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
buildscript {
|
||||
apply from: "$rootDir/kotlin-native/gradle/kotlinGradlePlugin.gradle"
|
||||
}
|
||||
|
||||
apply plugin: 'kotlin'
|
||||
apply plugin: 'application'
|
||||
|
||||
mainClassName = "org.jetbrains.kotlin.native.interop.gen.jvm.MainKt"
|
||||
|
||||
|
||||
|
||||
dependencies {
|
||||
implementation project(":kotlin-native:Interop:Indexer")
|
||||
implementation project(":kotlin-native:utilities:basic-utils")
|
||||
api project(path: ":kotlin-native:endorsedLibraries:kotlinx.cli", configuration: "jvmRuntimeElements")
|
||||
|
||||
api project(":kotlin-stdlib")
|
||||
api project(kotlinCompilerModule)
|
||||
api project(":kotlinx-metadata-klib")
|
||||
api project(":native:kotlin-native-utils")
|
||||
implementation(project(":compiler:util"))
|
||||
implementation(project(":compiler:ir.serialization.common"))
|
||||
|
||||
testImplementation "junit:junit:4.12"
|
||||
testImplementation project(":kotlin-test:kotlin-test-junit")
|
||||
}
|
||||
|
||||
compileKotlin {
|
||||
kotlinOptions {
|
||||
freeCompilerArgs = ['-Xopt-in=kotlin.ExperimentalUnsignedTypes', '-Xskip-metadata-version-check']
|
||||
allWarningsAsErrors=true
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets{
|
||||
main {
|
||||
kotlin {
|
||||
srcDir("../../shared/src/library/kotlin")
|
||||
srcDir("../../shared/src/main/kotlin")
|
||||
srcDir(VersionGeneratorKt.kotlinNativeVersionSrc(project))
|
||||
}
|
||||
}
|
||||
}
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.native.interop.gen
|
||||
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
import org.jetbrains.kotlin.native.interop.indexer.*
|
||||
|
||||
/**
|
||||
* objc_msgSend*_stret functions must be used when return value is returned through memory
|
||||
* pointed by implicit argument, which is passed on the register that would otherwise be used for receiver.
|
||||
*
|
||||
* The entire implementation is just the real ABI approximation which is enough for practical cases.
|
||||
*/
|
||||
internal fun Type.isStret(target: KonanTarget): Boolean {
|
||||
val unwrappedType = this.unwrapTypedefs()
|
||||
val abiInfo: ObjCAbiInfo = when (target) {
|
||||
KonanTarget.IOS_ARM64,
|
||||
KonanTarget.TVOS_ARM64,
|
||||
KonanTarget.MACOS_ARM64 -> DarwinArm64AbiInfo()
|
||||
|
||||
KonanTarget.IOS_X64,
|
||||
KonanTarget.MACOS_X64,
|
||||
KonanTarget.WATCHOS_X64,
|
||||
KonanTarget.TVOS_X64 -> DarwinX64AbiInfo()
|
||||
|
||||
KonanTarget.WATCHOS_X86 -> DarwinX86AbiInfo()
|
||||
|
||||
KonanTarget.IOS_ARM32,
|
||||
KonanTarget.WATCHOS_ARM32 -> DarwinArm32AbiInfo(target)
|
||||
|
||||
else -> error("Cannot generate ObjC stubs for $target.")
|
||||
}
|
||||
return abiInfo.shouldUseStret(unwrappedType)
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides ABI-specific information about target for Objective C interop.
|
||||
*/
|
||||
interface ObjCAbiInfo {
|
||||
fun shouldUseStret(returnType: Type): Boolean
|
||||
}
|
||||
|
||||
class DarwinX64AbiInfo : ObjCAbiInfo {
|
||||
override fun shouldUseStret(returnType: Type): Boolean {
|
||||
return when (returnType) {
|
||||
is RecordType -> returnType.decl.def!!.size > 16 || returnType.hasUnalignedMembers()
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DarwinX86AbiInfo : ObjCAbiInfo {
|
||||
override fun shouldUseStret(returnType: Type): Boolean {
|
||||
// https://github.com/llvm/llvm-project/blob/6c8a34ed9b49704bdd60838143047c62ba9f2502/clang/lib/CodeGen/TargetInfo.cpp#L1243
|
||||
return when (returnType) {
|
||||
is RecordType -> {
|
||||
val size = returnType.decl.def!!.size
|
||||
val canBePassedInRegisters = (size == 1L || size == 2L || size == 4L || size == 8L)
|
||||
return !canBePassedInRegisters
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DarwinArm32AbiInfo(private val target: KonanTarget) : ObjCAbiInfo {
|
||||
override fun shouldUseStret(returnType: Type): Boolean = when (target) {
|
||||
KonanTarget.IOS_ARM32 -> when (returnType) {
|
||||
is RecordType -> !returnType.isIntegerLikeType()
|
||||
else -> false
|
||||
}
|
||||
// 32-bit watchOS uses armv7k which is effectively Cortex-A7 and
|
||||
// uses AAPCS16 VPF.
|
||||
KonanTarget.WATCHOS_ARM32 -> when (returnType) {
|
||||
is RecordType -> {
|
||||
// https://github.com/llvm/llvm-project/blob/6c8a34ed9b49704bdd60838143047c62ba9f2502/clang/lib/CodeGen/TargetInfo.cpp#L6165
|
||||
when {
|
||||
returnType.decl.def!!.size <= 16 -> false
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
else -> error("Unexpected target")
|
||||
}
|
||||
}
|
||||
|
||||
class DarwinArm64AbiInfo : ObjCAbiInfo {
|
||||
override fun shouldUseStret(returnType: Type): Boolean {
|
||||
// On aarch64 stret is never the case, since an implicit argument gets passed on x8.
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
private fun Type.isIntegerLikeType(): Boolean = when (this) {
|
||||
is RecordType -> {
|
||||
val def = this.decl.def
|
||||
if (def == null) {
|
||||
false
|
||||
} else {
|
||||
def.size <= 4 &&
|
||||
def.members.all {
|
||||
when (it) {
|
||||
is BitField -> it.type.isIntegerLikeType()
|
||||
is Field -> it.offset == 0L && it.type.isIntegerLikeType()
|
||||
is IncompleteField -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is ObjCPointer, is PointerType, CharType, is BoolType -> true
|
||||
is IntegerType -> this.size <= 4
|
||||
is Typedef -> this.def.aliased.isIntegerLikeType()
|
||||
is EnumType -> this.def.baseType.isIntegerLikeType()
|
||||
|
||||
else -> false
|
||||
}
|
||||
|
||||
private fun Type.hasUnalignedMembers(): Boolean = when (this) {
|
||||
is Typedef -> this.def.aliased.hasUnalignedMembers()
|
||||
is RecordType -> this.decl.def!!.let { def ->
|
||||
def.fields.any {
|
||||
!it.isAligned ||
|
||||
// Check members of fields too:
|
||||
it.type.hasUnalignedMembers()
|
||||
}
|
||||
}
|
||||
is ArrayType -> this.elemType.hasUnalignedMembers()
|
||||
else -> false
|
||||
|
||||
// TODO: should the recursive checks be made in indexer when computing `hasUnalignedFields`?
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
package org.jetbrains.kotlin.native.interop.gen
|
||||
|
||||
import org.jetbrains.kotlin.native.interop.indexer.FunctionDecl
|
||||
import org.jetbrains.kotlin.native.interop.indexer.GlobalDecl
|
||||
import org.jetbrains.kotlin.native.interop.indexer.VoidType
|
||||
import org.jetbrains.kotlin.native.interop.indexer.unwrapTypedefs
|
||||
|
||||
internal data class CCalleeWrapper(val lines: List<String>)
|
||||
|
||||
/**
|
||||
* Some functions don't have an address (e.g. macros-based or builtins).
|
||||
* To solve this problem we generate a wrapper function.
|
||||
*/
|
||||
internal class CWrappersGenerator(private val context: StubIrContext) {
|
||||
|
||||
private var currentFunctionWrapperId = 0
|
||||
|
||||
private val packageName =
|
||||
context.configuration.pkgName.replace(INVALID_CLANG_IDENTIFIER_REGEX, "_")
|
||||
|
||||
private fun generateFunctionWrapperName(functionName: String): String {
|
||||
return "${packageName}_${functionName}_wrapper${currentFunctionWrapperId++}"
|
||||
}
|
||||
|
||||
private fun bindSymbolToFunction(symbol: String, function: String): List<String> = listOf(
|
||||
"const void* $symbol __asm(${symbol.quoteAsKotlinLiteral()});",
|
||||
"const void* $symbol = &$function;"
|
||||
)
|
||||
|
||||
private data class Parameter(val type: String, val name: String)
|
||||
|
||||
private fun createWrapper(
|
||||
symbolName: String,
|
||||
wrapperName: String,
|
||||
returnType: String,
|
||||
parameters: List<Parameter>,
|
||||
body: String
|
||||
): List<String> = listOf(
|
||||
"__attribute__((always_inline))",
|
||||
"$returnType $wrapperName(${parameters.joinToString { "${it.type} ${it.name}" }}) {",
|
||||
body,
|
||||
"}",
|
||||
*bindSymbolToFunction(symbolName, wrapperName).toTypedArray()
|
||||
)
|
||||
|
||||
fun generateCCalleeWrapper(function: FunctionDecl, symbolName: String): CCalleeWrapper =
|
||||
if (function.isVararg) {
|
||||
CCalleeWrapper(bindSymbolToFunction(symbolName, function.name))
|
||||
} else {
|
||||
val wrapperName = generateFunctionWrapperName(function.name)
|
||||
|
||||
val returnType = function.returnType.getStringRepresentation()
|
||||
val parameters = function.parameters.mapIndexed { index, parameter ->
|
||||
Parameter(parameter.type.getStringRepresentation(), "p$index")
|
||||
}
|
||||
val callExpression = "${function.name}(${parameters.joinToString { it.name }});"
|
||||
val wrapperBody = if (function.returnType.unwrapTypedefs() is VoidType) {
|
||||
callExpression
|
||||
} else {
|
||||
"return $callExpression"
|
||||
}
|
||||
val wrapper = createWrapper(symbolName, wrapperName, returnType, parameters, wrapperBody)
|
||||
CCalleeWrapper(wrapper)
|
||||
}
|
||||
|
||||
fun generateCGlobalGetter(globalDecl: GlobalDecl, symbolName: String): CCalleeWrapper {
|
||||
val wrapperName = generateFunctionWrapperName("${globalDecl.name}_getter")
|
||||
val returnType = globalDecl.type.getStringRepresentation()
|
||||
val wrapperBody = "return ${globalDecl.name};"
|
||||
val wrapper = createWrapper(symbolName, wrapperName, returnType, emptyList(), wrapperBody)
|
||||
return CCalleeWrapper(wrapper)
|
||||
}
|
||||
|
||||
fun generateCGlobalByPointerGetter(globalDecl: GlobalDecl, symbolName: String): CCalleeWrapper {
|
||||
val wrapperName = generateFunctionWrapperName("${globalDecl.name}_getter")
|
||||
val returnType = "void*"
|
||||
val wrapperBody = "return &${globalDecl.name};"
|
||||
val wrapper = createWrapper(symbolName, wrapperName, returnType, emptyList(), wrapperBody)
|
||||
return CCalleeWrapper(wrapper)
|
||||
}
|
||||
|
||||
fun generateCGlobalSetter(globalDecl: GlobalDecl, symbolName: String): CCalleeWrapper {
|
||||
val wrapperName = generateFunctionWrapperName("${globalDecl.name}_setter")
|
||||
val globalType = globalDecl.type.getStringRepresentation()
|
||||
val parameter = Parameter(globalType, "p1")
|
||||
val wrapperBody = "${globalDecl.name} = ${parameter.name};"
|
||||
val wrapper = createWrapper(symbolName, wrapperName, "void", listOf(parameter), wrapperBody)
|
||||
return CCalleeWrapper(wrapper)
|
||||
}
|
||||
}
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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.native.interop.gen
|
||||
|
||||
interface NativeScope {
|
||||
val mappingBridgeGenerator: MappingBridgeGenerator
|
||||
}
|
||||
|
||||
class NativeCodeBuilder(val scope: NativeScope) {
|
||||
val lines = mutableListOf<String>()
|
||||
|
||||
fun out(line: String): Unit {
|
||||
lines.add(line)
|
||||
}
|
||||
}
|
||||
|
||||
inline fun buildNativeCodeLines(scope: NativeScope, block: NativeCodeBuilder.() -> Unit): List<String> {
|
||||
val builder = NativeCodeBuilder(scope)
|
||||
builder.block()
|
||||
return builder.lines
|
||||
}
|
||||
|
||||
private class Block(val nesting: Int, val start: String, val end: String) {
|
||||
val prologue = mutableListOf<String>()
|
||||
val body = mutableListOf<String>()
|
||||
val epilogue = mutableListOf<String>()
|
||||
|
||||
fun indent(line: String) = " ".repeat(nesting) + line
|
||||
fun indentBraces(line: String) = " ".repeat(nesting - 1) + line
|
||||
}
|
||||
|
||||
class KotlinCodeBuilder(val scope: KotlinScope) {
|
||||
|
||||
private val blocks = mutableListOf<Block>()
|
||||
|
||||
init {
|
||||
pushBlock("", "")
|
||||
}
|
||||
|
||||
fun out(line: String) {
|
||||
currentBlock().body += line
|
||||
}
|
||||
|
||||
private var memScoped = false
|
||||
fun pushMemScoped() {
|
||||
if (!memScoped) {
|
||||
memScoped = true
|
||||
pushBlock("memScoped {")
|
||||
}
|
||||
}
|
||||
|
||||
fun getNativePointer(name: String): String {
|
||||
return "$name?.getPointer(memScope)"
|
||||
}
|
||||
|
||||
fun returnResult(result: String) {
|
||||
currentBlock().body += "return $result"
|
||||
}
|
||||
|
||||
private fun currentBlock() = blocks.last()
|
||||
|
||||
fun pushBlock(start: String, end: String = "}") {
|
||||
val block = Block(blocks.size, start = start, end = end)
|
||||
blocks += block
|
||||
}
|
||||
|
||||
private fun emitBlockAndNested(position: Int, lines: MutableList<String>) {
|
||||
if (position >= blocks.size) return
|
||||
val block = blocks[position]
|
||||
if (block.start.isNotEmpty()) lines += block.indentBraces(block.start)
|
||||
lines += block.prologue.map { block.indent(it) }
|
||||
lines += block.body.map { block.indent(it) }
|
||||
emitBlockAndNested(position + 1, lines)
|
||||
lines += block.epilogue.map { block.indent(it) }
|
||||
if (block.end.isNotEmpty()) lines += block.indentBraces(block.end)
|
||||
}
|
||||
|
||||
fun build(): List<String> {
|
||||
val lines = mutableListOf<String>()
|
||||
emitBlockAndNested(0, lines)
|
||||
return lines.toList()
|
||||
}
|
||||
}
|
||||
|
||||
inline fun buildKotlinCodeLines(scope: KotlinScope, block: KotlinCodeBuilder.() -> Unit): List<String> {
|
||||
val builder = KotlinCodeBuilder(scope)
|
||||
builder.block()
|
||||
return builder.build()
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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.native.interop.gen
|
||||
|
||||
val kotlinKeywords = setOf(
|
||||
"as", "break", "class", "continue", "do", "dynamic", "else", "false", "for", "fun", "if", "in",
|
||||
"interface", "is", "null", "object", "package", "return", "super", "this", "throw",
|
||||
"true", "try", "typealias", "val", "var", "when", "while",
|
||||
// While not technically keywords, those shall be escaped as well.
|
||||
"_", "__", "___"
|
||||
)
|
||||
|
||||
/**
|
||||
* The expression written in native language.
|
||||
*/
|
||||
typealias NativeExpression = String
|
||||
|
||||
/**
|
||||
* The expression written in Kotlin.
|
||||
*/
|
||||
typealias KotlinExpression = String
|
||||
|
||||
/**
|
||||
* For this identifier constructs the string to be parsed by Kotlin as `SimpleName`
|
||||
* defined [here](https://kotlinlang.org/docs/reference/grammar.html#SimpleName).
|
||||
*/
|
||||
fun String.asSimpleName(): String = if (this in kotlinKeywords || this.contains("$")) {
|
||||
"`$this`"
|
||||
} else {
|
||||
this
|
||||
}
|
||||
|
||||
/**
|
||||
* Yet another mangler, particularly to avoid secondary clash, e.g. when a property
|
||||
* in prototype (interface) is mangled and that will cause another clash in the class
|
||||
* which implements this interface.
|
||||
* Rationale: keep algorithm simple but use the mangling characters which are rare
|
||||
* in normal code, and keep mangling easy readable.
|
||||
*/
|
||||
internal fun mangleSimple(name: String): String {
|
||||
val reserved = setOf("Companion")
|
||||
val postfix = "\$"
|
||||
return if (name in reserved)
|
||||
"$name$postfix"
|
||||
else
|
||||
name
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the expression to be parsed by Kotlin as string literal with given contents,
|
||||
* i.e. transforms `foo$bar` to `"foo\$bar"`.
|
||||
*/
|
||||
fun String.quoteAsKotlinLiteral(): KotlinExpression = buildString {
|
||||
append('"')
|
||||
|
||||
this@quoteAsKotlinLiteral.forEach { c ->
|
||||
when (c) {
|
||||
in charactersAllowedInKotlinStringLiterals -> append(c)
|
||||
'$' -> append("\\$")
|
||||
else -> append("\\u" + "%04X".format(c.toInt()))
|
||||
}
|
||||
}
|
||||
|
||||
append('"')
|
||||
}
|
||||
|
||||
// TODO: improve literal readability by preserving more characters.
|
||||
|
||||
private val charactersAllowedInKotlinStringLiterals: Set<Char> = mutableSetOf<Char>().apply {
|
||||
addAll('a' .. 'z')
|
||||
addAll('A' .. 'Z')
|
||||
addAll('0' .. '9')
|
||||
addAll(listOf('_', '@', ':', ';', '.', ',', '{', '}', '=', '[', ']', '^', '#', '*', ' ', '(', ')'))
|
||||
}
|
||||
|
||||
val annotationForUnableToImport
|
||||
get() = "@Deprecated(${"Unable to import this declaration".quoteAsKotlinLiteral()}, level = DeprecationLevel.ERROR)"
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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.native.interop.gen
|
||||
|
||||
import org.jetbrains.kotlin.konan.library.KonanLibrary
|
||||
import org.jetbrains.kotlin.native.interop.indexer.*
|
||||
|
||||
interface Imports {
|
||||
fun getPackage(location: Location): String?
|
||||
}
|
||||
|
||||
|
||||
class PackageInfo(val name: String, val library: KonanLibrary)
|
||||
|
||||
class ImportsImpl(internal val headerIdToPackage: Map<HeaderId, PackageInfo>) : Imports {
|
||||
|
||||
override fun getPackage(location: Location): String? {
|
||||
val packageInfo = headerIdToPackage[location.headerId]
|
||||
?: return null
|
||||
accessedLibraries += packageInfo.library
|
||||
return packageInfo.name
|
||||
}
|
||||
|
||||
private val accessedLibraries = mutableSetOf<KonanLibrary>()
|
||||
|
||||
val requiredLibraries: Set<KonanLibrary>
|
||||
get() = accessedLibraries.toSet()
|
||||
}
|
||||
|
||||
class HeaderInclusionPolicyImpl(private val nameGlobs: List<String>) : HeaderInclusionPolicy {
|
||||
|
||||
override fun excludeUnused(headerName: String?): Boolean {
|
||||
if (nameGlobs.isEmpty()) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (headerName == null) {
|
||||
// Builtins; included only if no globs are specified:
|
||||
return true
|
||||
}
|
||||
|
||||
return nameGlobs.all { !headerName.matchesToGlob(it) }
|
||||
}
|
||||
}
|
||||
|
||||
class HeaderExclusionPolicyImpl(
|
||||
private val importsImpl: ImportsImpl
|
||||
) : HeaderExclusionPolicy {
|
||||
|
||||
override fun excludeAll(headerId: HeaderId): Boolean {
|
||||
return headerId in importsImpl.headerIdToPackage
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun String.matchesToGlob(glob: String): Boolean =
|
||||
java.nio.file.FileSystems.getDefault()
|
||||
.getPathMatcher("glob:$glob").matches(java.nio.file.Paths.get(this))
|
||||
+346
@@ -0,0 +1,346 @@
|
||||
/*
|
||||
* 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.native.interop.gen
|
||||
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
interface KotlinScope {
|
||||
/**
|
||||
* @return the string to be used to reference the classifier in current scope.
|
||||
*/
|
||||
fun reference(classifier: Classifier): String
|
||||
|
||||
/**
|
||||
* @return the string to be used as a name in the declaration of the classifier in current scope.
|
||||
*/
|
||||
fun declare(classifier: Classifier): String
|
||||
|
||||
/**
|
||||
* @return the string to be used as a name in the declaration of the property in current scope,
|
||||
* or `null` if the property with given name can't be declared.
|
||||
*/
|
||||
fun declareProperty(receiver: String?, name: String): String?
|
||||
|
||||
val mappingBridgeGenerator: MappingBridgeGenerator
|
||||
}
|
||||
|
||||
data class Classifier(
|
||||
val pkg: String,
|
||||
val topLevelName: String,
|
||||
private val nestedNames: List<String> = emptyList()
|
||||
) {
|
||||
|
||||
companion object {
|
||||
fun topLevel(pkg: String, name: String): Classifier {
|
||||
assert(!name.contains('.'))
|
||||
assert(!name.contains('`'))
|
||||
return Classifier(pkg, name)
|
||||
}
|
||||
}
|
||||
|
||||
val isTopLevel: Boolean get() = this.nestedNames.isEmpty()
|
||||
|
||||
fun nested(name: String): Classifier {
|
||||
assert(!name.contains('.'))
|
||||
assert(!name.contains('`'))
|
||||
return this.copy(nestedNames = nestedNames + name)
|
||||
}
|
||||
|
||||
fun getRelativeFqName(asSimpleName: Boolean = true): String = buildString {
|
||||
append(topLevelName.run { if (asSimpleName) asSimpleName() else this })
|
||||
nestedNames.forEach {
|
||||
append('.')
|
||||
append(it.run { if (asSimpleName) asSimpleName() else this })
|
||||
}
|
||||
}
|
||||
|
||||
val fqName: String get() = buildString {
|
||||
if (pkg.isNotEmpty()) {
|
||||
append(pkg)
|
||||
append('.')
|
||||
}
|
||||
append(getRelativeFqName())
|
||||
}
|
||||
}
|
||||
|
||||
val Classifier.type
|
||||
get() = KotlinClassifierType(this, arguments = emptyList(), nullable = false, underlyingType = null)
|
||||
|
||||
fun Classifier.typeWith(vararg arguments: KotlinTypeArgument) =
|
||||
KotlinClassifierType(this, arguments.toList(), nullable = false, underlyingType = null)
|
||||
|
||||
fun Classifier.typeAbbreviation(expandedType: KotlinType) =
|
||||
KotlinClassifierType(this, arguments = emptyList(), nullable = false, underlyingType = expandedType)
|
||||
|
||||
interface KotlinTypeArgument {
|
||||
/**
|
||||
* @return the string to be used in the given scope to denote this.
|
||||
*/
|
||||
fun render(scope: KotlinScope): String
|
||||
}
|
||||
|
||||
object StarProjection : KotlinTypeArgument {
|
||||
override fun render(scope: KotlinScope) = "*"
|
||||
}
|
||||
|
||||
interface KotlinType : KotlinTypeArgument {
|
||||
val classifier: Classifier
|
||||
fun makeNullableAsSpecified(nullable: Boolean): KotlinType
|
||||
}
|
||||
|
||||
/**
|
||||
* @property underlyingType is non-null if this type is an alias to another type.
|
||||
*/
|
||||
data class KotlinClassifierType(
|
||||
override val classifier: Classifier,
|
||||
val arguments: List<KotlinTypeArgument>,
|
||||
val nullable: Boolean,
|
||||
val underlyingType: KotlinType?
|
||||
) : KotlinType {
|
||||
|
||||
override fun makeNullableAsSpecified(nullable: Boolean) = if (this.nullable == nullable) {
|
||||
this
|
||||
} else {
|
||||
this.copy(nullable = nullable)
|
||||
}
|
||||
|
||||
override fun render(scope: KotlinScope): String = buildString {
|
||||
append(scope.reference(classifier))
|
||||
if (arguments.isNotEmpty()) {
|
||||
append('<')
|
||||
arguments.joinTo(this) { it.render(scope) }
|
||||
append('>')
|
||||
}
|
||||
if (nullable) {
|
||||
append('?')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun KotlinType.makeNullable() = this.makeNullableAsSpecified(true)
|
||||
|
||||
data class KotlinFunctionType(
|
||||
val parameterTypes: List<KotlinType>,
|
||||
val returnType: KotlinType,
|
||||
val nullable: Boolean = false
|
||||
) : KotlinType {
|
||||
|
||||
override fun makeNullableAsSpecified(nullable: Boolean) = if (this.nullable == nullable) {
|
||||
this
|
||||
} else {
|
||||
this.copy(nullable = nullable)
|
||||
}
|
||||
|
||||
override val classifier by lazy {
|
||||
Classifier.topLevel("kotlin", "Function${parameterTypes.size}")
|
||||
}
|
||||
|
||||
override fun render(scope: KotlinScope) = buildString {
|
||||
if (nullable) append("(")
|
||||
|
||||
append('(')
|
||||
parameterTypes.joinTo(this) { it.render(scope) }
|
||||
append(") -> ")
|
||||
append(returnType.render(scope))
|
||||
|
||||
if (nullable) append(")?")
|
||||
}
|
||||
}
|
||||
|
||||
internal val cnamesStructsPackageName = "cnames.structs"
|
||||
|
||||
object KotlinTypes {
|
||||
val independent = Classifier.topLevel("kotlin.native.internal", "Independent")
|
||||
|
||||
val boolean by BuiltInType
|
||||
val byte by BuiltInType
|
||||
val short by BuiltInType
|
||||
val int by BuiltInType
|
||||
val long by BuiltInType
|
||||
val uByte by BuiltInType
|
||||
val uShort by BuiltInType
|
||||
val uInt by BuiltInType
|
||||
val uLong by BuiltInType
|
||||
val float by BuiltInType
|
||||
val double by BuiltInType
|
||||
val unit by BuiltInType
|
||||
val string by BuiltInType
|
||||
val any by BuiltInType
|
||||
|
||||
val list by CollectionClassifier
|
||||
val mutableList by CollectionClassifier
|
||||
val set by CollectionClassifier
|
||||
val map by CollectionClassifier
|
||||
|
||||
val nativePtr by InteropType
|
||||
val vector128 by KotlinNativeType
|
||||
|
||||
val cOpaque by InteropType
|
||||
val cOpaquePointer by InteropType
|
||||
val cOpaquePointerVar by InteropType
|
||||
|
||||
val booleanVarOf by InteropClassifier
|
||||
|
||||
val objCObject by InteropClassifier
|
||||
val objCObjectMeta by InteropClassifier
|
||||
val objCClass by InteropClassifier
|
||||
val objCClassOf by InteropClassifier
|
||||
val objCProtocol by InteropClassifier
|
||||
|
||||
val cValuesRef by InteropClassifier
|
||||
|
||||
val cPointed by InteropClassifier
|
||||
val cPointer by InteropClassifier
|
||||
val cPointerVar by InteropClassifier
|
||||
val cArrayPointer by InteropClassifier
|
||||
val cArrayPointerVar by InteropClassifier
|
||||
val cPointerVarOf by InteropClassifier
|
||||
|
||||
val cFunction by InteropClassifier
|
||||
|
||||
val objCObjectVar by InteropClassifier
|
||||
|
||||
val objCObjectBase by InteropClassifier
|
||||
val objCObjectBaseMeta by InteropClassifier
|
||||
|
||||
val objCBlockVar by InteropClassifier
|
||||
val objCNotImplementedVar by InteropClassifier
|
||||
|
||||
val cValue by InteropClassifier
|
||||
|
||||
private open class ClassifierAtPackage(val pkg: String) {
|
||||
operator fun getValue(thisRef: KotlinTypes, property: KProperty<*>): Classifier =
|
||||
Classifier.topLevel(pkg, property.name.capitalize())
|
||||
}
|
||||
|
||||
private open class TypeAtPackage(val pkg: String) {
|
||||
operator fun getValue(thisRef: KotlinTypes, property: KProperty<*>): KotlinClassifierType =
|
||||
Classifier.topLevel(pkg, property.name.capitalize()).type
|
||||
}
|
||||
|
||||
private object BuiltInType : TypeAtPackage("kotlin")
|
||||
private object CollectionClassifier : ClassifierAtPackage("kotlin.collections")
|
||||
|
||||
private object InteropClassifier : ClassifierAtPackage("kotlinx.cinterop")
|
||||
private object InteropType : TypeAtPackage("kotlinx.cinterop")
|
||||
private object KotlinNativeType : TypeAtPackage("kotlin.native")
|
||||
}
|
||||
|
||||
abstract class KotlinFile(
|
||||
val pkg: String,
|
||||
namesToBeDeclared: List<String>
|
||||
) : KotlinScope {
|
||||
|
||||
// Note: all names are related to classifiers currently.
|
||||
|
||||
private val namesToBeDeclared: Set<String>
|
||||
|
||||
init {
|
||||
this.namesToBeDeclared = mutableSetOf()
|
||||
|
||||
namesToBeDeclared.forEach {
|
||||
if (it in this.namesToBeDeclared) {
|
||||
throw IllegalArgumentException("'$it' is going to be declared twice")
|
||||
} else {
|
||||
this.namesToBeDeclared.add(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val importedNameToPkg = mutableMapOf<String, String>()
|
||||
private val declaredProperties = mutableSetOf<String>()
|
||||
|
||||
override fun reference(classifier: Classifier): String = if (classifier.topLevelName in namesToBeDeclared) {
|
||||
if (classifier.pkg == this.pkg) {
|
||||
classifier.getRelativeFqName()
|
||||
} else {
|
||||
// Don't import if would clash with own declaration:
|
||||
classifier.fqName
|
||||
}
|
||||
} else if (classifier.pkg == this.pkg) {
|
||||
throw IllegalArgumentException(
|
||||
"'${classifier.topLevelName}' from the file package was not reserved for declaration"
|
||||
)
|
||||
} else {
|
||||
if (tryImport(classifier)) {
|
||||
// Is successfully imported:
|
||||
classifier.getRelativeFqName()
|
||||
} else {
|
||||
classifier.fqName
|
||||
}
|
||||
}
|
||||
|
||||
private fun tryImport(classifier: Classifier): Boolean {
|
||||
if (classifier.topLevelName in declaredProperties) {
|
||||
return false
|
||||
}
|
||||
|
||||
return importedNameToPkg.getOrPut(classifier.topLevelName) { classifier.pkg } == classifier.pkg
|
||||
}
|
||||
|
||||
private val alreadyDeclared = mutableSetOf<String>()
|
||||
|
||||
override fun declare(classifier: Classifier): String {
|
||||
if (classifier.pkg != this.pkg) {
|
||||
throw IllegalArgumentException("wrong package for classifier ${classifier.fqName}; expected '$pkg', got '${classifier.pkg}'")
|
||||
}
|
||||
|
||||
if (!classifier.isTopLevel) {
|
||||
throw IllegalArgumentException(
|
||||
"'${classifier.getRelativeFqName()}' is not top-level thus can't be declared at file scope"
|
||||
)
|
||||
}
|
||||
|
||||
val topLevelName = classifier.topLevelName
|
||||
if (topLevelName in alreadyDeclared) {
|
||||
throw IllegalStateException("'$topLevelName' is already declared")
|
||||
}
|
||||
alreadyDeclared.add(topLevelName)
|
||||
|
||||
return topLevelName
|
||||
}
|
||||
|
||||
override fun declareProperty(receiver: String?, name: String): String? {
|
||||
val fullName = receiver?.let { "$it.${name}" } ?: name
|
||||
return if (fullName in declaredProperties || name in namesToBeDeclared || name in importedNameToPkg) {
|
||||
null
|
||||
// TODO: using original global name should be preferred to importing the clashed name.
|
||||
} else {
|
||||
declaredProperties.add(fullName)
|
||||
name
|
||||
}
|
||||
}
|
||||
|
||||
fun buildImports(): List<String> = importedNameToPkg.mapNotNull { (name, pkg) ->
|
||||
if (pkg == "kotlin" || pkg == "kotlinx.cinterop") {
|
||||
// Is already imported either by default or with '*':
|
||||
null
|
||||
} else {
|
||||
"import $pkg.${name.asSimpleName()}"
|
||||
}
|
||||
}.sorted()
|
||||
|
||||
}
|
||||
|
||||
internal fun getTopLevelPropertyDeclarationName(scope: KotlinScope, property: PropertyStub): String {
|
||||
val receiverName = property.receiverType?.underlyingTypeFqName
|
||||
return getTopLevelPropertyDeclarationName(scope, receiverName, property.name)
|
||||
}
|
||||
|
||||
// Try to use the provided name. If failed, mangle it with underscore and try again:
|
||||
private tailrec fun getTopLevelPropertyDeclarationName(scope: KotlinScope, receiver: String?, name: String): String =
|
||||
scope.declareProperty(receiver, name) ?: getTopLevelPropertyDeclarationName(scope, receiver, name + "_")
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.native.interop.gen
|
||||
|
||||
import org.jetbrains.kotlin.konan.file.File
|
||||
|
||||
internal fun resolveLibraries(staticLibraries: List<String>, libraryPaths: List<String>): List<String> {
|
||||
val result = mutableListOf<String>()
|
||||
staticLibraries.forEach { library ->
|
||||
|
||||
val resolution = libraryPaths.map { "$it/$library" }
|
||||
.find { File(it).exists }
|
||||
|
||||
if (resolution != null) {
|
||||
result.add(resolution)
|
||||
} else {
|
||||
error("Could not find '$library' binary in neither of $libraryPaths")
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
internal fun argsToCompiler(staticLibraries: Array<String>, libraryPaths: Array<String>) = argsToCompiler(staticLibraries.toList(), libraryPaths.toList())
|
||||
|
||||
internal fun argsToCompiler(staticLibraries: List<String>, libraryPaths: List<String>) =
|
||||
resolveLibraries(staticLibraries, libraryPaths)
|
||||
.map { it -> listOf("-include-binary", it) }
|
||||
.flatten()
|
||||
.toTypedArray()
|
||||
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.native.interop.gen
|
||||
|
||||
import org.jetbrains.kotlin.native.interop.indexer.Type
|
||||
|
||||
data class TypedKotlinValue(val type: Type, val value: KotlinExpression)
|
||||
data class TypedNativeValue(val type: Type, val value: NativeExpression)
|
||||
|
||||
/**
|
||||
* Generates bridges between Kotlin and native, passing arbitrary native-typed values.
|
||||
*
|
||||
* It does the same as [SimpleBridgeGenerator] except that it supports any native types, e.g. struct values.
|
||||
*/
|
||||
interface MappingBridgeGenerator {
|
||||
fun kotlinToNative(
|
||||
builder: KotlinCodeBuilder,
|
||||
nativeBacked: NativeBacked,
|
||||
returnType: Type,
|
||||
kotlinValues: List<TypedKotlinValue>,
|
||||
independent: Boolean,
|
||||
block: NativeCodeBuilder.(nativeValues: List<NativeExpression>) -> NativeExpression
|
||||
): KotlinExpression
|
||||
|
||||
fun nativeToKotlin(
|
||||
builder: NativeCodeBuilder,
|
||||
nativeBacked: NativeBacked,
|
||||
returnType: Type,
|
||||
nativeValues: List<TypedNativeValue>,
|
||||
block: KotlinCodeBuilder.(kotlinValues: List<KotlinExpression>) -> KotlinExpression
|
||||
): NativeExpression
|
||||
}
|
||||
+207
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* 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.native.interop.gen
|
||||
|
||||
import org.jetbrains.kotlin.native.interop.indexer.RecordType
|
||||
import org.jetbrains.kotlin.native.interop.indexer.Type
|
||||
import org.jetbrains.kotlin.native.interop.indexer.VoidType
|
||||
import org.jetbrains.kotlin.native.interop.indexer.unwrapTypedefs
|
||||
|
||||
/**
|
||||
* The [MappingBridgeGenerator] implementation which uses [SimpleBridgeGenerator] as the backend and
|
||||
* maps the type using [mirror].
|
||||
*/
|
||||
class MappingBridgeGeneratorImpl(
|
||||
val declarationMapper: DeclarationMapper,
|
||||
val simpleBridgeGenerator: SimpleBridgeGenerator
|
||||
) : MappingBridgeGenerator {
|
||||
|
||||
override fun kotlinToNative(
|
||||
builder: KotlinCodeBuilder,
|
||||
nativeBacked: NativeBacked,
|
||||
returnType: Type,
|
||||
kotlinValues: List<TypedKotlinValue>,
|
||||
independent: Boolean,
|
||||
block: NativeCodeBuilder.(nativeValues: List<NativeExpression>) -> NativeExpression
|
||||
): KotlinExpression {
|
||||
val bridgeArguments = mutableListOf<BridgeTypedKotlinValue>()
|
||||
|
||||
kotlinValues.forEach { (type, value) ->
|
||||
if (type.unwrapTypedefs() is RecordType) {
|
||||
builder.pushMemScoped()
|
||||
val bridgeArgument = "$value.getPointer(memScope).rawValue"
|
||||
bridgeArguments.add(BridgeTypedKotlinValue(BridgedType.NATIVE_PTR, bridgeArgument))
|
||||
} else {
|
||||
val info = mirror(declarationMapper, type).info
|
||||
bridgeArguments.add(BridgeTypedKotlinValue(info.bridgedType, info.argToBridged(value)))
|
||||
}
|
||||
}
|
||||
|
||||
val unwrappedReturnType = returnType.unwrapTypedefs()
|
||||
val kniRetVal = "kniRetVal"
|
||||
val bridgeReturnType = when (unwrappedReturnType) {
|
||||
VoidType -> BridgedType.VOID
|
||||
is RecordType -> {
|
||||
val mirror = mirror(declarationMapper, returnType)
|
||||
val tmpVarName = kniRetVal
|
||||
// We clear in the finally block.
|
||||
builder.out("val $tmpVarName = nativeHeap.alloc<${mirror.pointedType.render(builder.scope)}>()")
|
||||
builder.pushBlock(start = "try {", end = "} finally { nativeHeap.free($tmpVarName) }")
|
||||
bridgeArguments.add(BridgeTypedKotlinValue(BridgedType.NATIVE_PTR, "$tmpVarName.rawPtr"))
|
||||
BridgedType.VOID
|
||||
}
|
||||
else -> {
|
||||
val mirror = mirror(declarationMapper, returnType)
|
||||
mirror.info.bridgedType
|
||||
}
|
||||
}
|
||||
|
||||
val callExpr = simpleBridgeGenerator.kotlinToNative(
|
||||
nativeBacked, bridgeReturnType, bridgeArguments, independent
|
||||
) { bridgeNativeValues ->
|
||||
|
||||
val nativeValues = mutableListOf<String>()
|
||||
kotlinValues.forEachIndexed { index, (type, _) ->
|
||||
val unwrappedType = type.unwrapTypedefs()
|
||||
if (unwrappedType is RecordType) {
|
||||
nativeValues.add("*(${unwrappedType.decl.spelling}*)${bridgeNativeValues[index]}")
|
||||
} else {
|
||||
nativeValues.add(
|
||||
mirror(declarationMapper, type).info.cFromBridged(
|
||||
bridgeNativeValues[index], scope, nativeBacked
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val nativeResult = block(nativeValues)
|
||||
|
||||
when (unwrappedReturnType) {
|
||||
is VoidType -> {
|
||||
out(nativeResult + ";")
|
||||
""
|
||||
}
|
||||
is RecordType -> {
|
||||
val kniStructResult = "kniStructResult"
|
||||
|
||||
out("${unwrappedReturnType.decl.spelling} $kniStructResult = $nativeResult;")
|
||||
out("memcpy(${bridgeNativeValues.last()}, &$kniStructResult, sizeof($kniStructResult));")
|
||||
""
|
||||
}
|
||||
else -> {
|
||||
nativeResult
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val result = when (unwrappedReturnType) {
|
||||
is VoidType -> callExpr
|
||||
is RecordType -> {
|
||||
builder.out(callExpr)
|
||||
"$kniRetVal.readValue()"
|
||||
}
|
||||
else -> {
|
||||
val mirror = mirror(declarationMapper, returnType)
|
||||
mirror.info.argFromBridged(callExpr, builder.scope, nativeBacked)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
override fun nativeToKotlin(
|
||||
builder: NativeCodeBuilder,
|
||||
nativeBacked: NativeBacked,
|
||||
returnType: Type,
|
||||
nativeValues: List<TypedNativeValue>,
|
||||
block: KotlinCodeBuilder.(kotlinValues: List<KotlinExpression>) -> KotlinExpression
|
||||
): NativeExpression {
|
||||
|
||||
val bridgeArguments = mutableListOf<BridgeTypedNativeValue>()
|
||||
|
||||
nativeValues.forEachIndexed { _, (type, value) ->
|
||||
val bridgeArgument = if (type.unwrapTypedefs() is RecordType) {
|
||||
BridgeTypedNativeValue(BridgedType.NATIVE_PTR, "&$value")
|
||||
} else {
|
||||
val info = mirror(declarationMapper, type).info
|
||||
BridgeTypedNativeValue(info.bridgedType, value)
|
||||
}
|
||||
bridgeArguments.add(bridgeArgument)
|
||||
}
|
||||
|
||||
val unwrappedReturnType = returnType.unwrapTypedefs()
|
||||
val kniRetVal = "kniRetVal"
|
||||
val bridgeReturnType = when (unwrappedReturnType) {
|
||||
VoidType -> BridgedType.VOID
|
||||
is RecordType -> {
|
||||
val tmpVarName = kniRetVal
|
||||
builder.out("${unwrappedReturnType.decl.spelling} $tmpVarName;")
|
||||
bridgeArguments.add(BridgeTypedNativeValue(BridgedType.NATIVE_PTR, "&$tmpVarName"))
|
||||
BridgedType.VOID
|
||||
}
|
||||
else -> {
|
||||
val mirror = mirror(declarationMapper, returnType)
|
||||
mirror.info.bridgedType
|
||||
}
|
||||
}
|
||||
|
||||
val callExpr = simpleBridgeGenerator.nativeToKotlin(
|
||||
nativeBacked,
|
||||
bridgeReturnType,
|
||||
bridgeArguments
|
||||
) { bridgeKotlinValues ->
|
||||
val kotlinValues = mutableListOf<String>()
|
||||
nativeValues.forEachIndexed { index, (type, _) ->
|
||||
val mirror = mirror(declarationMapper, type)
|
||||
if (type.unwrapTypedefs() is RecordType) {
|
||||
val pointedTypeName = mirror.pointedType.render(this.scope)
|
||||
kotlinValues.add(
|
||||
"interpretPointed<$pointedTypeName>(${bridgeKotlinValues[index]}).readValue()"
|
||||
)
|
||||
} else {
|
||||
kotlinValues.add(mirror.info.argFromBridged(bridgeKotlinValues[index], this.scope, nativeBacked))
|
||||
}
|
||||
}
|
||||
|
||||
val kotlinResult = block(kotlinValues)
|
||||
when (unwrappedReturnType) {
|
||||
is RecordType -> {
|
||||
"$kotlinResult.write(${bridgeKotlinValues.last()})"
|
||||
}
|
||||
is VoidType -> {
|
||||
kotlinResult
|
||||
}
|
||||
else -> {
|
||||
mirror(declarationMapper, returnType).info.argToBridged(kotlinResult)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val result = when (unwrappedReturnType) {
|
||||
is VoidType -> callExpr
|
||||
is RecordType -> {
|
||||
builder.out("$callExpr;")
|
||||
kniRetVal
|
||||
}
|
||||
else -> {
|
||||
mirror(declarationMapper, returnType).info.cFromBridged(callExpr, builder.scope, nativeBacked)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
+578
@@ -0,0 +1,578 @@
|
||||
/*
|
||||
* 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.native.interop.gen
|
||||
|
||||
import org.jetbrains.kotlin.native.interop.indexer.*
|
||||
|
||||
interface DeclarationMapper {
|
||||
fun getKotlinClassForPointed(structDecl: StructDecl): Classifier
|
||||
fun isMappedToStrict(enumDef: EnumDef): Boolean
|
||||
fun getKotlinNameForValue(enumDef: EnumDef): String
|
||||
fun getPackageFor(declaration: TypeDeclaration): String
|
||||
|
||||
val useUnsignedTypes: Boolean
|
||||
}
|
||||
|
||||
fun DeclarationMapper.isMappedToSigned(integerType: IntegerType): Boolean = integerType.isSigned || !useUnsignedTypes
|
||||
|
||||
fun DeclarationMapper.getKotlinClassFor(
|
||||
objCClassOrProtocol: ObjCClassOrProtocol,
|
||||
isMeta: Boolean = false
|
||||
): Classifier {
|
||||
val pkg = if (objCClassOrProtocol.isForwardDeclaration) {
|
||||
when (objCClassOrProtocol) {
|
||||
is ObjCClass -> "objcnames.classes"
|
||||
is ObjCProtocol -> "objcnames.protocols"
|
||||
}
|
||||
} else {
|
||||
this.getPackageFor(objCClassOrProtocol)
|
||||
}
|
||||
val className = objCClassOrProtocol.kotlinClassName(isMeta)
|
||||
return Classifier.topLevel(pkg, className)
|
||||
}
|
||||
|
||||
fun PrimitiveType.getKotlinType(declarationMapper: DeclarationMapper): KotlinClassifierType = when (this) {
|
||||
is CharType -> KotlinTypes.byte
|
||||
|
||||
is BoolType -> KotlinTypes.boolean
|
||||
|
||||
// TODO: C primitive types should probably be generated as type aliases for Kotlin types.
|
||||
is IntegerType -> if (declarationMapper.isMappedToSigned(this)) {
|
||||
when (this.size) {
|
||||
1 -> KotlinTypes.byte
|
||||
2 -> KotlinTypes.short
|
||||
4 -> KotlinTypes.int
|
||||
8 -> KotlinTypes.long
|
||||
else -> TODO(this.toString())
|
||||
}
|
||||
} else {
|
||||
when (this.size) {
|
||||
1 -> KotlinTypes.uByte
|
||||
2 -> KotlinTypes.uShort
|
||||
4 -> KotlinTypes.uInt
|
||||
8 -> KotlinTypes.uLong
|
||||
else -> TODO(this.toString())
|
||||
}
|
||||
}
|
||||
|
||||
is FloatingType -> when (this.size) {
|
||||
4 -> KotlinTypes.float
|
||||
8 -> KotlinTypes.double
|
||||
else -> TODO(this.toString())
|
||||
}
|
||||
|
||||
is VectorType -> {
|
||||
/// @todo assert elementType and size here
|
||||
KotlinTypes.vector128
|
||||
}
|
||||
else -> throw NotImplementedError()
|
||||
}
|
||||
|
||||
private fun PrimitiveType.getBridgedType(declarationMapper: DeclarationMapper): BridgedType {
|
||||
val kotlinType = this.getKotlinType(declarationMapper)
|
||||
return BridgedType.values().single {
|
||||
it.kotlinType == kotlinType
|
||||
}
|
||||
}
|
||||
|
||||
internal val ObjCPointer.isNullable: Boolean
|
||||
get() = this.nullability != ObjCPointer.Nullability.NonNull
|
||||
|
||||
/**
|
||||
* Describes the Kotlin types used to represent some C type.
|
||||
*/
|
||||
sealed class TypeMirror(val pointedType: KotlinClassifierType, val info: TypeInfo) {
|
||||
/**
|
||||
* Type to be used in bindings for argument or return value.
|
||||
*/
|
||||
abstract val argType: KotlinType
|
||||
|
||||
/**
|
||||
* Mirror for C type to be represented in Kotlin as by-value type.
|
||||
*/
|
||||
class ByValue(
|
||||
pointedType: KotlinClassifierType,
|
||||
info: TypeInfo,
|
||||
val valueType: KotlinType,
|
||||
val nullable: Boolean = (info is TypeInfo.Pointer)
|
||||
) : TypeMirror(pointedType, info) {
|
||||
|
||||
override val argType: KotlinType
|
||||
get() = valueType.makeNullableAsSpecified(nullable)
|
||||
}
|
||||
|
||||
/**
|
||||
* Mirror for C type to be represented in Kotlin as by-ref type.
|
||||
*/
|
||||
class ByRef(pointedType: KotlinClassifierType, info: TypeInfo) : TypeMirror(pointedType, info) {
|
||||
override val argType: KotlinType get() = KotlinTypes.cValue.typeWith(pointedType)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes various type conversions for [TypeMirror].
|
||||
*/
|
||||
sealed class TypeInfo {
|
||||
/**
|
||||
* The conversion from [TypeMirror.argType] to [bridgedType].
|
||||
*/
|
||||
abstract fun argToBridged(expr: KotlinExpression): KotlinExpression
|
||||
|
||||
/**
|
||||
* The conversion from [bridgedType] to [TypeMirror.argType].
|
||||
*/
|
||||
abstract fun argFromBridged(
|
||||
expr: KotlinExpression,
|
||||
scope: KotlinScope,
|
||||
nativeBacked: NativeBacked
|
||||
): KotlinExpression
|
||||
|
||||
abstract val bridgedType: BridgedType
|
||||
|
||||
open fun cFromBridged(
|
||||
expr: NativeExpression,
|
||||
scope: NativeScope,
|
||||
nativeBacked: NativeBacked
|
||||
): NativeExpression = expr
|
||||
|
||||
open fun cToBridged(expr: NativeExpression): NativeExpression = expr
|
||||
|
||||
/**
|
||||
* If this info is for [TypeMirror.ByValue], then this method describes how to
|
||||
* construct pointed-type from value type.
|
||||
*/
|
||||
abstract fun constructPointedType(valueType: KotlinType): KotlinClassifierType
|
||||
|
||||
class Primitive(override val bridgedType: BridgedType, val varClass: Classifier) : TypeInfo() {
|
||||
|
||||
override fun argToBridged(expr: KotlinExpression) = expr
|
||||
override fun argFromBridged(expr: KotlinExpression, scope: KotlinScope, nativeBacked: NativeBacked) = expr
|
||||
|
||||
override fun constructPointedType(valueType: KotlinType) = varClass.typeWith(valueType)
|
||||
}
|
||||
|
||||
class Boolean : TypeInfo() {
|
||||
override fun argToBridged(expr: KotlinExpression) = "$expr.toByte()"
|
||||
|
||||
override fun argFromBridged(expr: KotlinExpression, scope: KotlinScope, nativeBacked: NativeBacked) =
|
||||
"$expr.toBoolean()"
|
||||
|
||||
override val bridgedType: BridgedType get() = BridgedType.BYTE
|
||||
|
||||
override fun cFromBridged(expr: NativeExpression, scope: NativeScope, nativeBacked: NativeBacked) =
|
||||
"($expr) ? 1 : 0"
|
||||
|
||||
override fun cToBridged(expr: NativeExpression) = "($expr) ? 1 : 0"
|
||||
|
||||
override fun constructPointedType(valueType: KotlinType) = KotlinTypes.booleanVarOf.typeWith(valueType)
|
||||
}
|
||||
|
||||
class Enum(val clazz: Classifier, override val bridgedType: BridgedType) : TypeInfo() {
|
||||
override fun argToBridged(expr: KotlinExpression) = "$expr.value"
|
||||
|
||||
override fun argFromBridged(expr: KotlinExpression, scope: KotlinScope, nativeBacked: NativeBacked) =
|
||||
scope.reference(clazz) + ".byValue($expr)"
|
||||
|
||||
override fun constructPointedType(valueType: KotlinType) =
|
||||
clazz.nested("Var").type // TODO: improve
|
||||
|
||||
}
|
||||
|
||||
class Pointer(val pointee: KotlinType, val cPointee: Type) : TypeInfo() {
|
||||
override fun argToBridged(expr: String) = "$expr.rawValue"
|
||||
|
||||
override fun argFromBridged(expr: KotlinExpression, scope: KotlinScope, nativeBacked: NativeBacked) =
|
||||
"interpretCPointer<${pointee.render(scope)}>($expr)"
|
||||
|
||||
override val bridgedType: BridgedType
|
||||
get() = BridgedType.NATIVE_PTR
|
||||
|
||||
override fun cFromBridged(expr: NativeExpression, scope: NativeScope, nativeBacked: NativeBacked) =
|
||||
"(${getPointerTypeStringRepresentation(cPointee)})$expr"
|
||||
|
||||
override fun constructPointedType(valueType: KotlinType) = KotlinTypes.cPointerVarOf.typeWith(valueType)
|
||||
}
|
||||
|
||||
class ObjCPointerInfo(val kotlinType: KotlinType, val type: ObjCPointer) : TypeInfo() {
|
||||
override fun argToBridged(expr: String) = "$expr.objcPtr()"
|
||||
|
||||
override fun argFromBridged(expr: KotlinExpression, scope: KotlinScope, nativeBacked: NativeBacked) =
|
||||
"interpretObjCPointerOrNull<${kotlinType.render(scope)}>($expr)" +
|
||||
if (type.isNullable) "" else "!!"
|
||||
|
||||
override val bridgedType: BridgedType
|
||||
get() = BridgedType.OBJC_POINTER
|
||||
|
||||
override fun constructPointedType(valueType: KotlinType) = KotlinTypes.objCObjectVar.typeWith(valueType)
|
||||
}
|
||||
|
||||
class ObjCBlockPointerInfo(val kotlinType: KotlinFunctionType, val type: ObjCBlockPointer) : TypeInfo() {
|
||||
|
||||
override val bridgedType: BridgedType
|
||||
get() = BridgedType.OBJC_POINTER
|
||||
|
||||
// When passing Kotlin function as block pointer from Kotlin to native,
|
||||
// it first gets wrapped by a holder in [argToBridged],
|
||||
// and then converted to block in [cFromBridged].
|
||||
|
||||
override fun argToBridged(expr: KotlinExpression): KotlinExpression = "createKotlinObjectHolder($expr)"
|
||||
|
||||
override fun cFromBridged(
|
||||
expr: NativeExpression,
|
||||
scope: NativeScope,
|
||||
nativeBacked: NativeBacked
|
||||
): NativeExpression {
|
||||
val mappingBridgeGenerator = scope.mappingBridgeGenerator
|
||||
|
||||
val blockParameters = type.parameterTypes.mapIndexed { index, it ->
|
||||
"p$index" to it.getStringRepresentation()
|
||||
}.joinToString { "${it.second} ${it.first}" }
|
||||
|
||||
val blockReturnType = type.returnType.getStringRepresentation()
|
||||
|
||||
val kniFunction = "kniFunction"
|
||||
|
||||
val codeBuilder = NativeCodeBuilder(scope)
|
||||
|
||||
return buildString {
|
||||
append("({ ") // Statement expression begins.
|
||||
append("id $kniFunction = $expr; ") // Note: it gets captured below.
|
||||
append("($kniFunction == nil) ? nil : ")
|
||||
append("(id)") // Cast the block to `id`.
|
||||
append("^$blockReturnType($blockParameters) {") // Block begins.
|
||||
|
||||
// As block body, generate the code which simply bridges to Kotlin and calls the Kotlin function:
|
||||
mappingBridgeGenerator.nativeToKotlin(
|
||||
codeBuilder,
|
||||
nativeBacked,
|
||||
type.returnType,
|
||||
type.parameterTypes.mapIndexed { index, it ->
|
||||
TypedNativeValue(it, "p$index")
|
||||
} + TypedNativeValue(ObjCIdType(ObjCPointer.Nullability.Nullable, emptyList()), kniFunction)
|
||||
) { kotlinValues ->
|
||||
val kotlinFunctionType = kotlinType.render(this.scope)
|
||||
val kotlinFunction = "unwrapKotlinObjectHolder<$kotlinFunctionType>(${kotlinValues.last()})"
|
||||
"$kotlinFunction(${kotlinValues.dropLast(1).joinToString()})"
|
||||
}.let {
|
||||
codeBuilder.out("return $it;")
|
||||
}
|
||||
|
||||
codeBuilder.lines.joinTo(this, separator = " ")
|
||||
|
||||
append(" };") // Block ends.
|
||||
append(" })") // Statement expression ends.
|
||||
}
|
||||
}
|
||||
|
||||
// When passing block pointer as Kotlin function from native to Kotlin,
|
||||
// it is converted to Kotlin function in [cFromBridged].
|
||||
|
||||
override fun cToBridged(expr: NativeExpression): NativeExpression = expr
|
||||
|
||||
override fun argFromBridged(
|
||||
expr: KotlinExpression,
|
||||
scope: KotlinScope,
|
||||
nativeBacked: NativeBacked
|
||||
): KotlinExpression {
|
||||
val mappingBridgeGenerator = scope.mappingBridgeGenerator
|
||||
|
||||
val funParameters = type.parameterTypes.mapIndexed { index, _ ->
|
||||
"p$index" to kotlinType.parameterTypes[index]
|
||||
}.joinToString { "${it.first}: ${it.second.render(scope)}" }
|
||||
|
||||
val funReturnType = kotlinType.returnType.render(scope)
|
||||
|
||||
val codeBuilder = KotlinCodeBuilder(scope)
|
||||
val kniBlockPtr = "kniBlockPtr"
|
||||
|
||||
|
||||
// Build the anonymous function expression:
|
||||
val anonymousFun = buildString {
|
||||
append("fun($funParameters): $funReturnType {\n") // Anonymous function begins.
|
||||
|
||||
// As function body, generate the code which simply bridges to native and calls the block:
|
||||
mappingBridgeGenerator.kotlinToNative(
|
||||
codeBuilder,
|
||||
nativeBacked,
|
||||
type.returnType,
|
||||
type.parameterTypes.mapIndexed { index, it ->
|
||||
TypedKotlinValue(it, "p$index")
|
||||
} + TypedKotlinValue(PointerType(VoidType), "interpretCPointer<COpaque>($kniBlockPtr)"),
|
||||
independent = true
|
||||
|
||||
) { nativeValues ->
|
||||
val type = type
|
||||
val blockType = blockTypeStringRepresentation(type)
|
||||
val objCBlock = "((__bridge $blockType)${nativeValues.last()})"
|
||||
"$objCBlock(${nativeValues.dropLast(1).joinToString()})"
|
||||
}.let {
|
||||
codeBuilder.returnResult(it)
|
||||
}
|
||||
|
||||
codeBuilder.build().joinTo(this, separator = "\n")
|
||||
append("}") // Anonymous function ends.
|
||||
}
|
||||
|
||||
val nullOutput = if (type.isNullable) "null" else "throw NullPointerException()"
|
||||
|
||||
return "$expr.let { $kniBlockPtr -> if (kniBlockPtr == nativeNullPtr) $nullOutput else $anonymousFun }"
|
||||
}
|
||||
|
||||
override fun constructPointedType(valueType: KotlinType): KotlinClassifierType {
|
||||
return Classifier.topLevel("kotlinx.cinterop", "ObjCBlockVar").typeWith(valueType)
|
||||
}
|
||||
}
|
||||
|
||||
class ByRef(val pointed: KotlinType) : TypeInfo() {
|
||||
override fun argToBridged(expr: String) = error(pointed)
|
||||
override fun argFromBridged(expr: KotlinExpression, scope: KotlinScope, nativeBacked: NativeBacked) =
|
||||
error(pointed)
|
||||
override val bridgedType: BridgedType get() = error(pointed)
|
||||
override fun cFromBridged(expr: NativeExpression, scope: NativeScope, nativeBacked: NativeBacked) =
|
||||
error(pointed)
|
||||
override fun cToBridged(expr: String) = error(pointed)
|
||||
|
||||
// TODO: this method must not exist.
|
||||
override fun constructPointedType(valueType: KotlinType): KotlinClassifierType = error(pointed)
|
||||
}
|
||||
}
|
||||
|
||||
fun mirrorPrimitiveType(type: PrimitiveType, declarationMapper: DeclarationMapper): TypeMirror.ByValue {
|
||||
val varClassName = when (type) {
|
||||
is CharType -> "ByteVar"
|
||||
is BoolType -> "BooleanVar"
|
||||
is IntegerType -> if (declarationMapper.isMappedToSigned(type)) {
|
||||
when (type.size) {
|
||||
1 -> "ByteVar"
|
||||
2 -> "ShortVar"
|
||||
4 -> "IntVar"
|
||||
8 -> "LongVar"
|
||||
else -> TODO(type.toString())
|
||||
}
|
||||
} else {
|
||||
when (type.size) {
|
||||
1 -> "UByteVar"
|
||||
2 -> "UShortVar"
|
||||
4 -> "UIntVar"
|
||||
8 -> "ULongVar"
|
||||
else -> TODO(type.toString())
|
||||
}
|
||||
}
|
||||
is FloatingType -> when (type.size) {
|
||||
4 -> "FloatVar"
|
||||
8 -> "DoubleVar"
|
||||
else -> TODO(type.toString())
|
||||
}
|
||||
is VectorType -> {
|
||||
"Vector128Var"
|
||||
}
|
||||
else -> TODO(type.toString())
|
||||
}
|
||||
|
||||
val varClass = Classifier.topLevel("kotlinx.cinterop", varClassName)
|
||||
val varClassOf = Classifier.topLevel("kotlinx.cinterop", "${varClassName}Of")
|
||||
|
||||
val info = if (type is BoolType) {
|
||||
TypeInfo.Boolean()
|
||||
} else {
|
||||
TypeInfo.Primitive(type.getBridgedType(declarationMapper), varClassOf)
|
||||
}
|
||||
return TypeMirror.ByValue(varClass.type, info, type.getKotlinType(declarationMapper))
|
||||
}
|
||||
|
||||
private fun byRefTypeMirror(pointedType: KotlinClassifierType) : TypeMirror.ByRef {
|
||||
val info = TypeInfo.ByRef(pointedType)
|
||||
return TypeMirror.ByRef(pointedType, info)
|
||||
}
|
||||
|
||||
fun mirror(declarationMapper: DeclarationMapper, type: Type): TypeMirror = when (type) {
|
||||
is PrimitiveType -> mirrorPrimitiveType(type, declarationMapper)
|
||||
|
||||
is RecordType -> byRefTypeMirror(declarationMapper.getKotlinClassForPointed(type.decl).type)
|
||||
|
||||
is EnumType -> {
|
||||
val pkg = declarationMapper.getPackageFor(type.def)
|
||||
val kotlinName = declarationMapper.getKotlinNameForValue(type.def)
|
||||
.let { mangleSimple(it) } // enum class requires additional mangling
|
||||
|
||||
when {
|
||||
declarationMapper.isMappedToStrict(type.def) -> {
|
||||
val bridgedType = (type.def.baseType.unwrapTypedefs() as PrimitiveType).getBridgedType(declarationMapper)
|
||||
val clazz = Classifier.topLevel(pkg, kotlinName)
|
||||
val info = TypeInfo.Enum(clazz, bridgedType)
|
||||
TypeMirror.ByValue(clazz.nested("Var").type, info, clazz.type)
|
||||
}
|
||||
!type.def.isAnonymous -> {
|
||||
val baseTypeMirror = mirror(declarationMapper, type.def.baseType)
|
||||
TypeMirror.ByValue(
|
||||
Classifier.topLevel(pkg, kotlinName + "Var").typeAbbreviation(baseTypeMirror.pointedType),
|
||||
baseTypeMirror.info,
|
||||
Classifier.topLevel(pkg, kotlinName).typeAbbreviation(baseTypeMirror.argType)
|
||||
)
|
||||
}
|
||||
else -> mirror(declarationMapper, type.def.baseType)
|
||||
}
|
||||
}
|
||||
|
||||
is PointerType -> {
|
||||
val pointeeType = type.pointeeType
|
||||
val unwrappedPointeeType = pointeeType.unwrapTypedefs()
|
||||
if (unwrappedPointeeType is VoidType) {
|
||||
val info = TypeInfo.Pointer(KotlinTypes.cOpaque, pointeeType)
|
||||
TypeMirror.ByValue(KotlinTypes.cOpaquePointerVar, info, KotlinTypes.cOpaquePointer)
|
||||
} else if (unwrappedPointeeType is ArrayType) {
|
||||
mirror(declarationMapper, pointeeType)
|
||||
} else {
|
||||
val pointeeMirror = mirror(declarationMapper, pointeeType)
|
||||
val info = TypeInfo.Pointer(pointeeMirror.pointedType, pointeeType)
|
||||
TypeMirror.ByValue(
|
||||
KotlinTypes.cPointerVar.typeWith(pointeeMirror.pointedType),
|
||||
info,
|
||||
KotlinTypes.cPointer.typeWith(pointeeMirror.pointedType)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
is ArrayType -> {
|
||||
// TODO: array type doesn't exactly correspond neither to pointer nor to value.
|
||||
val elemTypeMirror = mirror(declarationMapper, type.elemType)
|
||||
if (type.elemType.unwrapTypedefs() is ArrayType) {
|
||||
elemTypeMirror
|
||||
} else {
|
||||
val info = TypeInfo.Pointer(elemTypeMirror.pointedType, type.elemType)
|
||||
TypeMirror.ByValue(
|
||||
KotlinTypes.cArrayPointerVar.typeWith(elemTypeMirror.pointedType),
|
||||
info,
|
||||
KotlinTypes.cArrayPointer.typeWith(elemTypeMirror.pointedType)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
is FunctionType -> byRefTypeMirror(KotlinTypes.cFunction.typeWith(getKotlinFunctionType(declarationMapper, type)))
|
||||
|
||||
is Typedef -> {
|
||||
val baseType = mirror(declarationMapper, type.def.aliased)
|
||||
val pkg = declarationMapper.getPackageFor(type.def)
|
||||
|
||||
val name = type.def.name
|
||||
when (baseType) {
|
||||
is TypeMirror.ByValue -> {
|
||||
val valueType = Classifier.topLevel(pkg, name).typeAbbreviation(baseType.valueType)
|
||||
val underlyingPointedType = if (baseType.info is TypeInfo.Pointer) {
|
||||
KotlinTypes.cPointerVarOf.typeWith(valueType)
|
||||
} else {
|
||||
baseType.pointedType
|
||||
}
|
||||
val pointedType = Classifier.topLevel(pkg, "${name}Var").typeAbbreviation(underlyingPointedType)
|
||||
TypeMirror.ByValue(
|
||||
pointedType,
|
||||
baseType.info,
|
||||
valueType,
|
||||
nullable = baseType.nullable)
|
||||
}
|
||||
|
||||
is TypeMirror.ByRef -> TypeMirror.ByRef(
|
||||
Classifier.topLevel(pkg, name).typeAbbreviation(baseType.pointedType),
|
||||
baseType.info
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
is ObjCPointer -> objCPointerMirror(declarationMapper, type)
|
||||
|
||||
else -> TODO(type.toString())
|
||||
}
|
||||
|
||||
internal tailrec fun ObjCClass.isNSStringOrSubclass(): Boolean = when (this.name) {
|
||||
"NSMutableString", // fast path and handling for forward declarations.
|
||||
"NSString" -> true
|
||||
else -> {
|
||||
val baseClass = this.baseClass
|
||||
if (baseClass != null) {
|
||||
baseClass.isNSStringOrSubclass()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun ObjCClass.isNSStringSubclass(): Boolean = this.baseClass?.isNSStringOrSubclass() == true
|
||||
|
||||
private fun objCPointerMirror(declarationMapper: DeclarationMapper, type: ObjCPointer): TypeMirror.ByValue {
|
||||
if (type is ObjCObjectPointer && type.def.isNSStringOrSubclass()) {
|
||||
val valueType = KotlinTypes.string
|
||||
return objCMirror(valueType, TypeInfo.ObjCPointerInfo(valueType, type), type.isNullable)
|
||||
}
|
||||
|
||||
val valueType = when (type) {
|
||||
is ObjCIdType -> {
|
||||
type.protocols.firstOrNull()?.let { declarationMapper.getKotlinClassFor(it) }?.type
|
||||
?: KotlinTypes.any
|
||||
}
|
||||
is ObjCClassPointer -> KotlinTypes.objCClass.type
|
||||
is ObjCObjectPointer -> {
|
||||
when (type.def.name) {
|
||||
"NSArray" -> KotlinTypes.list.typeWith(StarProjection)
|
||||
"NSMutableArray" -> KotlinTypes.mutableList.typeWith(KotlinTypes.any.makeNullable())
|
||||
"NSSet" -> KotlinTypes.set.typeWith(StarProjection)
|
||||
"NSDictionary" -> KotlinTypes.map.typeWith(KotlinTypes.any.makeNullable(), StarProjection)
|
||||
else -> declarationMapper.getKotlinClassFor(type.def).type
|
||||
}
|
||||
}
|
||||
is ObjCInstanceType -> TODO(type.toString()) // Must have already been handled.
|
||||
is ObjCBlockPointer -> return objCBlockPointerMirror(declarationMapper, type)
|
||||
}
|
||||
|
||||
return objCMirror(valueType, TypeInfo.ObjCPointerInfo(valueType, type), type.isNullable)
|
||||
}
|
||||
|
||||
private fun objCBlockPointerMirror(declarationMapper: DeclarationMapper, type: ObjCBlockPointer): TypeMirror.ByValue {
|
||||
val returnType = if (type.returnType.unwrapTypedefs() is VoidType) {
|
||||
KotlinTypes.unit
|
||||
} else {
|
||||
mirror(declarationMapper, type.returnType).argType
|
||||
}
|
||||
val kotlinType = KotlinFunctionType(
|
||||
type.parameterTypes.map { mirror(declarationMapper, it).argType },
|
||||
returnType
|
||||
)
|
||||
|
||||
val info = TypeInfo.ObjCBlockPointerInfo(kotlinType, type)
|
||||
return objCMirror(kotlinType, info, type.isNullable)
|
||||
}
|
||||
|
||||
private fun objCMirror(valueType: KotlinType, info: TypeInfo, nullable: Boolean) = TypeMirror.ByValue(
|
||||
info.constructPointedType(valueType.makeNullableAsSpecified(nullable)),
|
||||
info,
|
||||
valueType.makeNullable(), // All typedefs to Objective-C pointers would be nullable for simplicity
|
||||
nullable
|
||||
)
|
||||
|
||||
fun getKotlinFunctionType(declarationMapper: DeclarationMapper, type: FunctionType): KotlinFunctionType {
|
||||
val returnType = if (type.returnType.unwrapTypedefs() is VoidType) {
|
||||
KotlinTypes.unit
|
||||
} else {
|
||||
mirror(declarationMapper, type.returnType).argType
|
||||
}
|
||||
return KotlinFunctionType(
|
||||
type.parameterTypes.map { mirror(declarationMapper, it).argType },
|
||||
returnType,
|
||||
nullable = false
|
||||
)
|
||||
}
|
||||
|
||||
+602
@@ -0,0 +1,602 @@
|
||||
/*
|
||||
* 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.native.interop.gen
|
||||
|
||||
import org.jetbrains.kotlin.native.interop.indexer.*
|
||||
|
||||
internal fun ObjCMethod.getKotlinParameterNames(forConstructorOrFactory: Boolean = false): List<String> {
|
||||
val selectorParts = this.selector.split(":")
|
||||
|
||||
val result = mutableListOf<String>()
|
||||
|
||||
fun String.mangled(): String {
|
||||
var mangled = this
|
||||
while (mangled in result) {
|
||||
mangled = "_$mangled"
|
||||
}
|
||||
return mangled
|
||||
}
|
||||
|
||||
// The names of all parameters except first must depend only on the selector:
|
||||
this.parameters.forEachIndexed { index, _ ->
|
||||
if (index > 0) {
|
||||
val name = selectorParts[index].takeIf { it.isNotEmpty() } ?: "_$index"
|
||||
result.add(name.mangled())
|
||||
}
|
||||
}
|
||||
|
||||
this.parameters.firstOrNull()?.let {
|
||||
val name = this.getFirstKotlinParameterNameCandidate(forConstructorOrFactory)
|
||||
result.add(0, name.mangled())
|
||||
}
|
||||
|
||||
if (this.isVariadic) {
|
||||
result.add("args".mangled())
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private fun ObjCMethod.getFirstKotlinParameterNameCandidate(forConstructorOrFactory: Boolean): String {
|
||||
if (forConstructorOrFactory) {
|
||||
val selectorPart = this.selector.takeWhile { it != ':' }.trimStart('_')
|
||||
if (selectorPart.startsWith("init")) {
|
||||
selectorPart.removePrefix("init").removePrefix("With")
|
||||
.takeIf { it.isNotEmpty() }?.let { return it.decapitalize() }
|
||||
}
|
||||
}
|
||||
|
||||
return this.parameters.first().name?.takeIf { it.isNotEmpty() } ?: "_0"
|
||||
}
|
||||
|
||||
private fun ObjCMethod.getKotlinParameters(
|
||||
stubIrBuilder: StubsBuildingContext,
|
||||
forConstructorOrFactory: Boolean
|
||||
): List<FunctionParameterStub> {
|
||||
if (this.isInit && this.parameters.isEmpty() && this.selector != "init") {
|
||||
// Create synthetic Unit parameter, just like Swift does in this case:
|
||||
val parameterName = this.selector.removePrefix("init").removePrefix("With").decapitalize()
|
||||
return listOf(FunctionParameterStub(parameterName, KotlinTypes.unit.toStubIrType()))
|
||||
// Note: this parameter is explicitly handled in compiler.
|
||||
}
|
||||
|
||||
val names = getKotlinParameterNames(forConstructorOrFactory) // TODO: consider refactoring.
|
||||
val result = mutableListOf<FunctionParameterStub>()
|
||||
|
||||
this.parameters.mapIndexedTo(result) { index, it ->
|
||||
val kotlinType = stubIrBuilder.mirror(it.type).argType
|
||||
val name = names[index]
|
||||
val annotations = if (it.nsConsumed) listOf(AnnotationStub.ObjC.Consumed) else emptyList()
|
||||
FunctionParameterStub(name, kotlinType.toStubIrType(), isVararg = false, annotations = annotations)
|
||||
}
|
||||
if (this.isVariadic) {
|
||||
result += FunctionParameterStub(
|
||||
names.last(),
|
||||
KotlinTypes.any.makeNullable().toStubIrType(),
|
||||
isVararg = true,
|
||||
annotations = emptyList()
|
||||
)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private class ObjCMethodStubBuilder(
|
||||
private val method: ObjCMethod,
|
||||
private val container: ObjCContainer,
|
||||
private val isDesignatedInitializer: Boolean,
|
||||
override val context: StubsBuildingContext
|
||||
) : StubElementBuilder {
|
||||
private val isStret: Boolean
|
||||
private val stubReturnType: StubType
|
||||
val annotations = mutableListOf<AnnotationStub>()
|
||||
private val kotlinMethodParameters: List<FunctionParameterStub>
|
||||
private val external: Boolean
|
||||
private val receiver: ReceiverParameterStub?
|
||||
private val name: String = method.kotlinName
|
||||
private val origin = StubOrigin.ObjCMethod(method, container)
|
||||
private val modality: MemberStubModality
|
||||
private val isOverride: Boolean =
|
||||
container is ObjCClassOrProtocol && method.isOverride(container)
|
||||
|
||||
init {
|
||||
val returnType = method.getReturnType(container.classOrProtocol)
|
||||
isStret = returnType.isStret(context.configuration.target)
|
||||
stubReturnType = if (returnType.unwrapTypedefs() is VoidType) {
|
||||
KotlinTypes.unit
|
||||
} else {
|
||||
context.mirror(returnType).argType
|
||||
}.toStubIrType()
|
||||
val methodAnnotation = AnnotationStub.ObjC.Method(
|
||||
method.selector,
|
||||
method.encoding,
|
||||
isStret
|
||||
)
|
||||
annotations += buildObjCMethodAnnotations(methodAnnotation)
|
||||
kotlinMethodParameters = method.getKotlinParameters(context, forConstructorOrFactory = false)
|
||||
external = (container !is ObjCProtocol)
|
||||
modality = when (container) {
|
||||
is ObjCClass -> MemberStubModality.OPEN
|
||||
is ObjCProtocol -> if (method.isOptional) MemberStubModality.OPEN else MemberStubModality.ABSTRACT
|
||||
is ObjCCategory -> MemberStubModality.FINAL
|
||||
}
|
||||
receiver = if (container is ObjCCategory) {
|
||||
val receiverType = ClassifierStubType(context.getKotlinClassFor(container.clazz, isMeta = method.isClass))
|
||||
ReceiverParameterStub(receiverType)
|
||||
} else null
|
||||
}
|
||||
|
||||
private fun buildObjCMethodAnnotations(main: AnnotationStub): List<AnnotationStub> = listOfNotNull(
|
||||
main,
|
||||
AnnotationStub.ObjC.ConsumesReceiver.takeIf { method.nsConsumesSelf },
|
||||
AnnotationStub.ObjC.ReturnsRetained.takeIf { method.nsReturnsRetained }
|
||||
)
|
||||
|
||||
fun isDefaultConstructor(): Boolean =
|
||||
method.isInit && method.parameters.isEmpty()
|
||||
|
||||
override fun build(): List<FunctionalStub> {
|
||||
val replacement = if (method.isInit) {
|
||||
val parameters = method.getKotlinParameters(context, forConstructorOrFactory = true)
|
||||
when (container) {
|
||||
is ObjCClass -> {
|
||||
annotations.add(0, deprecatedInit(
|
||||
container.kotlinClassName(method.isClass),
|
||||
kotlinMethodParameters.map { it.name },
|
||||
factory = false
|
||||
))
|
||||
val designated = isDesignatedInitializer ||
|
||||
context.configuration.disableDesignatedInitializerChecks
|
||||
|
||||
val annotations = listOf(AnnotationStub.ObjC.Constructor(method.selector, designated))
|
||||
val constructor = ConstructorStub(parameters, annotations, isPrimary = false, origin = origin)
|
||||
constructor
|
||||
}
|
||||
is ObjCCategory -> {
|
||||
assert(!method.isClass)
|
||||
|
||||
|
||||
val clazz = context.getKotlinClassFor(container.clazz, isMeta = false).type
|
||||
|
||||
annotations.add(0, deprecatedInit(
|
||||
clazz.classifier.getRelativeFqName(),
|
||||
kotlinMethodParameters.map { it.name },
|
||||
factory = true
|
||||
))
|
||||
|
||||
val factoryAnnotation = AnnotationStub.ObjC.Factory(
|
||||
method.selector,
|
||||
method.encoding,
|
||||
isStret
|
||||
)
|
||||
val annotations = buildObjCMethodAnnotations(factoryAnnotation)
|
||||
|
||||
val originalReturnType = method.getReturnType(container.clazz)
|
||||
val typeParameter = TypeParameterStub("T", clazz.toStubIrType())
|
||||
val returnType = if (originalReturnType is ObjCPointer) {
|
||||
typeParameter.getStubType(originalReturnType.isNullable)
|
||||
} else {
|
||||
// This shouldn't happen actually.
|
||||
this.stubReturnType
|
||||
}
|
||||
val typeArgument = TypeArgumentStub(typeParameter.getStubType(false))
|
||||
val receiverType = ClassifierStubType(KotlinTypes.objCClassOf, listOf(typeArgument))
|
||||
val receiver = ReceiverParameterStub(receiverType)
|
||||
val createMethod = FunctionStub(
|
||||
"create",
|
||||
returnType,
|
||||
parameters,
|
||||
receiver = receiver,
|
||||
typeParameters = listOf(typeParameter),
|
||||
external = true,
|
||||
origin = StubOrigin.ObjCCategoryInitMethod(method),
|
||||
annotations = annotations,
|
||||
modality = MemberStubModality.FINAL
|
||||
)
|
||||
createMethod
|
||||
}
|
||||
is ObjCProtocol -> null
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
return listOfNotNull(
|
||||
FunctionStub(
|
||||
name,
|
||||
stubReturnType,
|
||||
kotlinMethodParameters.toList(),
|
||||
origin,
|
||||
annotations.toList(),
|
||||
external,
|
||||
receiver,
|
||||
modality,
|
||||
emptyList(),
|
||||
isOverride),
|
||||
replacement
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
internal val ObjCContainer.classOrProtocol: ObjCClassOrProtocol
|
||||
get() = when (this) {
|
||||
is ObjCClassOrProtocol -> this
|
||||
is ObjCCategory -> this.clazz
|
||||
}
|
||||
|
||||
private fun deprecatedInit(className: String, initParameterNames: List<String>, factory: Boolean): AnnotationStub {
|
||||
val replacement = if (factory) "$className.create" else className
|
||||
val replacementKind = if (factory) "factory method" else "constructor"
|
||||
val replaceWith = "$replacement(${initParameterNames.joinToString { it.asSimpleName() }})"
|
||||
return AnnotationStub.Deprecated("Use $replacementKind instead", replaceWith, DeprecationLevel.ERROR)
|
||||
}
|
||||
|
||||
internal val ObjCMethod.kotlinName: String
|
||||
get() {
|
||||
val candidate = selector.split(":").first()
|
||||
val trimmed = candidate.trimEnd('_')
|
||||
return if (trimmed == "equals" && parameters.size == 1
|
||||
|| (trimmed == "hashCode" || trimmed == "toString") && parameters.size == 0) {
|
||||
candidate + "_"
|
||||
} else {
|
||||
candidate
|
||||
}
|
||||
}
|
||||
|
||||
internal val ObjCClassOrProtocol.protocolsWithSupers: Sequence<ObjCProtocol>
|
||||
get() = this.protocols.asSequence().flatMap { sequenceOf(it) + it.protocolsWithSupers }
|
||||
|
||||
internal val ObjCClassOrProtocol.immediateSuperTypes: Sequence<ObjCClassOrProtocol>
|
||||
get() {
|
||||
val baseClass = (this as? ObjCClass)?.baseClass
|
||||
if (baseClass != null) {
|
||||
return sequenceOf(baseClass) + this.protocols.asSequence()
|
||||
}
|
||||
|
||||
return this.protocols.asSequence()
|
||||
}
|
||||
|
||||
internal val ObjCClassOrProtocol.selfAndSuperTypes: Sequence<ObjCClassOrProtocol>
|
||||
get() = sequenceOf(this) + this.superTypes
|
||||
|
||||
internal val ObjCClassOrProtocol.superTypes: Sequence<ObjCClassOrProtocol>
|
||||
get() = this.immediateSuperTypes.flatMap { it.selfAndSuperTypes }.distinct()
|
||||
|
||||
internal fun ObjCClassOrProtocol.declaredMethods(isClass: Boolean): Sequence<ObjCMethod> =
|
||||
this.methods.asSequence().filter { it.isClass == isClass }
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
internal fun Sequence<ObjCMethod>.inheritedTo(container: ObjCClassOrProtocol, isMeta: Boolean): Sequence<ObjCMethod> =
|
||||
this // TODO: exclude methods that are marked as unavailable in [container].
|
||||
|
||||
internal fun ObjCClassOrProtocol.inheritedMethods(isClass: Boolean): Sequence<ObjCMethod> =
|
||||
this.immediateSuperTypes.flatMap { it.methodsWithInherited(isClass) }
|
||||
.distinctBy { it.selector }
|
||||
.inheritedTo(this, isClass)
|
||||
|
||||
internal fun ObjCClassOrProtocol.methodsWithInherited(isClass: Boolean): Sequence<ObjCMethod> =
|
||||
(this.declaredMethods(isClass) + this.inheritedMethods(isClass)).distinctBy { it.selector }
|
||||
|
||||
internal fun ObjCClass.getDesignatedInitializerSelectors(result: MutableSet<String>): Set<String> {
|
||||
// Note: Objective-C initializers act as usual methods and thus are inherited by subclasses.
|
||||
// Swift considers all super initializers to be available (unless otherwise specified explicitly),
|
||||
// but seems to consider them as non-designated if class declares its own ones explicitly.
|
||||
// Simulate the similar behaviour:
|
||||
val explicitlyDesignatedInitializers = this.methods.filter { it.isExplicitlyDesignatedInitializer && !it.isClass }
|
||||
|
||||
if (explicitlyDesignatedInitializers.isNotEmpty()) {
|
||||
explicitlyDesignatedInitializers.mapTo(result) { it.selector }
|
||||
} else {
|
||||
this.declaredMethods(isClass = false).filter { it.isInit }.mapTo(result) { it.selector }
|
||||
this.baseClass?.getDesignatedInitializerSelectors(result)
|
||||
}
|
||||
|
||||
this.superTypes.filterIsInstance<ObjCProtocol>()
|
||||
.flatMap { it.declaredMethods(isClass = false) }.filter { it.isInit }
|
||||
.mapTo(result) { it.selector }
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
internal fun ObjCMethod.isOverride(container: ObjCClassOrProtocol): Boolean =
|
||||
container.superTypes.any { superType -> superType.methods.any(this::replaces) }
|
||||
|
||||
internal abstract class ObjCContainerStubBuilder(
|
||||
final override val context: StubsBuildingContext,
|
||||
private val container: ObjCClassOrProtocol,
|
||||
protected val metaContainerStub: ObjCContainerStubBuilder?
|
||||
) : StubElementBuilder {
|
||||
private val isMeta: Boolean get() = metaContainerStub == null
|
||||
|
||||
private val designatedInitializerSelectors = if (container is ObjCClass && !isMeta) {
|
||||
container.getDesignatedInitializerSelectors(mutableSetOf())
|
||||
} else {
|
||||
emptySet()
|
||||
}
|
||||
|
||||
private val methods: List<ObjCMethod>
|
||||
private val properties: List<ObjCProperty>
|
||||
|
||||
private val protocolGetter: String?
|
||||
|
||||
init {
|
||||
val superMethods = container.inheritedMethods(isMeta)
|
||||
|
||||
// Add all methods declared in the class or protocol:
|
||||
var methods = container.declaredMethods(isMeta)
|
||||
|
||||
// Exclude those which are identically declared in super types:
|
||||
methods -= superMethods
|
||||
|
||||
// Add some special methods from super types:
|
||||
methods += superMethods.filter { it.returnsInstancetype() || it.isInit }
|
||||
|
||||
// Add methods from adopted protocols that must be implemented according to Kotlin rules:
|
||||
if (container is ObjCClass) {
|
||||
methods += container.protocolsWithSupers.flatMap { it.declaredMethods(isMeta) }.filter { !it.isOptional }
|
||||
}
|
||||
|
||||
// Add methods inherited from multiple supertypes that must be defined according to Kotlin rules:
|
||||
methods += container.immediateSuperTypes
|
||||
.flatMap { superType ->
|
||||
val methodsWithInherited = superType.methodsWithInherited(isMeta).inheritedTo(container, isMeta)
|
||||
// Select only those which are represented as non-abstract in Kotlin:
|
||||
when (superType) {
|
||||
is ObjCClass -> methodsWithInherited
|
||||
is ObjCProtocol -> methodsWithInherited.filter { it.isOptional }
|
||||
}
|
||||
}
|
||||
.groupBy { it.selector }
|
||||
.mapNotNull { (_, inheritedMethods) -> if (inheritedMethods.size > 1) inheritedMethods.first() else null }
|
||||
|
||||
this.methods = methods.distinctBy { it.selector }.toList()
|
||||
|
||||
this.properties = container.properties.filter { property ->
|
||||
property.getter.isClass == isMeta &&
|
||||
// Select only properties that don't override anything:
|
||||
superMethods.none { property.getter.replaces(it) || property.setter?.replaces(it) ?: false }
|
||||
}
|
||||
}
|
||||
|
||||
private val methodToStub = methods.map {
|
||||
it to ObjCMethodStubBuilder(it, container, it.selector in designatedInitializerSelectors, context)
|
||||
}.toMap()
|
||||
|
||||
private val propertyBuilders = properties.mapNotNull {
|
||||
createObjCPropertyBuilder(context, it, container, this.methodToStub)
|
||||
}
|
||||
|
||||
private val modality = when (container) {
|
||||
is ObjCClass -> ClassStubModality.OPEN
|
||||
is ObjCProtocol -> ClassStubModality.INTERFACE
|
||||
}
|
||||
|
||||
private val classifier = context.getKotlinClassFor(container, isMeta)
|
||||
|
||||
private val externalObjCAnnotation = when (container) {
|
||||
is ObjCProtocol -> {
|
||||
protocolGetter = if (metaContainerStub != null) {
|
||||
metaContainerStub.protocolGetter!!
|
||||
} else {
|
||||
// TODO: handle the case when protocol getter stub can't be compiled.
|
||||
context.generateNextUniqueId("kniprot_")
|
||||
}
|
||||
AnnotationStub.ObjC.ExternalClass(protocolGetter)
|
||||
}
|
||||
is ObjCClass -> {
|
||||
protocolGetter = null
|
||||
val binaryName = container.binaryName
|
||||
AnnotationStub.ObjC.ExternalClass("", binaryName ?: "")
|
||||
}
|
||||
}
|
||||
|
||||
private val interfaces: List<StubType> by lazy {
|
||||
val interfaces = mutableListOf<StubType>()
|
||||
if (container is ObjCClass) {
|
||||
val baseClass = container.baseClass
|
||||
val baseClassifier = if (baseClass != null) {
|
||||
context.getKotlinClassFor(baseClass, isMeta)
|
||||
} else {
|
||||
if (isMeta) KotlinTypes.objCObjectBaseMeta else KotlinTypes.objCObjectBase
|
||||
}
|
||||
interfaces += baseClassifier.type.toStubIrType()
|
||||
}
|
||||
container.protocols.forEach {
|
||||
interfaces += context.getKotlinClassFor(it, isMeta).type.toStubIrType()
|
||||
}
|
||||
if (interfaces.isEmpty()) {
|
||||
assert(container is ObjCProtocol)
|
||||
val classifier = if (isMeta) KotlinTypes.objCObjectMeta else KotlinTypes.objCObject
|
||||
interfaces += classifier.type.toStubIrType()
|
||||
}
|
||||
if (!isMeta && container.isProtocolClass()) {
|
||||
// TODO: map Protocol type to ObjCProtocol instead.
|
||||
interfaces += KotlinTypes.objCProtocol.type.toStubIrType()
|
||||
}
|
||||
interfaces
|
||||
}
|
||||
|
||||
private fun buildBody(): Pair<List<PropertyStub>, List<FunctionalStub>> {
|
||||
val defaultConstructor = if (container is ObjCClass && methodToStub.values.none { it.isDefaultConstructor() }) {
|
||||
// Always generate default constructor.
|
||||
// If it is not produced for an init method, then include it manually:
|
||||
ConstructorStub(
|
||||
isPrimary = false,
|
||||
visibility = VisibilityModifier.PROTECTED,
|
||||
origin = StubOrigin.Synthetic.DefaultConstructor)
|
||||
} else null
|
||||
|
||||
return Pair(
|
||||
propertyBuilders.flatMap { it.build() },
|
||||
methodToStub.values.flatMap { it.build() } + listOfNotNull(defaultConstructor)
|
||||
)
|
||||
}
|
||||
|
||||
protected fun buildClassStub(origin: StubOrigin, companion: ClassStub.Companion? = null): ClassStub {
|
||||
val (properties, methods) = buildBody()
|
||||
return ClassStub.Simple(
|
||||
classifier,
|
||||
properties = properties,
|
||||
methods = methods.filterIsInstance<FunctionStub>(),
|
||||
constructors = methods.filterIsInstance<ConstructorStub>(),
|
||||
origin = origin,
|
||||
modality = modality,
|
||||
annotations = listOf(externalObjCAnnotation),
|
||||
interfaces = interfaces,
|
||||
companion = companion
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class ObjCClassOrProtocolStubBuilder(
|
||||
context: StubsBuildingContext,
|
||||
private val container: ObjCClassOrProtocol
|
||||
) : ObjCContainerStubBuilder(
|
||||
context,
|
||||
container,
|
||||
metaContainerStub = object : ObjCContainerStubBuilder(context, container, metaContainerStub = null) {
|
||||
|
||||
override fun build(): List<StubIrElement> {
|
||||
val origin = when (container) {
|
||||
is ObjCProtocol -> StubOrigin.ObjCProtocol(container, isMeta = true)
|
||||
is ObjCClass -> StubOrigin.ObjCClass(container, isMeta = true)
|
||||
}
|
||||
return listOf(buildClassStub(origin))
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
internal class ObjCProtocolStubBuilder(
|
||||
context: StubsBuildingContext,
|
||||
private val protocol: ObjCProtocol
|
||||
) : ObjCClassOrProtocolStubBuilder(context, protocol), StubElementBuilder {
|
||||
override fun build(): List<StubIrElement> {
|
||||
val classStub = buildClassStub(StubOrigin.ObjCProtocol(protocol, isMeta = false))
|
||||
return listOf(*metaContainerStub!!.build().toTypedArray(), classStub)
|
||||
}
|
||||
}
|
||||
|
||||
internal class ObjCClassStubBuilder(
|
||||
context: StubsBuildingContext,
|
||||
private val clazz: ObjCClass
|
||||
) : ObjCClassOrProtocolStubBuilder(context, clazz), StubElementBuilder {
|
||||
override fun build(): List<StubIrElement> {
|
||||
val companionSuper = ClassifierStubType(context.getKotlinClassFor(clazz, isMeta = true))
|
||||
|
||||
val objCClassType = KotlinTypes.objCClassOf.typeWith(
|
||||
context.getKotlinClassFor(clazz, isMeta = false).type
|
||||
).toStubIrType()
|
||||
|
||||
val superClassInit = SuperClassInit(companionSuper)
|
||||
val companionClassifier = context.getKotlinClassFor(clazz, isMeta = false).nested("Companion")
|
||||
val companion = ClassStub.Companion(companionClassifier, emptyList(), superClassInit, listOf(objCClassType))
|
||||
val classStub = buildClassStub(StubOrigin.ObjCClass(clazz, isMeta = false), companion)
|
||||
return listOf(*metaContainerStub!!.build().toTypedArray(), classStub)
|
||||
}
|
||||
}
|
||||
|
||||
class GeneratedObjCCategoriesMembers {
|
||||
private val propertyNames = mutableSetOf<String>()
|
||||
private val instanceMethodSelectors = mutableSetOf<String>()
|
||||
private val classMethodSelectors = mutableSetOf<String>()
|
||||
|
||||
fun register(method: ObjCMethod): Boolean =
|
||||
(if (method.isClass) classMethodSelectors else instanceMethodSelectors).add(method.selector)
|
||||
|
||||
fun register(property: ObjCProperty): Boolean = propertyNames.add(property.name)
|
||||
|
||||
}
|
||||
|
||||
internal class ObjCCategoryStubBuilder(
|
||||
override val context: StubsBuildingContext,
|
||||
private val category: ObjCCategory
|
||||
) : StubElementBuilder {
|
||||
private val generatedMembers = context.generatedObjCCategoriesMembers
|
||||
.getOrPut(category.clazz, { GeneratedObjCCategoriesMembers() })
|
||||
|
||||
private val methodToBuilder = category.methods.filter { generatedMembers.register(it) }.map {
|
||||
it to ObjCMethodStubBuilder(it, category, isDesignatedInitializer = false, context = context)
|
||||
}.toMap()
|
||||
|
||||
private val methodBuilders get() = methodToBuilder.values
|
||||
|
||||
private val propertyBuilders = category.properties.filter { generatedMembers.register(it) }.mapNotNull {
|
||||
createObjCPropertyBuilder(context, it, category, methodToBuilder)
|
||||
}
|
||||
|
||||
override fun build(): List<StubIrElement> {
|
||||
val description = "${category.clazz.name} (${category.name})"
|
||||
val meta = StubContainerMeta(
|
||||
"// @interface $description",
|
||||
"// @end; // $description"
|
||||
)
|
||||
val container = SimpleStubContainer(
|
||||
meta = meta,
|
||||
functions = methodBuilders.flatMap { it.build() },
|
||||
properties = propertyBuilders.flatMap { it.build() }
|
||||
)
|
||||
return listOf(container)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createObjCPropertyBuilder(
|
||||
context: StubsBuildingContext,
|
||||
property: ObjCProperty,
|
||||
container: ObjCContainer,
|
||||
methodToStub: Map<ObjCMethod, ObjCMethodStubBuilder>
|
||||
): ObjCPropertyStubBuilder? {
|
||||
// Note: the code below assumes that if the property is generated,
|
||||
// then its accessors are also generated as explicit methods.
|
||||
val getterStub = methodToStub[property.getter] ?: return null
|
||||
val setterStub = property.setter?.let { methodToStub[it] ?: return null }
|
||||
return ObjCPropertyStubBuilder(context, property, container, getterStub, setterStub)
|
||||
}
|
||||
|
||||
private class ObjCPropertyStubBuilder(
|
||||
override val context: StubsBuildingContext,
|
||||
private val property: ObjCProperty,
|
||||
private val container: ObjCContainer,
|
||||
private val getterBuilder: ObjCMethodStubBuilder,
|
||||
private val setterMethod: ObjCMethodStubBuilder?
|
||||
) : StubElementBuilder {
|
||||
override fun build(): List<PropertyStub> {
|
||||
val type = property.getType(container.classOrProtocol)
|
||||
val kotlinType = context.mirror(type).argType
|
||||
val getter = PropertyAccessor.Getter.ExternalGetter(annotations = getterBuilder.annotations)
|
||||
val setter = property.setter?.let { PropertyAccessor.Setter.ExternalSetter(annotations = setterMethod!!.annotations) }
|
||||
val kind = setter?.let { PropertyStub.Kind.Var(getter, it) } ?: PropertyStub.Kind.Val(getter)
|
||||
val modality = MemberStubModality.FINAL
|
||||
val receiver = when (container) {
|
||||
is ObjCClassOrProtocol -> null
|
||||
is ObjCCategory -> ClassifierStubType(context.getKotlinClassFor(container.clazz, isMeta = property.getter.isClass))
|
||||
}
|
||||
val origin = StubOrigin.ObjCProperty(property, container)
|
||||
return listOf(PropertyStub(mangleSimple(property.name), kotlinType.toStubIrType(), kind, modality, receiver, origin = origin))
|
||||
}
|
||||
}
|
||||
|
||||
fun ObjCClassOrProtocol.kotlinClassName(isMeta: Boolean): String {
|
||||
val baseClassName = when (this) {
|
||||
is ObjCClass -> this.name
|
||||
is ObjCProtocol -> "${this.name}Protocol"
|
||||
}
|
||||
|
||||
return if (isMeta) "${baseClassName}Meta" else baseClassName
|
||||
}
|
||||
|
||||
internal fun ObjCClassOrProtocol.isProtocolClass(): Boolean = when (this) {
|
||||
is ObjCClass -> (name == "Protocol" || binaryName == "Protocol")
|
||||
is ObjCProtocol -> false
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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.native.interop.gen
|
||||
|
||||
/**
|
||||
* The type which has exact counterparts on both Kotlin and native side and can be directly passed through bridges.
|
||||
*/
|
||||
enum class BridgedType(val kotlinType: KotlinClassifierType, val convertor: String? = null) {
|
||||
BYTE(KotlinTypes.byte, "toByte"),
|
||||
SHORT(KotlinTypes.short, "toShort"),
|
||||
INT(KotlinTypes.int, "toInt"),
|
||||
LONG(KotlinTypes.long, "toLong"),
|
||||
UBYTE(KotlinTypes.uByte, "toUByte"),
|
||||
USHORT(KotlinTypes.uShort, "toUShort"),
|
||||
UINT(KotlinTypes.uInt, "toUInt"),
|
||||
ULONG(KotlinTypes.uLong, "toULong"),
|
||||
FLOAT(KotlinTypes.float, "toFloat"),
|
||||
DOUBLE(KotlinTypes.double, "toDouble"),
|
||||
VECTOR128(KotlinTypes.vector128),
|
||||
NATIVE_PTR(KotlinTypes.nativePtr),
|
||||
OBJC_POINTER(KotlinTypes.nativePtr),
|
||||
VOID(KotlinTypes.unit)
|
||||
}
|
||||
|
||||
data class BridgeTypedKotlinValue(val type: BridgedType, val value: KotlinExpression)
|
||||
data class BridgeTypedNativeValue(val type: BridgedType, val value: NativeExpression)
|
||||
|
||||
/**
|
||||
* The entity which depends on native bridges.
|
||||
*/
|
||||
interface NativeBacked
|
||||
|
||||
/**
|
||||
* Generates simple bridges between Kotlin and native, passing [BridgedType] values.
|
||||
*/
|
||||
interface SimpleBridgeGenerator {
|
||||
|
||||
val topLevelNativeScope: NativeScope
|
||||
|
||||
/**
|
||||
* Generates the expression to convert given Kotlin values to native counterparts, pass through the bridge,
|
||||
* use inside the native code produced by [block] and then return the result back.
|
||||
*
|
||||
* @param block produces native code lines into the builder and returns the expression to be used as the result.
|
||||
*/
|
||||
fun kotlinToNative(
|
||||
nativeBacked: NativeBacked,
|
||||
returnType: BridgedType,
|
||||
kotlinValues: List<BridgeTypedKotlinValue>,
|
||||
independent: Boolean,
|
||||
block: NativeCodeBuilder.(nativeValues: List<NativeExpression>) -> NativeExpression
|
||||
): KotlinExpression
|
||||
|
||||
/**
|
||||
* Generates the expression to convert given native values to Kotlin counterparts, pass through the bridge,
|
||||
* use inside the Kotlin code produced by [block] and then return the result back.
|
||||
*/
|
||||
fun nativeToKotlin(
|
||||
nativeBacked: NativeBacked,
|
||||
returnType: BridgedType,
|
||||
nativeValues: List<BridgeTypedNativeValue>,
|
||||
block: KotlinCodeBuilder.(kotlinValues: List<KotlinExpression>) -> KotlinExpression
|
||||
): NativeExpression
|
||||
|
||||
fun insertNativeBridge(
|
||||
nativeBacked: NativeBacked,
|
||||
kotlinLines: List<String>,
|
||||
nativeLines: List<String>
|
||||
)
|
||||
|
||||
/**
|
||||
* Prepares all requested native bridges.
|
||||
*/
|
||||
fun prepare(): NativeBridges
|
||||
}
|
||||
|
||||
interface NativeBridges {
|
||||
/**
|
||||
* @return `true` iff given entity is supported by these bridges,
|
||||
* i.e. all bridges it depends on can be successfully generated.
|
||||
*/
|
||||
fun isSupported(nativeBacked: NativeBacked): Boolean
|
||||
|
||||
val kotlinLines: Sequence<String>
|
||||
val nativeLines: Sequence<String>
|
||||
}
|
||||
|
||||
+253
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
* 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.native.interop.gen
|
||||
|
||||
import org.jetbrains.kotlin.native.interop.gen.jvm.KotlinPlatform
|
||||
import org.jetbrains.kotlin.native.interop.indexer.CompilationWithPCH
|
||||
import org.jetbrains.kotlin.native.interop.indexer.Language
|
||||
import org.jetbrains.kotlin.native.interop.indexer.mapFragmentIsCompilable
|
||||
|
||||
internal val INVALID_CLANG_IDENTIFIER_REGEX = "[^a-zA-Z1-9_]".toRegex()
|
||||
|
||||
class SimpleBridgeGeneratorImpl(
|
||||
private val platform: KotlinPlatform,
|
||||
private val pkgName: String,
|
||||
private val jvmFileClassName: String,
|
||||
private val libraryForCStubs: CompilationWithPCH,
|
||||
override val topLevelNativeScope: NativeScope,
|
||||
private val topLevelKotlinScope: KotlinScope
|
||||
) : SimpleBridgeGenerator {
|
||||
|
||||
private var nextUniqueId = 0
|
||||
|
||||
private val BridgedType.nativeType: String get() = when (platform) {
|
||||
KotlinPlatform.JVM -> when (this) {
|
||||
BridgedType.BYTE -> "jbyte"
|
||||
BridgedType.SHORT -> "jshort"
|
||||
BridgedType.INT -> "jint"
|
||||
BridgedType.LONG -> "jlong"
|
||||
BridgedType.UBYTE -> "jbyte"
|
||||
BridgedType.USHORT -> "jshort"
|
||||
BridgedType.UINT -> "jint"
|
||||
BridgedType.ULONG -> "jlong"
|
||||
BridgedType.FLOAT -> "jfloat"
|
||||
BridgedType.DOUBLE -> "jdouble"
|
||||
BridgedType.VECTOR128 -> TODO()
|
||||
BridgedType.NATIVE_PTR -> "jlong"
|
||||
BridgedType.OBJC_POINTER -> TODO()
|
||||
BridgedType.VOID -> "void"
|
||||
}
|
||||
KotlinPlatform.NATIVE -> when (this) {
|
||||
BridgedType.BYTE -> "int8_t"
|
||||
BridgedType.SHORT -> "int16_t"
|
||||
BridgedType.INT -> "int32_t"
|
||||
BridgedType.LONG -> "int64_t"
|
||||
BridgedType.UBYTE -> "uint8_t"
|
||||
BridgedType.USHORT -> "uint16_t"
|
||||
BridgedType.UINT -> "uint32_t"
|
||||
BridgedType.ULONG -> "uint64_t"
|
||||
BridgedType.FLOAT -> "float"
|
||||
BridgedType.DOUBLE -> "double"
|
||||
BridgedType.VECTOR128 -> TODO() // "float __attribute__ ((__vector_size__ (16)))"
|
||||
BridgedType.NATIVE_PTR -> "void*"
|
||||
BridgedType.OBJC_POINTER -> "id"
|
||||
BridgedType.VOID -> "void"
|
||||
}
|
||||
}
|
||||
|
||||
private inner class NativeBridge(val kotlinLines: List<String>, val nativeLines: List<String>)
|
||||
|
||||
override fun kotlinToNative(
|
||||
nativeBacked: NativeBacked,
|
||||
returnType: BridgedType,
|
||||
kotlinValues: List<BridgeTypedKotlinValue>,
|
||||
independent: Boolean,
|
||||
block: NativeCodeBuilder.(nativeValues: List<NativeExpression>) -> NativeExpression
|
||||
): KotlinExpression {
|
||||
|
||||
val kotlinLines = mutableListOf<String>()
|
||||
val nativeLines = mutableListOf<String>()
|
||||
|
||||
val kotlinFunctionName = "kniBridge${nextUniqueId++}"
|
||||
val kotlinParameters = kotlinValues.withIndex().joinToString {
|
||||
"p${it.index}: ${it.value.type.kotlinType.render(topLevelKotlinScope)}"
|
||||
}
|
||||
|
||||
val callExpr = "$kotlinFunctionName(${kotlinValues.joinToString { it.value }})"
|
||||
|
||||
val cFunctionParameters = when (platform) {
|
||||
KotlinPlatform.JVM -> mutableListOf(
|
||||
"jniEnv" to "JNIEnv*",
|
||||
"jclss" to "jclass"
|
||||
)
|
||||
KotlinPlatform.NATIVE -> mutableListOf()
|
||||
}
|
||||
|
||||
kotlinValues.withIndex().mapTo(cFunctionParameters) {
|
||||
"p${it.index}" to it.value.type.nativeType
|
||||
}
|
||||
|
||||
val joinedCParameters = cFunctionParameters.joinToString { (name, type) -> "$type $name" }
|
||||
val cReturnType = returnType.nativeType
|
||||
|
||||
val cFunctionHeader = when (platform) {
|
||||
KotlinPlatform.JVM -> {
|
||||
val funcFullName = buildString {
|
||||
if (pkgName.isNotEmpty()) {
|
||||
append(pkgName)
|
||||
append('.')
|
||||
}
|
||||
append(jvmFileClassName)
|
||||
append('.')
|
||||
append(kotlinFunctionName)
|
||||
}
|
||||
|
||||
val functionName = "Java_" + funcFullName.replace("_", "_1").replace('.', '_').replace("$", "_00024")
|
||||
"JNIEXPORT $cReturnType JNICALL $functionName ($joinedCParameters)"
|
||||
}
|
||||
KotlinPlatform.NATIVE -> {
|
||||
val functionName = pkgName.replace(INVALID_CLANG_IDENTIFIER_REGEX, "_") + "_$kotlinFunctionName"
|
||||
if (independent) kotlinLines.add("@" + topLevelKotlinScope.reference(KotlinTypes.independent))
|
||||
kotlinLines.add("@SymbolName(${functionName.quoteAsKotlinLiteral()})")
|
||||
"$cReturnType $functionName ($joinedCParameters)"
|
||||
}
|
||||
}
|
||||
nativeLines.add(cFunctionHeader + " {")
|
||||
|
||||
buildNativeCodeLines(topLevelNativeScope) {
|
||||
val cExpr = block(cFunctionParameters.takeLast(kotlinValues.size).map { (name, _) -> name })
|
||||
if (returnType != BridgedType.VOID) {
|
||||
out("return ($cReturnType)$cExpr;")
|
||||
}
|
||||
}.forEach {
|
||||
nativeLines.add(" $it")
|
||||
}
|
||||
|
||||
if (libraryForCStubs.language == Language.OBJECTIVE_C) {
|
||||
// Prevent Objective-C exceptions from passing to Kotlin:
|
||||
nativeLines.add(1, "@try {")
|
||||
nativeLines.add("} @catch (id e) { objc_terminate(); }")
|
||||
// 'objc_terminate' will report the exception.
|
||||
// TODO: consider implementing this in bitcode generator.
|
||||
}
|
||||
|
||||
nativeLines.add("}")
|
||||
val kotlinReturnType = returnType.kotlinType.render(topLevelKotlinScope)
|
||||
kotlinLines.add("private external fun $kotlinFunctionName($kotlinParameters): $kotlinReturnType")
|
||||
|
||||
val nativeBridge = NativeBridge(kotlinLines, nativeLines)
|
||||
nativeBridges.add(nativeBacked to nativeBridge)
|
||||
|
||||
return callExpr
|
||||
}
|
||||
|
||||
override fun nativeToKotlin(
|
||||
nativeBacked: NativeBacked,
|
||||
returnType: BridgedType,
|
||||
nativeValues: List<BridgeTypedNativeValue>,
|
||||
block: KotlinCodeBuilder.(arguments: List<KotlinExpression>) -> KotlinExpression
|
||||
): NativeExpression {
|
||||
|
||||
if (platform != KotlinPlatform.NATIVE) TODO()
|
||||
|
||||
val kotlinLines = mutableListOf<String>()
|
||||
val nativeLines = mutableListOf<String>()
|
||||
|
||||
val kotlinFunctionName = "kniBridge${nextUniqueId++}"
|
||||
val kotlinParameters = nativeValues.withIndex().map {
|
||||
"p${it.index}" to it.value.type.kotlinType
|
||||
}
|
||||
val joinedKotlinParameters = kotlinParameters.joinToString {
|
||||
"${it.first}: ${it.second.render(topLevelKotlinScope)}"
|
||||
}
|
||||
|
||||
val cFunctionParameters = nativeValues.withIndex().map {
|
||||
"p${it.index}" to it.value.type.nativeType
|
||||
}
|
||||
val joinedCParameters = cFunctionParameters.joinToString { (name, type) -> "$type $name" }
|
||||
val cReturnType = returnType.nativeType
|
||||
|
||||
val symbolName = pkgName.replace(INVALID_CLANG_IDENTIFIER_REGEX, "_") + "_$kotlinFunctionName"
|
||||
kotlinLines.add("@kotlin.native.internal.ExportForCppRuntime(${symbolName.quoteAsKotlinLiteral()})")
|
||||
val cFunctionHeader = "$cReturnType $symbolName($joinedCParameters)"
|
||||
|
||||
nativeLines.add("$cFunctionHeader;")
|
||||
val kotlinReturnType = returnType.kotlinType.render(topLevelKotlinScope)
|
||||
kotlinLines.add("private fun $kotlinFunctionName($joinedKotlinParameters): $kotlinReturnType {")
|
||||
|
||||
buildKotlinCodeLines(topLevelKotlinScope) {
|
||||
var kotlinExpr = block(kotlinParameters.map { (name, _) -> name })
|
||||
if (returnType == BridgedType.OBJC_POINTER) {
|
||||
// The Kotlin code may lose the ownership on this pointer after returning from the bridge,
|
||||
// so retain the pointer and autorelease it:
|
||||
kotlinExpr = "objc_retainAutoreleaseReturnValue($kotlinExpr)"
|
||||
// (Objective-C does the same for returned pointers).
|
||||
}
|
||||
returnResult(kotlinExpr)
|
||||
}.forEach {
|
||||
kotlinLines.add(" $it")
|
||||
}
|
||||
|
||||
kotlinLines.add("}")
|
||||
|
||||
insertNativeBridge(nativeBacked, kotlinLines, nativeLines)
|
||||
|
||||
return "$symbolName(${nativeValues.joinToString { it.value }})"
|
||||
|
||||
}
|
||||
|
||||
override fun insertNativeBridge(nativeBacked: NativeBacked, kotlinLines: List<String>, nativeLines: List<String>) {
|
||||
val nativeBridge = NativeBridge(kotlinLines, nativeLines)
|
||||
nativeBridges.add(nativeBacked to nativeBridge)
|
||||
}
|
||||
|
||||
private val nativeBridges = mutableListOf<Pair<NativeBacked, NativeBridge>>()
|
||||
|
||||
override fun prepare(): NativeBridges {
|
||||
val includedBridges = mutableListOf<NativeBridge>()
|
||||
val excludedClients = mutableSetOf<NativeBacked>()
|
||||
|
||||
nativeBridges.map { it.second.nativeLines }
|
||||
.mapFragmentIsCompilable(libraryForCStubs)
|
||||
.forEachIndexed { index, isCompilable ->
|
||||
if (!isCompilable) {
|
||||
excludedClients.add(nativeBridges[index].first)
|
||||
}
|
||||
}
|
||||
|
||||
nativeBridges.mapNotNullTo(includedBridges) { (nativeBacked, nativeBridge) ->
|
||||
if (nativeBacked in excludedClients) {
|
||||
null
|
||||
} else {
|
||||
nativeBridge
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: exclude unused bridges.
|
||||
return object : NativeBridges {
|
||||
|
||||
override val kotlinLines: Sequence<String>
|
||||
get() = includedBridges.asSequence().flatMap { it.kotlinLines.asSequence() }
|
||||
|
||||
override val nativeLines: Sequence<String>
|
||||
get() = includedBridges.asSequence().flatMap { it.nativeLines.asSequence() }
|
||||
|
||||
override fun isSupported(nativeBacked: NativeBacked): Boolean =
|
||||
nativeBacked !in excludedClients
|
||||
}
|
||||
}
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
package org.jetbrains.kotlin.native.interop.gen
|
||||
|
||||
import org.jetbrains.kotlin.native.interop.indexer.*
|
||||
|
||||
fun tryRenderStructOrUnion(def: StructDef): String? = when (def.kind) {
|
||||
StructDef.Kind.STRUCT -> tryRenderStruct(def)
|
||||
StructDef.Kind.UNION -> tryRenderUnion(def)
|
||||
}
|
||||
|
||||
private fun tryRenderStruct(def: StructDef): String? {
|
||||
val isPackedStruct = def.fields.any { !it.isAligned }
|
||||
|
||||
var offset = 0L
|
||||
|
||||
return buildString {
|
||||
append("struct")
|
||||
if (isPackedStruct) append(" __attribute__((packed))")
|
||||
append(" { ")
|
||||
|
||||
def.members.forEachIndexed { index, it ->
|
||||
val name = "p$index"
|
||||
val decl = when (it) {
|
||||
is Field -> {
|
||||
val defaultAlignment = if (isPackedStruct) 1L else it.typeAlign
|
||||
val alignment = guessAlignment(offset, it.offsetBytes, defaultAlignment) ?: return null
|
||||
|
||||
offset = it.offsetBytes + it.typeSize
|
||||
|
||||
tryRenderVar(it.type, name)
|
||||
?.plus(if (alignment == defaultAlignment) "" else "__attribute__((aligned($alignment)))")
|
||||
}
|
||||
|
||||
is BitField, // TODO: tryRenderVar(it.type, name)?.plus(" : ${it.size}")
|
||||
is IncompleteField -> null // e.g. flexible array member.
|
||||
} ?: return null
|
||||
append("$decl; ")
|
||||
}
|
||||
|
||||
append("}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun guessAlignment(offset: Long, paddedOffset: Long, defaultAlignment: Long): Long? =
|
||||
longArrayOf(defaultAlignment, 1L, 2L, 4L, 8L, 16L, 32L).firstOrNull {
|
||||
alignUp(offset, it) == paddedOffset
|
||||
}
|
||||
|
||||
private fun alignUp(x: Long, alignment: Long): Long = (x + alignment - 1) and ((alignment - 1).inv())
|
||||
|
||||
private fun tryRenderUnion(def: StructDef): String? =
|
||||
if (def.members.any { it.offset != 0L }) null else buildString {
|
||||
append("union { ")
|
||||
def.members.forEachIndexed { index, it ->
|
||||
val decl = when (it) {
|
||||
is Field -> tryRenderVar(it.type, "p$index")
|
||||
is BitField, is IncompleteField -> null
|
||||
} ?: return null
|
||||
|
||||
append("$decl; ")
|
||||
}
|
||||
append("}")
|
||||
|
||||
}
|
||||
|
||||
private fun tryRenderVar(type: Type, name: String): String? = when (type) {
|
||||
CharType, is BoolType -> "char $name"
|
||||
is IntegerType -> "${type.spelling} $name"
|
||||
is FloatingType -> "${type.spelling} $name"
|
||||
is VectorType -> "${type.spelling} $name"
|
||||
is RecordType -> "${tryRenderStructOrUnion(type.decl.def!!)} $name"
|
||||
is EnumType -> tryRenderVar(type.def.baseType, name)
|
||||
is PointerType -> "void* $name"
|
||||
is ConstArrayType -> tryRenderVar(type.elemType, "$name[${type.length}]")
|
||||
is IncompleteArrayType -> tryRenderVar(type.elemType, "$name[]")
|
||||
is Typedef -> tryRenderVar(type.def.aliased, name)
|
||||
is ObjCPointer -> "void* $name"
|
||||
else -> null
|
||||
}
|
||||
|
||||
private val Field.offsetBytes: Long get() {
|
||||
require(this.offset % 8 == 0L)
|
||||
return this.offset / 8
|
||||
}
|
||||
+529
@@ -0,0 +1,529 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
package org.jetbrains.kotlin.native.interop.gen
|
||||
|
||||
import org.jetbrains.kotlin.native.interop.indexer.*
|
||||
|
||||
// TODO: Replace all usages of these strings with constants.
|
||||
const val cinteropPackage = "kotlinx.cinterop"
|
||||
const val cinteropInternalPackage = "$cinteropPackage.internal"
|
||||
|
||||
interface StubIrElement {
|
||||
fun <T, R> accept(visitor: StubIrVisitor<T, R>, data: T): R
|
||||
}
|
||||
|
||||
sealed class StubContainer : StubIrElement {
|
||||
abstract val meta: StubContainerMeta
|
||||
abstract val classes: List<ClassStub>
|
||||
abstract val functions: List<FunctionalStub>
|
||||
abstract val properties: List<PropertyStub>
|
||||
abstract val typealiases: List<TypealiasStub>
|
||||
abstract val simpleContainers: List<SimpleStubContainer>
|
||||
}
|
||||
|
||||
/**
|
||||
* Meta information about [StubContainer].
|
||||
* For example, can be used for comments in textual representation.
|
||||
*/
|
||||
class StubContainerMeta(
|
||||
val textAtStart: String = "",
|
||||
val textAtEnd: String = ""
|
||||
)
|
||||
|
||||
class SimpleStubContainer(
|
||||
override val meta: StubContainerMeta = StubContainerMeta(),
|
||||
override val classes: List<ClassStub> = emptyList(),
|
||||
override val functions: List<FunctionalStub> = emptyList(),
|
||||
override val properties: List<PropertyStub> = emptyList(),
|
||||
override val typealiases: List<TypealiasStub> = emptyList(),
|
||||
override val simpleContainers: List<SimpleStubContainer> = emptyList()
|
||||
) : StubContainer() {
|
||||
|
||||
override fun <T, R> accept(visitor: StubIrVisitor<T, R>, data: T): R {
|
||||
return visitor.visitSimpleStubContainer(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
val StubContainer.children: List<StubIrElement>
|
||||
get() = (classes as List<StubIrElement>) + properties + functions + typealiases
|
||||
|
||||
/**
|
||||
* Marks that abstract value of such type can be passed as value.
|
||||
*/
|
||||
sealed class ValueStub
|
||||
|
||||
class TypeParameterStub(
|
||||
val name: String,
|
||||
val upperBound: StubType? = null
|
||||
) {
|
||||
fun getStubType(nullable: Boolean) =
|
||||
TypeParameterType(name, nullable = nullable, typeParameterDeclaration = this)
|
||||
|
||||
}
|
||||
|
||||
interface TypeArgument {
|
||||
object StarProjection : TypeArgument {
|
||||
override fun toString(): String =
|
||||
"*"
|
||||
}
|
||||
|
||||
enum class Variance {
|
||||
INVARIANT,
|
||||
IN,
|
||||
OUT
|
||||
}
|
||||
}
|
||||
|
||||
class TypeArgumentStub(
|
||||
val type: StubType,
|
||||
val variance: TypeArgument.Variance = TypeArgument.Variance.INVARIANT
|
||||
) : TypeArgument {
|
||||
override fun toString(): String =
|
||||
type.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a source of StubIr element.
|
||||
*/
|
||||
sealed class StubOrigin {
|
||||
/**
|
||||
* Special case when element of IR was generated.
|
||||
*/
|
||||
sealed class Synthetic : StubOrigin() {
|
||||
object CompanionObject : Synthetic()
|
||||
|
||||
/**
|
||||
* Denotes default constructor that was generated and has no real origin.
|
||||
*/
|
||||
object DefaultConstructor : Synthetic()
|
||||
|
||||
/**
|
||||
* CEnum.Companion.byValue.
|
||||
*/
|
||||
class EnumByValue(val enum: EnumDef) : Synthetic()
|
||||
|
||||
/**
|
||||
* CEnum.value.
|
||||
*/
|
||||
class EnumValueField(val enum: EnumDef) : Synthetic()
|
||||
|
||||
/**
|
||||
* E.CEnumVar.value.
|
||||
*/
|
||||
class EnumVarValueField(val enum: EnumDef) : Synthetic()
|
||||
}
|
||||
|
||||
class ObjCCategoryInitMethod(
|
||||
val method: org.jetbrains.kotlin.native.interop.indexer.ObjCMethod
|
||||
) : StubOrigin()
|
||||
|
||||
class ObjCMethod(
|
||||
val method: org.jetbrains.kotlin.native.interop.indexer.ObjCMethod,
|
||||
val container: ObjCContainer
|
||||
) : StubOrigin()
|
||||
|
||||
class ObjCProperty(
|
||||
val property: org.jetbrains.kotlin.native.interop.indexer.ObjCProperty,
|
||||
val container: ObjCContainer
|
||||
) : StubOrigin()
|
||||
|
||||
class ObjCClass(
|
||||
val clazz: org.jetbrains.kotlin.native.interop.indexer.ObjCClass,
|
||||
val isMeta: Boolean
|
||||
) : StubOrigin()
|
||||
|
||||
class ObjCProtocol(
|
||||
val protocol: org.jetbrains.kotlin.native.interop.indexer.ObjCProtocol,
|
||||
val isMeta: Boolean
|
||||
) : StubOrigin()
|
||||
|
||||
class Enum(val enum: EnumDef) : StubOrigin()
|
||||
|
||||
class EnumEntry(val constant: EnumConstant) : StubOrigin()
|
||||
|
||||
class Function(val function: FunctionDecl) : StubOrigin()
|
||||
|
||||
class Struct(val struct: StructDecl) : StubOrigin()
|
||||
|
||||
class StructMember(
|
||||
val member: org.jetbrains.kotlin.native.interop.indexer.StructMember
|
||||
) : StubOrigin()
|
||||
|
||||
class Constant(val constantDef: ConstantDef): StubOrigin()
|
||||
|
||||
class Global(val global: GlobalDecl) : StubOrigin()
|
||||
|
||||
class TypeDef(val typedefDef: TypedefDef) : StubOrigin()
|
||||
|
||||
class VarOf(val typeOrigin: StubOrigin) : StubOrigin()
|
||||
}
|
||||
|
||||
interface StubElementWithOrigin : StubIrElement {
|
||||
val origin: StubOrigin
|
||||
}
|
||||
|
||||
interface AnnotationHolder {
|
||||
val annotations: List<AnnotationStub>
|
||||
}
|
||||
|
||||
sealed class AnnotationStub(val classifier: Classifier) {
|
||||
|
||||
sealed class ObjC(classifier: Classifier) : AnnotationStub(classifier) {
|
||||
object ConsumesReceiver :
|
||||
ObjC(cCallClassifier.nested("ConsumesReceiver"))
|
||||
|
||||
object ReturnsRetained :
|
||||
ObjC(cCallClassifier.nested("ReturnsRetained"))
|
||||
|
||||
class Method(val selector: String, val encoding: String, val isStret: Boolean = false) :
|
||||
ObjC(Classifier.topLevel(cinteropPackage, "ObjCMethod"))
|
||||
|
||||
class Factory(val selector: String, val encoding: String, val isStret: Boolean = false) :
|
||||
ObjC(Classifier.topLevel(cinteropPackage, "ObjCFactory"))
|
||||
|
||||
object Consumed :
|
||||
ObjC(cCallClassifier.nested("Consumed"))
|
||||
|
||||
class Constructor(val selector: String, val designated: Boolean) :
|
||||
ObjC(Classifier.topLevel(cinteropPackage, "ObjCConstructor"))
|
||||
|
||||
class ExternalClass(val protocolGetter: String = "", val binaryName: String = "") :
|
||||
ObjC(Classifier.topLevel(cinteropPackage, "ExternalObjCClass"))
|
||||
}
|
||||
|
||||
sealed class CCall(classifier: Classifier) : AnnotationStub(classifier) {
|
||||
object CString : CCall(cCallClassifier.nested("CString"))
|
||||
object WCString : CCall(cCallClassifier.nested("WCString"))
|
||||
class Symbol(val symbolName: String) : CCall(cCallClassifier)
|
||||
}
|
||||
|
||||
class CStruct(val struct: String) : AnnotationStub(cStructClassifier) {
|
||||
class MemberAt(val offset: Long) : AnnotationStub(cStructClassifier.nested("MemberAt"))
|
||||
|
||||
class ArrayMemberAt(val offset: Long) : AnnotationStub(cStructClassifier.nested("ArrayMemberAt"))
|
||||
|
||||
class BitField(val offset: Long, val size: Int) : AnnotationStub(cStructClassifier.nested("BitField"))
|
||||
|
||||
class VarType(val size: Long, val align: Int) : AnnotationStub(cStructClassifier.nested("VarType"))
|
||||
}
|
||||
|
||||
class CNaturalStruct(val members: List<StructMember>) :
|
||||
AnnotationStub(Classifier.topLevel(cinteropPackage, "CNaturalStruct"))
|
||||
|
||||
class CLength(val length: Long) :
|
||||
AnnotationStub(Classifier.topLevel(cinteropPackage, "CLength"))
|
||||
|
||||
class Deprecated(val message: String, val replaceWith: String, val level: DeprecationLevel) :
|
||||
AnnotationStub(Classifier.topLevel("kotlin", "Deprecated")) {
|
||||
companion object {
|
||||
val unableToImport = Deprecated(
|
||||
"Unable to import this declaration",
|
||||
"",
|
||||
DeprecationLevel.ERROR
|
||||
)
|
||||
|
||||
val deprecatedCVariableCompanion = Deprecated(
|
||||
"Use sizeOf<T>() or alignOf<T>() instead.",
|
||||
"",
|
||||
DeprecationLevel.WARNING
|
||||
)
|
||||
|
||||
val deprecatedCEnumByValue = Deprecated(
|
||||
"Will be removed.",
|
||||
"",
|
||||
DeprecationLevel.WARNING
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class CEnumEntryAlias(val entryName: String) :
|
||||
AnnotationStub(Classifier.topLevel(cinteropInternalPackage, "CEnumEntryAlias"))
|
||||
|
||||
class CEnumVarTypeSize(val size: Int) :
|
||||
AnnotationStub(Classifier.topLevel(cinteropInternalPackage, "CEnumVarTypeSize"))
|
||||
|
||||
private companion object {
|
||||
val cCallClassifier = Classifier.topLevel(cinteropInternalPackage, "CCall")
|
||||
|
||||
val cStructClassifier = Classifier.topLevel(cinteropInternalPackage, "CStruct")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile-time known values.
|
||||
*/
|
||||
sealed class ConstantStub : ValueStub()
|
||||
class StringConstantStub(val value: String) : ConstantStub()
|
||||
data class IntegralConstantStub(val value: Long, val size: Int, val isSigned: Boolean) : ConstantStub()
|
||||
data class DoubleConstantStub(val value: Double, val size: Int) : ConstantStub()
|
||||
|
||||
|
||||
data class PropertyStub(
|
||||
val name: String,
|
||||
val type: StubType,
|
||||
val kind: Kind,
|
||||
val modality: MemberStubModality = MemberStubModality.FINAL,
|
||||
val receiverType: StubType? = null,
|
||||
override val annotations: List<AnnotationStub> = emptyList(),
|
||||
val origin: StubOrigin,
|
||||
val isOverride: Boolean = false
|
||||
) : StubIrElement, AnnotationHolder {
|
||||
sealed class Kind {
|
||||
class Val(
|
||||
val getter: PropertyAccessor.Getter
|
||||
) : Kind()
|
||||
|
||||
class Var(
|
||||
val getter: PropertyAccessor.Getter,
|
||||
val setter: PropertyAccessor.Setter
|
||||
) : Kind()
|
||||
|
||||
class Constant(val constant: ConstantStub) : Kind()
|
||||
}
|
||||
|
||||
override fun <T, R> accept(visitor: StubIrVisitor<T, R>, data: T): R {
|
||||
return visitor.visitProperty(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
enum class ClassStubModality {
|
||||
INTERFACE, OPEN, ABSTRACT, NONE
|
||||
}
|
||||
|
||||
enum class VisibilityModifier {
|
||||
PRIVATE, PROTECTED, INTERNAL, PUBLIC
|
||||
}
|
||||
|
||||
class GetConstructorParameter(
|
||||
val constructorParameterStub: FunctionParameterStub
|
||||
) : ValueStub()
|
||||
|
||||
class SuperClassInit(
|
||||
val type: StubType,
|
||||
val arguments: List<ValueStub> = listOf()
|
||||
)
|
||||
|
||||
// TODO: Consider unifying these classes.
|
||||
sealed class ClassStub : StubContainer(), StubElementWithOrigin, AnnotationHolder {
|
||||
|
||||
abstract val superClassInit: SuperClassInit?
|
||||
abstract val interfaces: List<StubType>
|
||||
abstract val childrenClasses: List<ClassStub>
|
||||
abstract val companion : Companion?
|
||||
abstract val classifier: Classifier
|
||||
|
||||
class Simple(
|
||||
override val classifier: Classifier,
|
||||
val modality: ClassStubModality,
|
||||
constructors: List<ConstructorStub> = emptyList(),
|
||||
methods: List<FunctionStub> = emptyList(),
|
||||
override val superClassInit: SuperClassInit? = null,
|
||||
override val interfaces: List<StubType> = emptyList(),
|
||||
override val properties: List<PropertyStub> = emptyList(),
|
||||
override val origin: StubOrigin,
|
||||
override val annotations: List<AnnotationStub> = emptyList(),
|
||||
override val childrenClasses: List<ClassStub> = emptyList(),
|
||||
override val companion: Companion? = null,
|
||||
override val simpleContainers: List<SimpleStubContainer> = emptyList()
|
||||
) : ClassStub() {
|
||||
override val functions: List<FunctionalStub> = constructors + methods
|
||||
}
|
||||
|
||||
class Companion(
|
||||
override val classifier: Classifier,
|
||||
methods: List<FunctionStub> = emptyList(),
|
||||
override val superClassInit: SuperClassInit? = null,
|
||||
override val interfaces: List<StubType> = emptyList(),
|
||||
override val properties: List<PropertyStub> = emptyList(),
|
||||
override val origin: StubOrigin = StubOrigin.Synthetic.CompanionObject,
|
||||
override val annotations: List<AnnotationStub> = emptyList(),
|
||||
override val childrenClasses: List<ClassStub> = emptyList(),
|
||||
override val simpleContainers: List<SimpleStubContainer> = emptyList()
|
||||
) : ClassStub() {
|
||||
override val companion: Companion? = null
|
||||
|
||||
override val functions: List<FunctionalStub> = methods
|
||||
}
|
||||
|
||||
class Enum(
|
||||
override val classifier: Classifier,
|
||||
val entries: List<EnumEntryStub>,
|
||||
constructors: List<ConstructorStub>,
|
||||
override val superClassInit: SuperClassInit? = null,
|
||||
override val interfaces: List<StubType> = emptyList(),
|
||||
override val properties: List<PropertyStub> = emptyList(),
|
||||
override val origin: StubOrigin,
|
||||
override val annotations: List<AnnotationStub> = emptyList(),
|
||||
override val childrenClasses: List<ClassStub> = emptyList(),
|
||||
override val companion: Companion?= null,
|
||||
override val simpleContainers: List<SimpleStubContainer> = emptyList()
|
||||
) : ClassStub() {
|
||||
override val functions: List<FunctionalStub> = constructors
|
||||
}
|
||||
|
||||
override val meta: StubContainerMeta = StubContainerMeta()
|
||||
|
||||
override val classes: List<ClassStub>
|
||||
get() = childrenClasses + listOfNotNull(companion)
|
||||
|
||||
override fun <T, R> accept(visitor: StubIrVisitor<T, R>, data: T) =
|
||||
visitor.visitClass(this, data)
|
||||
|
||||
override val typealiases: List<TypealiasStub> = emptyList()
|
||||
}
|
||||
|
||||
class ReceiverParameterStub(
|
||||
val type: StubType
|
||||
)
|
||||
|
||||
class FunctionParameterStub(
|
||||
val name: String,
|
||||
val type: StubType,
|
||||
override val annotations: List<AnnotationStub> = emptyList(),
|
||||
val isVararg: Boolean = false
|
||||
) : AnnotationHolder
|
||||
|
||||
enum class MemberStubModality {
|
||||
OPEN,
|
||||
FINAL,
|
||||
ABSTRACT
|
||||
}
|
||||
|
||||
interface FunctionalStub : AnnotationHolder, StubIrElement, NativeBacked {
|
||||
val parameters: List<FunctionParameterStub>
|
||||
}
|
||||
|
||||
sealed class PropertyAccessor : FunctionalStub {
|
||||
|
||||
sealed class Getter : PropertyAccessor() {
|
||||
|
||||
override val parameters: List<FunctionParameterStub> = emptyList()
|
||||
|
||||
class SimpleGetter(
|
||||
override val annotations: List<AnnotationStub> = emptyList(),
|
||||
val constant: ConstantStub? = null
|
||||
) : Getter()
|
||||
|
||||
class GetConstructorParameter(
|
||||
val constructorParameter: FunctionParameterStub,
|
||||
override val annotations: List<AnnotationStub> = emptyList()
|
||||
) : Getter()
|
||||
|
||||
class ExternalGetter(
|
||||
override val annotations: List<AnnotationStub> = emptyList()
|
||||
) : Getter()
|
||||
|
||||
class ArrayMemberAt(
|
||||
val offset: Long
|
||||
) : Getter() {
|
||||
override val parameters: List<FunctionParameterStub> = emptyList()
|
||||
override val annotations: List<AnnotationStub> = emptyList()
|
||||
}
|
||||
|
||||
class MemberAt(
|
||||
val offset: Long,
|
||||
val typeArguments: List<TypeArgumentStub> = emptyList(),
|
||||
val hasValueAccessor: Boolean
|
||||
) : Getter() {
|
||||
override val annotations: List<AnnotationStub> = emptyList()
|
||||
}
|
||||
|
||||
class ReadBits(
|
||||
val offset: Long,
|
||||
val size: Int,
|
||||
val signed: Boolean
|
||||
) : Getter() {
|
||||
override val annotations: List<AnnotationStub> = emptyList()
|
||||
}
|
||||
|
||||
class InterpretPointed(val cGlobalName:String, pointedType: StubType) : Getter() {
|
||||
override val annotations: List<AnnotationStub> = emptyList()
|
||||
val typeParameters: List<StubType> = listOf(pointedType)
|
||||
}
|
||||
|
||||
class GetEnumEntry(
|
||||
val enumEntryStub: EnumEntryStub,
|
||||
override val annotations: List<AnnotationStub> = emptyList()
|
||||
) : Getter()
|
||||
}
|
||||
|
||||
sealed class Setter : PropertyAccessor() {
|
||||
|
||||
override val parameters: List<FunctionParameterStub> = emptyList()
|
||||
|
||||
class SimpleSetter(
|
||||
override val annotations: List<AnnotationStub> = emptyList()
|
||||
) : Setter()
|
||||
|
||||
class ExternalSetter(
|
||||
override val annotations: List<AnnotationStub> = emptyList()
|
||||
) : Setter()
|
||||
|
||||
class MemberAt(
|
||||
val offset: Long,
|
||||
override val annotations: List<AnnotationStub> = emptyList(),
|
||||
val typeArguments: List<TypeArgumentStub> = emptyList()
|
||||
) : Setter()
|
||||
|
||||
class WriteBits(
|
||||
val offset: Long,
|
||||
val size: Int,
|
||||
override val annotations: List<AnnotationStub> = emptyList()
|
||||
) : Setter()
|
||||
}
|
||||
|
||||
override fun <T, R> accept(visitor: StubIrVisitor<T, R>, data: T) =
|
||||
visitor.visitPropertyAccessor(this, data)
|
||||
|
||||
}
|
||||
|
||||
data class FunctionStub(
|
||||
val name: String,
|
||||
val returnType: StubType,
|
||||
override val parameters: List<FunctionParameterStub>,
|
||||
override val origin: StubOrigin,
|
||||
override val annotations: List<AnnotationStub>,
|
||||
val external: Boolean = false,
|
||||
val receiver: ReceiverParameterStub?,
|
||||
val modality: MemberStubModality,
|
||||
val typeParameters: List<TypeParameterStub> = emptyList(),
|
||||
val isOverride: Boolean = false,
|
||||
val hasStableParameterNames: Boolean = true
|
||||
) : StubElementWithOrigin, FunctionalStub {
|
||||
|
||||
override fun <T, R> accept(visitor: StubIrVisitor<T, R>, data: T) =
|
||||
visitor.visitFunction(this, data)
|
||||
}
|
||||
|
||||
// TODO: should we support non-trivial constructors?
|
||||
class ConstructorStub(
|
||||
override val parameters: List<FunctionParameterStub> = emptyList(),
|
||||
override val annotations: List<AnnotationStub> = emptyList(),
|
||||
val isPrimary: Boolean,
|
||||
val visibility: VisibilityModifier = VisibilityModifier.PUBLIC,
|
||||
val origin: StubOrigin
|
||||
) : FunctionalStub {
|
||||
|
||||
override fun <T, R> accept(visitor: StubIrVisitor<T, R>, data: T) =
|
||||
visitor.visitConstructor(this, data)
|
||||
}
|
||||
|
||||
class EnumEntryStub(
|
||||
val name: String,
|
||||
val constant: IntegralConstantStub,
|
||||
val origin: StubOrigin.EnumEntry,
|
||||
val ordinal: Int
|
||||
)
|
||||
|
||||
class TypealiasStub(
|
||||
val alias: Classifier,
|
||||
val aliasee: StubType,
|
||||
val origin: StubOrigin
|
||||
) : StubIrElement {
|
||||
|
||||
override fun <T, R> accept(visitor: StubIrVisitor<T, R>, data: T) =
|
||||
visitor.visitTypealias(this, data)
|
||||
}
|
||||
+319
@@ -0,0 +1,319 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
package org.jetbrains.kotlin.native.interop.gen
|
||||
|
||||
import org.jetbrains.kotlin.native.interop.gen.jvm.KotlinPlatform
|
||||
import org.jetbrains.kotlin.native.interop.indexer.*
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
|
||||
class BridgeBuilderResult(
|
||||
val kotlinFile: KotlinFile,
|
||||
val nativeBridges: NativeBridges,
|
||||
val propertyAccessorBridgeBodies: Map<PropertyAccessor, String>,
|
||||
val functionBridgeBodies: Map<FunctionStub, List<String>>,
|
||||
val excludedStubs: Set<StubIrElement>
|
||||
)
|
||||
|
||||
/**
|
||||
* Generates [NativeBridges] and corresponding function bodies and property accessors.
|
||||
*/
|
||||
class StubIrBridgeBuilder(
|
||||
private val context: StubIrContext,
|
||||
private val builderResult: StubIrBuilderResult) {
|
||||
|
||||
private val globalAddressExpressions = mutableMapOf<Pair<String, PropertyAccessor>, KotlinExpression>()
|
||||
|
||||
private val wrapperGenerator = CWrappersGenerator(context)
|
||||
|
||||
private fun getGlobalAddressExpression(cGlobalName: String, accessor: PropertyAccessor) =
|
||||
globalAddressExpressions.getOrPut(Pair(cGlobalName, accessor)) {
|
||||
simpleBridgeGenerator.kotlinToNative(
|
||||
nativeBacked = accessor,
|
||||
returnType = BridgedType.NATIVE_PTR,
|
||||
kotlinValues = emptyList(),
|
||||
independent = false
|
||||
) {
|
||||
"&$cGlobalName"
|
||||
}
|
||||
}
|
||||
|
||||
private val declarationMapper = builderResult.declarationMapper
|
||||
|
||||
private val kotlinFile = object : KotlinFile(
|
||||
context.configuration.pkgName,
|
||||
namesToBeDeclared = builderResult.stubs.computeNamesToBeDeclared(context.configuration.pkgName)
|
||||
) {
|
||||
override val mappingBridgeGenerator: MappingBridgeGenerator
|
||||
get() = this@StubIrBridgeBuilder.mappingBridgeGenerator
|
||||
}
|
||||
|
||||
private val simpleBridgeGenerator: SimpleBridgeGenerator =
|
||||
SimpleBridgeGeneratorImpl(
|
||||
context.platform,
|
||||
context.configuration.pkgName,
|
||||
context.jvmFileClassName,
|
||||
context.libraryForCStubs,
|
||||
topLevelNativeScope = object : NativeScope {
|
||||
override val mappingBridgeGenerator: MappingBridgeGenerator
|
||||
get() = this@StubIrBridgeBuilder.mappingBridgeGenerator
|
||||
},
|
||||
topLevelKotlinScope = kotlinFile
|
||||
)
|
||||
|
||||
private val mappingBridgeGenerator: MappingBridgeGenerator =
|
||||
MappingBridgeGeneratorImpl(declarationMapper, simpleBridgeGenerator)
|
||||
|
||||
private val propertyAccessorBridgeBodies = mutableMapOf<PropertyAccessor, String>()
|
||||
private val functionBridgeBodies = mutableMapOf<FunctionStub, List<String>>()
|
||||
private val excludedStubs = mutableSetOf<StubIrElement>()
|
||||
|
||||
private val bridgeGeneratingVisitor = object : StubIrVisitor<StubContainer?, Unit> {
|
||||
|
||||
override fun visitClass(element: ClassStub, data: StubContainer?) {
|
||||
element.annotations.filterIsInstance<AnnotationStub.ObjC.ExternalClass>().firstOrNull()?.let {
|
||||
val origin = element.origin
|
||||
if (it.protocolGetter.isNotEmpty() && origin is StubOrigin.ObjCProtocol && !origin.isMeta) {
|
||||
val protocol = (element.origin as StubOrigin.ObjCProtocol).protocol
|
||||
// TODO: handle the case when protocol getter stub can't be compiled.
|
||||
generateProtocolGetter(it.protocolGetter, protocol)
|
||||
}
|
||||
}
|
||||
element.children.forEach {
|
||||
it.accept(this, element)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitTypealias(element: TypealiasStub, data: StubContainer?) {
|
||||
}
|
||||
|
||||
override fun visitFunction(element: FunctionStub, data: StubContainer?) {
|
||||
try {
|
||||
when {
|
||||
element.external -> tryProcessCCallAnnotation(element)
|
||||
element.isOptionalObjCMethod() -> { }
|
||||
element.origin is StubOrigin.Synthetic.EnumByValue -> { }
|
||||
data != null && data.isInterface -> { }
|
||||
else -> generateBridgeBody(element)
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
context.log("Warning: cannot generate bridge for ${element.name}.")
|
||||
excludedStubs += element
|
||||
}
|
||||
}
|
||||
|
||||
private fun tryProcessCCallAnnotation(function: FunctionStub) {
|
||||
val origin = function.origin as? StubOrigin.Function
|
||||
?: return
|
||||
val cCallAnnotation = function.annotations.firstIsInstanceOrNull<AnnotationStub.CCall.Symbol>()
|
||||
?: return
|
||||
val wrapper = wrapperGenerator.generateCCalleeWrapper(origin.function, cCallAnnotation.symbolName)
|
||||
simpleBridgeGenerator.insertNativeBridge(function, emptyList(), wrapper.lines)
|
||||
}
|
||||
|
||||
override fun visitProperty(element: PropertyStub, data: StubContainer?) {
|
||||
try {
|
||||
when (val kind = element.kind) {
|
||||
is PropertyStub.Kind.Constant -> {
|
||||
}
|
||||
is PropertyStub.Kind.Val -> {
|
||||
visitPropertyAccessor(kind.getter, data)
|
||||
}
|
||||
is PropertyStub.Kind.Var -> {
|
||||
visitPropertyAccessor(kind.getter, data)
|
||||
visitPropertyAccessor(kind.setter, data)
|
||||
}
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
context.log("Warning: cannot generate bridge for ${element.name}.")
|
||||
excludedStubs += element
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitConstructor(constructorStub: ConstructorStub, data: StubContainer?) {
|
||||
}
|
||||
|
||||
override fun visitPropertyAccessor(propertyAccessor: PropertyAccessor, data: StubContainer?) {
|
||||
when (propertyAccessor) {
|
||||
is PropertyAccessor.Getter.SimpleGetter -> {
|
||||
when (propertyAccessor) {
|
||||
in builderResult.bridgeGenerationComponents.getterToBridgeInfo -> {
|
||||
val extra = builderResult.bridgeGenerationComponents.getterToBridgeInfo.getValue(propertyAccessor)
|
||||
val typeInfo = extra.typeInfo
|
||||
propertyAccessorBridgeBodies[propertyAccessor] = typeInfo.argFromBridged(simpleBridgeGenerator.kotlinToNative(
|
||||
nativeBacked = propertyAccessor,
|
||||
returnType = typeInfo.bridgedType,
|
||||
kotlinValues = emptyList(),
|
||||
independent = false
|
||||
) {
|
||||
typeInfo.cToBridged(expr = extra.cGlobalName)
|
||||
}, kotlinFile, nativeBacked = propertyAccessor)
|
||||
}
|
||||
in builderResult.bridgeGenerationComponents.arrayGetterInfo -> {
|
||||
val extra = builderResult.bridgeGenerationComponents.arrayGetterInfo.getValue(propertyAccessor)
|
||||
val typeInfo = extra.typeInfo
|
||||
val getAddressExpression = getGlobalAddressExpression(extra.cGlobalName, propertyAccessor)
|
||||
propertyAccessorBridgeBodies[propertyAccessor] = typeInfo.argFromBridged(getAddressExpression, kotlinFile, nativeBacked = propertyAccessor) + "!!"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is PropertyAccessor.Getter.ReadBits -> {
|
||||
val extra = builderResult.bridgeGenerationComponents.getterToBridgeInfo.getValue(propertyAccessor)
|
||||
val rawType = extra.typeInfo.bridgedType
|
||||
val readBits = "readBits(this.rawPtr, ${propertyAccessor.offset}, ${propertyAccessor.size}, ${propertyAccessor.signed}).${rawType.convertor!!}()"
|
||||
val getExpr = extra.typeInfo.argFromBridged(readBits, kotlinFile, object : NativeBacked {})
|
||||
propertyAccessorBridgeBodies[propertyAccessor] = getExpr
|
||||
}
|
||||
|
||||
is PropertyAccessor.Setter.SimpleSetter -> when (propertyAccessor) {
|
||||
in builderResult.bridgeGenerationComponents.setterToBridgeInfo -> {
|
||||
val extra = builderResult.bridgeGenerationComponents.setterToBridgeInfo.getValue(propertyAccessor)
|
||||
val typeInfo = extra.typeInfo
|
||||
val bridgedValue = BridgeTypedKotlinValue(typeInfo.bridgedType, typeInfo.argToBridged("value"))
|
||||
val setter = simpleBridgeGenerator.kotlinToNative(
|
||||
nativeBacked = propertyAccessor,
|
||||
returnType = BridgedType.VOID,
|
||||
kotlinValues = listOf(bridgedValue),
|
||||
independent = false
|
||||
) { nativeValues ->
|
||||
out("${extra.cGlobalName} = ${typeInfo.cFromBridged(
|
||||
nativeValues.single(),
|
||||
scope,
|
||||
nativeBacked = propertyAccessor
|
||||
)};")
|
||||
""
|
||||
}
|
||||
propertyAccessorBridgeBodies[propertyAccessor] = setter
|
||||
}
|
||||
}
|
||||
|
||||
is PropertyAccessor.Setter.WriteBits -> {
|
||||
val extra = builderResult.bridgeGenerationComponents.setterToBridgeInfo.getValue(propertyAccessor)
|
||||
val rawValue = extra.typeInfo.argToBridged("value")
|
||||
propertyAccessorBridgeBodies[propertyAccessor] = "writeBits(this.rawPtr, ${propertyAccessor.offset}, ${propertyAccessor.size}, $rawValue.toLong())"
|
||||
}
|
||||
|
||||
is PropertyAccessor.Getter.InterpretPointed -> {
|
||||
val getAddressExpression = getGlobalAddressExpression(propertyAccessor.cGlobalName, propertyAccessor)
|
||||
propertyAccessorBridgeBodies[propertyAccessor] = getAddressExpression
|
||||
}
|
||||
|
||||
is PropertyAccessor.Getter.ExternalGetter -> {
|
||||
if (propertyAccessor in builderResult.wrapperGenerationComponents.getterToWrapperInfo) {
|
||||
val extra = builderResult.wrapperGenerationComponents.getterToWrapperInfo.getValue(propertyAccessor)
|
||||
val cCallAnnotation = propertyAccessor.annotations.firstIsInstanceOrNull<AnnotationStub.CCall.Symbol>()
|
||||
?: error("external getter for ${extra.global.name} wasn't marked with @CCall")
|
||||
val wrapper = if (extra.passViaPointer) {
|
||||
wrapperGenerator.generateCGlobalByPointerGetter(extra.global, cCallAnnotation.symbolName)
|
||||
} else {
|
||||
wrapperGenerator.generateCGlobalGetter(extra.global, cCallAnnotation.symbolName)
|
||||
}
|
||||
simpleBridgeGenerator.insertNativeBridge(propertyAccessor, emptyList(), wrapper.lines)
|
||||
}
|
||||
}
|
||||
|
||||
is PropertyAccessor.Setter.ExternalSetter -> {
|
||||
if (propertyAccessor in builderResult.wrapperGenerationComponents.setterToWrapperInfo) {
|
||||
val extra = builderResult.wrapperGenerationComponents.setterToWrapperInfo.getValue(propertyAccessor)
|
||||
val cCallAnnotation = propertyAccessor.annotations.firstIsInstanceOrNull<AnnotationStub.CCall.Symbol>()
|
||||
?: error("external setter for ${extra.global.name} wasn't marked with @CCall")
|
||||
val wrapper = wrapperGenerator.generateCGlobalSetter(extra.global, cCallAnnotation.symbolName)
|
||||
simpleBridgeGenerator.insertNativeBridge(propertyAccessor, emptyList(), wrapper.lines)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitSimpleStubContainer(simpleStubContainer: SimpleStubContainer, data: StubContainer?) {
|
||||
simpleStubContainer.classes.forEach {
|
||||
it.accept(this, simpleStubContainer)
|
||||
}
|
||||
simpleStubContainer.functions.forEach {
|
||||
it.accept(this, simpleStubContainer)
|
||||
}
|
||||
simpleStubContainer.properties.forEach {
|
||||
it.accept(this, simpleStubContainer)
|
||||
}
|
||||
simpleStubContainer.typealiases.forEach {
|
||||
it.accept(this, simpleStubContainer)
|
||||
}
|
||||
simpleStubContainer.simpleContainers.forEach {
|
||||
it.accept(this, simpleStubContainer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun isCValuesRef(type: StubType): Boolean =
|
||||
(type as? ClassifierStubType)?.let { it.classifier == KotlinTypes.cValuesRef }
|
||||
?: false
|
||||
|
||||
private fun generateBridgeBody(function: FunctionStub) {
|
||||
assert(context.platform == KotlinPlatform.JVM) { "Function ${function.name} was not marked as external." }
|
||||
assert(function.origin is StubOrigin.Function) { "Can't create bridge for ${function.name}" }
|
||||
val origin = function.origin as StubOrigin.Function
|
||||
val bodyGenerator = KotlinCodeBuilder(scope = kotlinFile)
|
||||
val bridgeArguments = mutableListOf<TypedKotlinValue>()
|
||||
var isVararg = false
|
||||
function.parameters.forEachIndexed { index, parameter ->
|
||||
isVararg = isVararg or parameter.isVararg
|
||||
val parameterName = parameter.name.asSimpleName()
|
||||
val bridgeArgument = when {
|
||||
parameter in builderResult.bridgeGenerationComponents.cStringParameters -> {
|
||||
bodyGenerator.pushMemScoped()
|
||||
"$parameterName?.cstr?.getPointer(memScope)"
|
||||
}
|
||||
parameter in builderResult.bridgeGenerationComponents.wCStringParameters -> {
|
||||
bodyGenerator.pushMemScoped()
|
||||
"$parameterName?.wcstr?.getPointer(memScope)"
|
||||
}
|
||||
isCValuesRef(parameter.type) -> {
|
||||
bodyGenerator.pushMemScoped()
|
||||
bodyGenerator.getNativePointer(parameterName)
|
||||
}
|
||||
else -> {
|
||||
parameterName
|
||||
}
|
||||
}
|
||||
bridgeArguments += TypedKotlinValue(origin.function.parameters[index].type, bridgeArgument)
|
||||
}
|
||||
// TODO: Improve assertion message.
|
||||
assert(!isVararg || context.platform != KotlinPlatform.NATIVE) {
|
||||
"Function ${function.name} was processed incorrectly."
|
||||
}
|
||||
val result = mappingBridgeGenerator.kotlinToNative(
|
||||
bodyGenerator,
|
||||
function,
|
||||
origin.function.returnType,
|
||||
bridgeArguments,
|
||||
independent = false
|
||||
) { nativeValues ->
|
||||
"${origin.function.name}(${nativeValues.joinToString()})"
|
||||
}
|
||||
bodyGenerator.returnResult(result)
|
||||
functionBridgeBodies[function] = bodyGenerator.build()
|
||||
}
|
||||
|
||||
private fun generateProtocolGetter(protocolGetterName: String, protocol: ObjCProtocol) {
|
||||
val builder = NativeCodeBuilder(simpleBridgeGenerator.topLevelNativeScope)
|
||||
val nativeBacked = object : NativeBacked {}
|
||||
with(builder) {
|
||||
out("Protocol* $protocolGetterName() {")
|
||||
out(" return @protocol(${protocol.name});")
|
||||
out("}")
|
||||
}
|
||||
simpleBridgeGenerator.insertNativeBridge(nativeBacked, emptyList(), builder.lines)
|
||||
}
|
||||
|
||||
fun build(): BridgeBuilderResult {
|
||||
bridgeGeneratingVisitor.visitSimpleStubContainer(builderResult.stubs, null)
|
||||
return BridgeBuilderResult(
|
||||
kotlinFile,
|
||||
simpleBridgeGenerator.prepare(),
|
||||
propertyAccessorBridgeBodies.toMap(),
|
||||
functionBridgeBodies.toMap(),
|
||||
excludedStubs.toSet()
|
||||
)
|
||||
}
|
||||
}
|
||||
+407
@@ -0,0 +1,407 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
package org.jetbrains.kotlin.native.interop.gen
|
||||
|
||||
import org.jetbrains.kotlin.native.interop.gen.jvm.GenerationMode
|
||||
import org.jetbrains.kotlin.native.interop.gen.jvm.InteropConfiguration
|
||||
import org.jetbrains.kotlin.native.interop.gen.jvm.KotlinPlatform
|
||||
import org.jetbrains.kotlin.native.interop.indexer.*
|
||||
|
||||
/**
|
||||
* Components that are not passed via StubIr but required for bridge generation.
|
||||
*/
|
||||
class BridgeGenerationInfo(val cGlobalName: String, val typeInfo: TypeInfo)
|
||||
|
||||
/**
|
||||
* Additional components that are required to generate bridges.
|
||||
* TODO: Metadata-based interop should not depend on these components.
|
||||
*/
|
||||
interface BridgeGenerationComponents {
|
||||
|
||||
val setterToBridgeInfo: Map<PropertyAccessor.Setter, BridgeGenerationInfo>
|
||||
|
||||
val getterToBridgeInfo: Map<PropertyAccessor.Getter, BridgeGenerationInfo>
|
||||
|
||||
val arrayGetterInfo: Map<PropertyAccessor.Getter, BridgeGenerationInfo>
|
||||
|
||||
val enumToTypeMirror: Map<ClassStub.Enum, TypeMirror>
|
||||
|
||||
val wCStringParameters: Set<FunctionParameterStub>
|
||||
|
||||
val cStringParameters: Set<FunctionParameterStub>
|
||||
}
|
||||
|
||||
class BridgeGenerationComponentsBuilder {
|
||||
|
||||
val getterToBridgeInfo = mutableMapOf<PropertyAccessor.Getter, BridgeGenerationInfo>()
|
||||
val setterToBridgeInfo = mutableMapOf<PropertyAccessor.Setter, BridgeGenerationInfo>()
|
||||
val arrayGetterBridgeInfo = mutableMapOf<PropertyAccessor.Getter, BridgeGenerationInfo>()
|
||||
val enumToTypeMirror = mutableMapOf<ClassStub.Enum, TypeMirror>()
|
||||
val wCStringParameters = mutableSetOf<FunctionParameterStub>()
|
||||
val cStringParameters = mutableSetOf<FunctionParameterStub>()
|
||||
|
||||
fun build(): BridgeGenerationComponents = object : BridgeGenerationComponents {
|
||||
override val getterToBridgeInfo =
|
||||
this@BridgeGenerationComponentsBuilder.getterToBridgeInfo.toMap()
|
||||
|
||||
override val setterToBridgeInfo =
|
||||
this@BridgeGenerationComponentsBuilder.setterToBridgeInfo.toMap()
|
||||
|
||||
override val enumToTypeMirror =
|
||||
this@BridgeGenerationComponentsBuilder.enumToTypeMirror.toMap()
|
||||
|
||||
override val wCStringParameters: Set<FunctionParameterStub> =
|
||||
this@BridgeGenerationComponentsBuilder.wCStringParameters.toSet()
|
||||
|
||||
override val cStringParameters: Set<FunctionParameterStub> =
|
||||
this@BridgeGenerationComponentsBuilder.cStringParameters.toSet()
|
||||
|
||||
override val arrayGetterInfo: Map<PropertyAccessor.Getter, BridgeGenerationInfo> =
|
||||
this@BridgeGenerationComponentsBuilder.arrayGetterBridgeInfo.toMap()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Components that are not passed via StubIr but required for generation of wrappers.
|
||||
*/
|
||||
class WrapperGenerationInfo(val global: GlobalDecl, val passViaPointer: Boolean = false)
|
||||
|
||||
interface WrapperGenerationComponents {
|
||||
val getterToWrapperInfo: Map<PropertyAccessor.Getter.ExternalGetter, WrapperGenerationInfo>
|
||||
val setterToWrapperInfo: Map<PropertyAccessor.Setter.ExternalSetter, WrapperGenerationInfo>
|
||||
}
|
||||
|
||||
class WrapperGenerationComponentsBuilder {
|
||||
|
||||
val getterToWrapperInfo = mutableMapOf<PropertyAccessor.Getter.ExternalGetter, WrapperGenerationInfo>()
|
||||
val setterToWrapperInfo = mutableMapOf<PropertyAccessor.Setter.ExternalSetter, WrapperGenerationInfo>()
|
||||
|
||||
fun build(): WrapperGenerationComponents = object : WrapperGenerationComponents {
|
||||
override val getterToWrapperInfo = this@WrapperGenerationComponentsBuilder.getterToWrapperInfo.toMap()
|
||||
|
||||
override val setterToWrapperInfo = this@WrapperGenerationComponentsBuilder.setterToWrapperInfo.toMap()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Common part of all [StubIrBuilder] implementations.
|
||||
*/
|
||||
interface StubsBuildingContext {
|
||||
val configuration: InteropConfiguration
|
||||
|
||||
fun mirror(type: Type): TypeMirror
|
||||
|
||||
val declarationMapper: DeclarationMapper
|
||||
|
||||
fun generateNextUniqueId(prefix: String): String
|
||||
|
||||
val generatedObjCCategoriesMembers: MutableMap<ObjCClass, GeneratedObjCCategoriesMembers>
|
||||
|
||||
val platform: KotlinPlatform
|
||||
|
||||
/**
|
||||
* In some cases StubIr should be different for metadata and sourcecode modes.
|
||||
* For example, it is impossible to represent call to superclass constructor in
|
||||
* metadata directly and arguments should be passed via annotations instead.
|
||||
*/
|
||||
val generationMode: GenerationMode
|
||||
|
||||
fun isStrictEnum(enumDef: EnumDef): Boolean
|
||||
|
||||
val macroConstantsByName: Map<String, MacroDef>
|
||||
|
||||
fun tryCreateIntegralStub(type: Type, value: Long): IntegralConstantStub?
|
||||
|
||||
fun tryCreateDoubleStub(type: Type, value: Double): DoubleConstantStub?
|
||||
|
||||
val bridgeComponentsBuilder: BridgeGenerationComponentsBuilder
|
||||
|
||||
val wrapperComponentsBuilder: WrapperGenerationComponentsBuilder
|
||||
|
||||
fun getKotlinClassFor(objCClassOrProtocol: ObjCClassOrProtocol, isMeta: Boolean = false): Classifier
|
||||
|
||||
fun getKotlinClassForPointed(structDecl: StructDecl): Classifier
|
||||
|
||||
fun isOverloading(func: FunctionDecl): Boolean
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
internal interface StubElementBuilder {
|
||||
val context: StubsBuildingContext
|
||||
|
||||
fun build(): List<StubIrElement>
|
||||
}
|
||||
|
||||
class StubsBuildingContextImpl(
|
||||
private val stubIrContext: StubIrContext
|
||||
) : StubsBuildingContext {
|
||||
|
||||
override val configuration: InteropConfiguration = stubIrContext.configuration
|
||||
override val platform: KotlinPlatform = stubIrContext.platform
|
||||
override val generationMode: GenerationMode = stubIrContext.generationMode
|
||||
val imports: Imports = stubIrContext.imports
|
||||
private val nativeIndex: NativeIndex = stubIrContext.nativeIndex
|
||||
|
||||
private var theCounter = 0
|
||||
|
||||
private val uniqFunctions = mutableSetOf<String>()
|
||||
override fun isOverloading(func: FunctionDecl) = !uniqFunctions.add(func.name) // TODO: params & return type.
|
||||
|
||||
override fun generateNextUniqueId(prefix: String) =
|
||||
prefix + pkgName.replace('.', '_') + theCounter++
|
||||
|
||||
override fun mirror(type: Type): TypeMirror = mirror(declarationMapper, type)
|
||||
|
||||
/**
|
||||
* Indicates whether this enum should be represented as Kotlin enum.
|
||||
*/
|
||||
|
||||
override fun isStrictEnum(enumDef: EnumDef): Boolean = with(enumDef) {
|
||||
if (this.isAnonymous) {
|
||||
return false
|
||||
}
|
||||
|
||||
val name = this.kotlinName
|
||||
|
||||
if (name in configuration.strictEnums) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (name in configuration.nonStrictEnums) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Let the simple heuristic decide:
|
||||
return !this.constants.any { it.isExplicitlyDefined }
|
||||
}
|
||||
|
||||
override val generatedObjCCategoriesMembers = mutableMapOf<ObjCClass, GeneratedObjCCategoriesMembers>()
|
||||
|
||||
override val declarationMapper = object : DeclarationMapper {
|
||||
override fun getKotlinClassForPointed(structDecl: StructDecl): Classifier {
|
||||
val baseName = structDecl.kotlinName
|
||||
val pkg = when (platform) {
|
||||
KotlinPlatform.JVM -> pkgName
|
||||
KotlinPlatform.NATIVE -> if (structDecl.def == null) {
|
||||
cnamesStructsPackageName // to be imported as forward declaration.
|
||||
} else {
|
||||
getPackageFor(structDecl)
|
||||
}
|
||||
}
|
||||
return Classifier.topLevel(pkg, baseName)
|
||||
}
|
||||
|
||||
override fun isMappedToStrict(enumDef: EnumDef): Boolean = isStrictEnum(enumDef)
|
||||
|
||||
override fun getKotlinNameForValue(enumDef: EnumDef): String = enumDef.kotlinName
|
||||
|
||||
override fun getPackageFor(declaration: TypeDeclaration): String {
|
||||
return imports.getPackage(declaration.location) ?: pkgName
|
||||
}
|
||||
|
||||
override val useUnsignedTypes: Boolean
|
||||
get() = when (platform) {
|
||||
KotlinPlatform.JVM -> false
|
||||
KotlinPlatform.NATIVE -> true
|
||||
}
|
||||
}
|
||||
|
||||
override val macroConstantsByName: Map<String, MacroDef> =
|
||||
(nativeIndex.macroConstants + nativeIndex.wrappedMacros).associateBy { it.name }
|
||||
|
||||
/**
|
||||
* The name to be used for this enum in Kotlin
|
||||
*/
|
||||
val EnumDef.kotlinName: String
|
||||
get() = if (spelling.startsWith("enum ")) {
|
||||
spelling.substringAfter(' ')
|
||||
} else {
|
||||
assert (!isAnonymous)
|
||||
spelling
|
||||
}
|
||||
|
||||
|
||||
private val pkgName: String
|
||||
get() = configuration.pkgName
|
||||
|
||||
/**
|
||||
* The name to be used for this struct in Kotlin
|
||||
*/
|
||||
val StructDecl.kotlinName: String
|
||||
get() = stubIrContext.getKotlinName(this)
|
||||
|
||||
override fun tryCreateIntegralStub(type: Type, value: Long): IntegralConstantStub? {
|
||||
val integerType = when (val unwrappedType = type.unwrapTypedefs()) {
|
||||
is IntegerType -> unwrappedType
|
||||
CharType -> IntegerType(1, true, "char")
|
||||
else -> return null
|
||||
}
|
||||
val size = integerType.size
|
||||
if (size != 1 && size != 2 && size != 4 && size != 8) return null
|
||||
return IntegralConstantStub(value, size, declarationMapper.isMappedToSigned(integerType))
|
||||
}
|
||||
|
||||
override fun tryCreateDoubleStub(type: Type, value: Double): DoubleConstantStub? {
|
||||
val unwrappedType = type.unwrapTypedefs() as? FloatingType ?: return null
|
||||
val size = unwrappedType.size
|
||||
if (size != 4 && size != 8) return null
|
||||
return DoubleConstantStub(value, size)
|
||||
}
|
||||
|
||||
override val bridgeComponentsBuilder = BridgeGenerationComponentsBuilder()
|
||||
|
||||
override val wrapperComponentsBuilder = WrapperGenerationComponentsBuilder()
|
||||
|
||||
override fun getKotlinClassFor(objCClassOrProtocol: ObjCClassOrProtocol, isMeta: Boolean): Classifier {
|
||||
return declarationMapper.getKotlinClassFor(objCClassOrProtocol, isMeta)
|
||||
}
|
||||
|
||||
override fun getKotlinClassForPointed(structDecl: StructDecl): Classifier {
|
||||
val classifier = declarationMapper.getKotlinClassForPointed(structDecl)
|
||||
return classifier
|
||||
}
|
||||
}
|
||||
|
||||
data class StubIrBuilderResult(
|
||||
val stubs: SimpleStubContainer,
|
||||
val declarationMapper: DeclarationMapper,
|
||||
val bridgeGenerationComponents: BridgeGenerationComponents,
|
||||
val wrapperGenerationComponents: WrapperGenerationComponents
|
||||
)
|
||||
|
||||
/**
|
||||
* Produces [StubIrBuilderResult] for given [KotlinPlatform] using [InteropConfiguration].
|
||||
*/
|
||||
class StubIrBuilder(private val context: StubIrContext) {
|
||||
|
||||
private val configuration = context.configuration
|
||||
private val nativeIndex: NativeIndex = context.nativeIndex
|
||||
|
||||
private val classes = mutableListOf<ClassStub>()
|
||||
private val functions = mutableListOf<FunctionStub>()
|
||||
private val globals = mutableListOf<PropertyStub>()
|
||||
private val typealiases = mutableListOf<TypealiasStub>()
|
||||
private val containers = mutableListOf<SimpleStubContainer>()
|
||||
|
||||
private fun addStubs(stubs: List<StubIrElement>) = stubs.forEach(this::addStub)
|
||||
|
||||
private fun addStub(stub: StubIrElement) {
|
||||
when(stub) {
|
||||
is ClassStub -> classes += stub
|
||||
is FunctionStub -> functions += stub
|
||||
is PropertyStub -> globals += stub
|
||||
is TypealiasStub -> typealiases += stub
|
||||
is SimpleStubContainer -> containers += stub
|
||||
else -> error("Unexpected stub: $stub")
|
||||
}
|
||||
}
|
||||
|
||||
private val excludedFunctions: Set<String>
|
||||
get() = configuration.excludedFunctions
|
||||
|
||||
private val excludedMacros: Set<String>
|
||||
get() = configuration.excludedMacros
|
||||
|
||||
private val buildingContext = StubsBuildingContextImpl(context)
|
||||
|
||||
fun build(): StubIrBuilderResult {
|
||||
nativeIndex.objCProtocols.filter { !it.isForwardDeclaration }.forEach { generateStubsForObjCProtocol(it) }
|
||||
nativeIndex.objCClasses.filter { !it.isForwardDeclaration && !it.isNSStringSubclass()} .forEach { generateStubsForObjCClass(it) }
|
||||
nativeIndex.objCCategories.filter { !it.clazz.isNSStringSubclass() }.forEach { generateStubsForObjCCategory(it) }
|
||||
nativeIndex.structs.forEach { generateStubsForStruct(it) }
|
||||
nativeIndex.enums.forEach { generateStubsForEnum(it) }
|
||||
nativeIndex.functions.filter { it.name !in excludedFunctions }.forEach { generateStubsForFunction(it) }
|
||||
nativeIndex.typedefs.forEach { generateStubsForTypedef(it) }
|
||||
nativeIndex.globals.filter { it.name !in excludedFunctions }.forEach { generateStubsForGlobal(it) }
|
||||
nativeIndex.macroConstants.filter { it.name !in excludedMacros }.forEach { generateStubsForMacroConstant(it) }
|
||||
nativeIndex.wrappedMacros.filter { it.name !in excludedMacros }.forEach { generateStubsForWrappedMacro(it) }
|
||||
|
||||
val meta = StubContainerMeta()
|
||||
val stubs = SimpleStubContainer(
|
||||
meta,
|
||||
classes.toList(),
|
||||
functions.toList(),
|
||||
globals.toList(),
|
||||
typealiases.toList(),
|
||||
containers.toList()
|
||||
)
|
||||
return StubIrBuilderResult(
|
||||
stubs,
|
||||
buildingContext.declarationMapper,
|
||||
buildingContext.bridgeComponentsBuilder.build(),
|
||||
buildingContext.wrapperComponentsBuilder.build()
|
||||
)
|
||||
}
|
||||
|
||||
private fun generateStubsForWrappedMacro(macro: WrappedMacroDef) {
|
||||
try {
|
||||
generateStubsForGlobal(GlobalDecl(macro.name, macro.type, isConst = true))
|
||||
} catch (e: Throwable) {
|
||||
context.log("Warning: cannot generate stubs for macro ${macro.name}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateStubsForMacroConstant(constant: ConstantDef) {
|
||||
try {
|
||||
addStubs(MacroConstantStubBuilder(buildingContext, constant).build())
|
||||
} catch (e: Throwable) {
|
||||
context.log("Warning: cannot generate stubs for constant ${constant.name}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateStubsForEnum(enumDef: EnumDef) {
|
||||
try {
|
||||
addStubs(EnumStubBuilder(buildingContext, enumDef).build())
|
||||
} catch (e: Throwable) {
|
||||
context.log("Warning: cannot generate definition for enum ${enumDef.spelling}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateStubsForFunction(func: FunctionDecl) {
|
||||
try {
|
||||
addStubs(FunctionStubBuilder(buildingContext, func, skipOverloads = true).build())
|
||||
} catch (e: Throwable) {
|
||||
context.log("Warning: cannot generate stubs for function ${func.name}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateStubsForStruct(decl: StructDecl) {
|
||||
try {
|
||||
addStubs(StructStubBuilder(buildingContext, decl).build())
|
||||
} catch (e: Throwable) {
|
||||
context.log("Warning: cannot generate definition for struct ${decl.spelling}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateStubsForTypedef(typedefDef: TypedefDef) {
|
||||
try {
|
||||
addStubs(TypedefStubBuilder(buildingContext, typedefDef).build())
|
||||
} catch (e: Throwable) {
|
||||
context.log("Warning: cannot generate typedef ${typedefDef.name}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateStubsForGlobal(global: GlobalDecl) {
|
||||
try {
|
||||
addStubs(GlobalStubBuilder(buildingContext, global).build())
|
||||
} catch (e: Throwable) {
|
||||
context.log("Warning: cannot generate stubs for global ${global.name}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateStubsForObjCProtocol(objCProtocol: ObjCProtocol) {
|
||||
addStubs(ObjCProtocolStubBuilder(buildingContext, objCProtocol).build())
|
||||
}
|
||||
|
||||
private fun generateStubsForObjCClass(objCClass: ObjCClass) {
|
||||
addStubs(ObjCClassStubBuilder(buildingContext, objCClass).build())
|
||||
}
|
||||
|
||||
private fun generateStubsForObjCCategory(objCCategory: ObjCCategory) {
|
||||
addStubs(ObjCCategoryStubBuilder(buildingContext, objCCategory).build())
|
||||
}
|
||||
}
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
package org.jetbrains.kotlin.native.interop.gen
|
||||
|
||||
import kotlinx.metadata.klib.KlibModuleMetadata
|
||||
import org.jetbrains.kotlin.native.interop.gen.jvm.GenerationMode
|
||||
import org.jetbrains.kotlin.native.interop.gen.jvm.InteropConfiguration
|
||||
import org.jetbrains.kotlin.native.interop.gen.jvm.KotlinPlatform
|
||||
import org.jetbrains.kotlin.native.interop.indexer.*
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
class StubIrContext(
|
||||
val log: (String) -> Unit,
|
||||
val configuration: InteropConfiguration,
|
||||
val nativeIndex: NativeIndex,
|
||||
val imports: Imports,
|
||||
val platform: KotlinPlatform,
|
||||
val generationMode: GenerationMode,
|
||||
val libName: String
|
||||
) {
|
||||
val libraryForCStubs = configuration.library.copy(
|
||||
includes = mutableListOf<String>().apply {
|
||||
add("stdint.h")
|
||||
add("string.h")
|
||||
if (platform == KotlinPlatform.JVM) {
|
||||
add("jni.h")
|
||||
}
|
||||
addAll(configuration.library.includes)
|
||||
},
|
||||
compilerArgs = configuration.library.compilerArgs,
|
||||
additionalPreambleLines = configuration.library.additionalPreambleLines +
|
||||
when (configuration.library.language) {
|
||||
Language.C -> emptyList()
|
||||
Language.OBJECTIVE_C -> listOf("void objc_terminate();")
|
||||
}
|
||||
).precompileHeaders()
|
||||
|
||||
// TODO: Used only for JVM.
|
||||
val jvmFileClassName = if (configuration.pkgName.isEmpty()) {
|
||||
libName
|
||||
} else {
|
||||
configuration.pkgName.substringAfterLast('.')
|
||||
}
|
||||
|
||||
val validPackageName = configuration.pkgName.split(".").joinToString(".") {
|
||||
if (it.matches(VALID_PACKAGE_NAME_REGEX)) it else "`$it`"
|
||||
}
|
||||
|
||||
private val anonymousStructKotlinNames = mutableMapOf<StructDecl, String>()
|
||||
|
||||
private val forbiddenStructNames = run {
|
||||
val typedefNames = nativeIndex.typedefs.map { it.name }
|
||||
typedefNames.toSet()
|
||||
}
|
||||
|
||||
/**
|
||||
* The name to be used for this struct in Kotlin
|
||||
*/
|
||||
fun getKotlinName(decl: StructDecl): String {
|
||||
val spelling = decl.spelling
|
||||
if (decl.isAnonymous) {
|
||||
val names = anonymousStructKotlinNames
|
||||
return names.getOrPut(decl) {
|
||||
"anonymousStruct${names.size + 1}"
|
||||
}
|
||||
}
|
||||
|
||||
val strippedCName = if (spelling.startsWith("struct ") || spelling.startsWith("union ")) {
|
||||
spelling.substringAfter(' ')
|
||||
} else {
|
||||
spelling
|
||||
}
|
||||
|
||||
// TODO: don't mangle struct names because it wouldn't work if the struct
|
||||
// is imported into another interop library.
|
||||
return if (strippedCName !in forbiddenStructNames) strippedCName else (strippedCName + "Struct")
|
||||
}
|
||||
|
||||
fun addManifestProperties(properties: Properties) {
|
||||
val exportForwardDeclarations = configuration.exportForwardDeclarations.toMutableList()
|
||||
|
||||
nativeIndex.structs
|
||||
.filter { it.def == null }
|
||||
.mapTo(exportForwardDeclarations) {
|
||||
"$cnamesStructsPackageName.${getKotlinName(it)}"
|
||||
}
|
||||
|
||||
properties["exportForwardDeclarations"] = exportForwardDeclarations.joinToString(" ")
|
||||
|
||||
// TODO: consider exporting Objective-C class and protocol forward refs.
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val VALID_PACKAGE_NAME_REGEX = "[a-zA-Z0-9_.]+".toRegex()
|
||||
}
|
||||
}
|
||||
|
||||
class StubIrDriver(
|
||||
private val context: StubIrContext,
|
||||
private val options: DriverOptions
|
||||
) {
|
||||
data class DriverOptions(
|
||||
val entryPoint: String?,
|
||||
val moduleName: String,
|
||||
val outCFile: File,
|
||||
val outKtFileCreator: () -> File
|
||||
)
|
||||
|
||||
sealed class Result {
|
||||
object SourceCode : Result()
|
||||
|
||||
class Metadata(val metadata: KlibModuleMetadata): Result()
|
||||
}
|
||||
|
||||
fun run(): Result {
|
||||
val (entryPoint, moduleName, outCFile, outKtFile) = options
|
||||
|
||||
val builderResult = StubIrBuilder(context).build()
|
||||
val bridgeBuilderResult = StubIrBridgeBuilder(context, builderResult).build()
|
||||
|
||||
outCFile.bufferedWriter().use {
|
||||
emitCFile(context, it, entryPoint, bridgeBuilderResult.nativeBridges)
|
||||
}
|
||||
|
||||
return when (context.generationMode) {
|
||||
GenerationMode.SOURCE_CODE -> {
|
||||
emitSourceCode(outKtFile(), builderResult, bridgeBuilderResult)
|
||||
}
|
||||
GenerationMode.METADATA -> emitMetadata(builderResult, moduleName, bridgeBuilderResult)
|
||||
}
|
||||
}
|
||||
|
||||
private fun emitSourceCode(
|
||||
outKtFile: File, builderResult: StubIrBuilderResult, bridgeBuilderResult: BridgeBuilderResult
|
||||
): Result.SourceCode {
|
||||
outKtFile.bufferedWriter().use { ktFile ->
|
||||
StubIrTextEmitter(context, builderResult, bridgeBuilderResult).emit(ktFile)
|
||||
}
|
||||
return Result.SourceCode
|
||||
}
|
||||
|
||||
private fun emitMetadata(
|
||||
builderResult: StubIrBuilderResult, moduleName: String, bridgeBuilderResult: BridgeBuilderResult
|
||||
) = Result.Metadata(StubIrMetadataEmitter(context, builderResult, moduleName, bridgeBuilderResult).emit())
|
||||
|
||||
private fun emitCFile(context: StubIrContext, cFile: Appendable, entryPoint: String?, nativeBridges: NativeBridges) {
|
||||
val out = { it: String -> cFile.appendLine(it) }
|
||||
|
||||
context.libraryForCStubs.preambleLines.forEach {
|
||||
out(it)
|
||||
}
|
||||
out("")
|
||||
|
||||
out("// NOTE THIS FILE IS AUTO-GENERATED")
|
||||
out("")
|
||||
|
||||
nativeBridges.nativeLines.forEach { out(it) }
|
||||
|
||||
if (entryPoint != null) {
|
||||
out("extern int Konan_main(int argc, char** argv);")
|
||||
out("")
|
||||
out("__attribute__((__used__))")
|
||||
out("int $entryPoint(int argc, char** argv) {")
|
||||
out(" return Konan_main(argc, argv);")
|
||||
out("}")
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user