[MERGE] KT: build-1.5.20-dev-1166 KT/N: 149cc4065 OLD: 3ee0dc112
This commit is contained in:
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>
|
||||
+1
-749
@@ -1,751 +1,3 @@
|
||||
# CocoaPods integration
|
||||
|
||||
Kotlin/Native provides integration with the [CocoaPods dependency manager](https://cocoapods.org/).
|
||||
You can add dependencies on Pod libraries as well as use a multiplatform project with
|
||||
native targets as a CocoaPods dependency (Kotlin Pod).
|
||||
|
||||
You can manage Pod dependencies directly in IntelliJ IDEA and enjoy all the additional features such as code highlighting
|
||||
and completion. You can build the whole Kotlin project with Gradle and not ever have to switch to Xcode.
|
||||
|
||||
Use Xcode only when you need to write Swift/Objective-C code or run your application on a simulator or device.
|
||||
To work correctly with Xcode, you should [update your Podfile](#update-podfile-for-xcode).
|
||||
|
||||
Depending on your project and purposes, you can add dependencies between [a Kotlin project and a Pod library](#add-dependencies-on-pod-libraries) as well as [a Kotlin Pod and an Xcode project](#use-a-kotlin-gradle-project-as-a-cocoapods-dependency).
|
||||
|
||||
>You can also add dependencies between a Kotlin Pod and multiple Xcode projects. However, in this case you need to add a
|
||||
>dependency by calling `pod install` manually for each Xcode project. In other cases, it's done automatically.
|
||||
{:.note}
|
||||
|
||||
## Install the CocoaPods dependency manager and plugin
|
||||
|
||||
1. Install the [CocoaPods dependency manager](https://cocoapods.org/).
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="ruby" data-highlight-only>
|
||||
|
||||
```ruby
|
||||
$ sudo gem install cocoapods
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
2. Install the [`cocoapods-generate`](https://github.com/square/cocoapods-generate) plugin.
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="ruby" data-highlight-only>
|
||||
|
||||
```ruby
|
||||
$ sudo gem install cocoapods-generate
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
3. In `build.gradle.kts` (or `build.gradle`) of your IDEA project, apply the CocoaPods plugin as well as the Kotlin
|
||||
Multiplatform plugin.
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
plugins {
|
||||
kotlin("multiplatform") version "{{ site.data.releases.latest.version }}"
|
||||
kotlin("native.cocoapods") version "{{ site.data.releases.latest.version }}"
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
4. Configure `summary`, `homepage`, and `frameworkName`of the `Podspec` file in the `cocoapods` block.
|
||||
`version` is a version of the Gradle project.
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
plugins {
|
||||
kotlin("multiplatform") version "{{ site.data.releases.latest.version }}"
|
||||
kotlin("native.cocoapods") version "{{ site.data.releases.latest.version }}"
|
||||
}
|
||||
|
||||
// CocoaPods requires the podspec to have a version.
|
||||
version = "1.0"
|
||||
|
||||
kotlin {
|
||||
cocoapods {
|
||||
// Configure fields required by CocoaPods.
|
||||
summary = "Some description for a Kotlin/Native module"
|
||||
homepage = "Link to a Kotlin/Native module homepage"
|
||||
|
||||
// You can change the name of the produced framework.
|
||||
// By default, it is the name of the Gradle project.
|
||||
frameworkName = "my_framework"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
5. Re-import the project.
|
||||
|
||||
6. Generate the [Gradle wrapper](https://docs.gradle.org/current/userguide/gradle_wrapper.html) to avoid compatibility issues during an Xcode build.
|
||||
|
||||
When applied, the CocoaPods plugin does the following:
|
||||
|
||||
* Adds both `debug` and `release` frameworks as output binaries for all macOS, iOS, tvOS, and watchOS targets.
|
||||
* Creates a `podspec` task which generates a [Podspec](https://guides.cocoapods.org/syntax/podspec.html)
|
||||
file for the project.
|
||||
|
||||
The `Podspec` file includes a path to an output framework and script phases that automate building this framework during
|
||||
the build process of an Xcode project.
|
||||
|
||||
## Add dependencies on Pod libraries
|
||||
|
||||
To add dependencies between a Kotlin project and a Pod library, you should [complete the initial configuration](#install-the-cocoapods-dependency-manager-and-plugin).
|
||||
This allows you to add dependencies on the following types of Pod libraries:
|
||||
* [A Pod library from the CocoaPods repository](#add-a-dependency-on-a-pod-library-from-the-cocoapods-repository)
|
||||
* [A Pod library stored locally](#add-a-dependency-on-a-pod-library-stored-locally)
|
||||
* [A Pod library from a Git repository](#add-a-dependency-on-a-pod-library-from-the-git-repository)
|
||||
* [A Pod library from an archive](#add-a-dependency-on-a-pod-library-from-an-archive)
|
||||
* [A Pod library from a custom Podspec repository](#add-a-dependency-on-a-pod-library-from-a-custom-podspec-repository)
|
||||
* [A Pod library with custom cinterop options](#add-a-dependency-on-a-pod-library-with-custom-cinterop-options)
|
||||
* [A static Pod library](#add-a-dependency-on-a-static-pod-library)
|
||||
|
||||
A Kotlin project requires the `pod()` function call in `build.gradle.kts` (`build.gradle`) for adding a Pod dependency. Each dependency requires its own separate function call.
|
||||
You can specify the parameters for the dependency in the configuration block of the function.
|
||||
|
||||
When you add a new dependency and re-import the project in IntelliJ IDEA, the new dependency will be added automatically.
|
||||
No additional steps are required.
|
||||
|
||||
To use your Kotlin project with Xcode, you should [make changes in your project Podfile](#update-podfile-for-xcode).
|
||||
|
||||
### Add a dependency on a Pod library from the CocoaPods repository
|
||||
|
||||
You can add dependencies on a Pod library from the CocoaPods repository with `pod()` to `build.gradle.kts`
|
||||
(`build.gradle`) of your project:
|
||||
|
||||
1. Specify the name of a Pod library in the `pod()` function. In the configuration block you can specify the version of the library using the `version` parameter. To use the latest version of the library, you can just omit this parameter all-together.
|
||||
|
||||
> You can add dependencies on subspecs.
|
||||
{:.note}
|
||||
|
||||
2. Specify the minimum deployment target version for the Pod library.
|
||||
|
||||
> If you don't specify the minimum deployment target version and a dependency Pod requires a higher deployment target, you will get an error.
|
||||
{:.note}
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
kotlin {
|
||||
ios()
|
||||
|
||||
cocoapods {
|
||||
|
||||
ios.deploymentTarget = "13.5"
|
||||
|
||||
summary = "CocoaPods test library"
|
||||
homepage = "https://github.com/JetBrains/kotlin"
|
||||
|
||||
pod("AFNetworking") {
|
||||
version = "~> 4.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
3. Re-import the project.
|
||||
|
||||
To use these dependencies from the Kotlin code, import the packages `cocoapods.<library-name>`.
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
import cocoapods.AFNetworking.*
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
You can find a sample project [here](https://github.com/Kotlin/kotlin-with-cocoapods-sample).
|
||||
|
||||
### Add a dependency on a Pod library stored locally
|
||||
|
||||
You can add a dependency on a Pod library stored locally with `pod()` to `build.gradle.kts` (`build.gradle`) of your project:
|
||||
|
||||
1. Specify the name of a Pod library in the `pod()` function. In the configuration block specify the path to the local Pod library: use the `path()` function in the `source` parameter value.
|
||||
|
||||
> You can add local dependencies on subspecs as well.
|
||||
> The `cocoapods` block can include dependencies to Pods stored locally and Pods from the CocoaPods repository at
|
||||
> the same time.
|
||||
{:.note}
|
||||
|
||||
2. Specify the minimum deployment target version for the Pod library.
|
||||
|
||||
> If you don't specify the minimum deployment target version and a dependency Pod requires a higher deployment target, you will get an error.
|
||||
{:.note}
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
kotlin {
|
||||
ios()
|
||||
|
||||
cocoapods {
|
||||
summary = "CocoaPods test library"
|
||||
homepage = "https://github.com/JetBrains/kotlin"
|
||||
|
||||
ios.deploymentTarget = "13.5"
|
||||
|
||||
pod("pod_dependency") {
|
||||
version = "1.0"
|
||||
source = path(project.file("../pod_dependency/pod_dependency.podspec"))
|
||||
}
|
||||
pod("subspec_dependency/Core") {
|
||||
version = "1.0"
|
||||
source = path(project.file("../subspec_dependency/subspec_dependency.podspec"))
|
||||
}
|
||||
pod("AFNetworking") {
|
||||
version = "~> 4.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
> You can also specify the version of the library using `version` parameter in the configuration block.
|
||||
> To use the latest version of the library, omit the parameter.
|
||||
{:.note}
|
||||
|
||||
3. Re-import the project.
|
||||
|
||||
To use these dependencies from the Kotlin code, import the packages `cocoapods.<library-name>`.
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
import cocoapods.pod_dependency.*
|
||||
import cocoapods.subspec_dependency.*
|
||||
import cocoapods.AFNetworking.*
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
You can find a sample project [here](https://github.com/Kotlin/kotlin-with-cocoapods-sample).
|
||||
|
||||
### Add a dependency on a Pod library from the Git repository
|
||||
|
||||
You can add dependencies on a Pod library from a custom Git repository with `pod()` to `build.gradle.kts`
|
||||
(`build.gradle`) of your project:
|
||||
|
||||
1. Specify the name of a Pod library in the `pod()` function.
|
||||
In the configuration block specify the path to the git repository: use the `git()` function in the `source` parameter value.
|
||||
|
||||
Additionally, you can specify the following parameters in the block after `git()`:
|
||||
* `commit` – to use a specific commit from the repository
|
||||
* `tag` – to use a specific tag from the repository
|
||||
* `branch` – to use a specific branch from the repository
|
||||
|
||||
The `git()` function prioritizes passed parameters in the following order: `commit`, `tag`, `branch`.
|
||||
If you don't specify a parameter, the Kotlin plugin uses `HEAD` from the `master` branch.
|
||||
|
||||
> You can combine `branch`, `commit`, and `tag` parameters to get the specific version of a Pod.
|
||||
{:.note}
|
||||
|
||||
2. Specify the minimum deployment target version for the Pod library.
|
||||
|
||||
> If you don't specify the minimum deployment target version and a dependency Pod requires a higher deployment target, you will get an error.
|
||||
{:.note}
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
kotlin {
|
||||
ios()
|
||||
|
||||
cocoapods {
|
||||
summary = "CocoaPods test library"
|
||||
homepage = "https://github.com/JetBrains/kotlin"
|
||||
|
||||
ios.deploymentTarget = "13.5"
|
||||
|
||||
pod("AFNetworking") {
|
||||
source = git("https://github.com/AFNetworking/AFNetworking") {
|
||||
tag = "4.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
pod("JSONModel") {
|
||||
source = git("https://github.com/jsonmodel/jsonmodel.git") {
|
||||
branch = "key-mapper-class"
|
||||
}
|
||||
}
|
||||
|
||||
pod("CocoaLumberjack") {
|
||||
source = git("https://github.com/CocoaLumberjack/CocoaLumberjack.git") {
|
||||
commit = "3e7f595e3a459c39b917aacf9856cd2a48c4dbf3"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
3. Re-import the project.
|
||||
|
||||
> To work correctly with Xcode, you should specify the path to the Podspec in your Podfile.
|
||||
> For example:
|
||||
>
|
||||
> <div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
>
|
||||
> ```ruby
|
||||
> target 'ios-app' do
|
||||
> # ... other pod depedencies ...
|
||||
> pod 'JSONModel', :path => '../cocoapods/kotlin-with-cocoapods-sample/kotlin-library/build/cocoapods/externalSources/git/JSONModel'
|
||||
> end
|
||||
> ```
|
||||
>
|
||||
> </div>
|
||||
>
|
||||
{:.note}
|
||||
|
||||
To use these dependencies from the Kotlin code, import the packages `cocoapods.<library-name>`.
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
import cocoapods.AFNetworking.*
|
||||
import cocoapods.JSONModel.*
|
||||
import cocoapods.CocoaLumberjack.*
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
You can find a sample project [here](https://github.com/Kotlin/kotlin-with-cocoapods-sample).
|
||||
|
||||
### Add a dependency on a Pod library from an archive
|
||||
|
||||
You can add dependencies on a Pod library from `zip`, `tar`, or `jar` archive with `pod()` to `build.gradle.kts`
|
||||
(`build.gradle`) of your project:
|
||||
|
||||
1. Specify the name of a Pod library in the `pod()` function.
|
||||
In the configuration block specify the path to the archive: use the `url()` function with an arbitrary HTTP address in the `source` parameter value.
|
||||
|
||||
Additionally, you can specify the boolean `flatten` parameter as a second argument for the `url()` function.
|
||||
This parameter indicates that all the Pod files are located in the root directory of the archive.
|
||||
|
||||
2. Specify the minimum deployment target version for the Pod library.
|
||||
|
||||
> If you don't specify the minimum deployment target version and a dependency Pod requires a higher deployment target, you will get an error.
|
||||
{:.note}
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
kotlin {
|
||||
ios()
|
||||
|
||||
cocoapods {
|
||||
summary = "CocoaPods test library"
|
||||
homepage = "https://github.com/JetBrains/kotlin"
|
||||
|
||||
ios.deploymentTarget = "13.5"
|
||||
|
||||
pod("pod_dependency") {
|
||||
source = url("https://github.com/Kotlin/kotlin-with-cocoapods-sample/raw/cocoapods-zip/cocoapodSourcesZip.zip", flatten = true)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
3. Re-import the project.
|
||||
|
||||
> To work correctly with Xcode, you should specify the path to the Podspec in your Podfile.
|
||||
> For example:
|
||||
>
|
||||
> <div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
>
|
||||
> ```ruby
|
||||
> target 'ios-app' do
|
||||
> # ... other pod depedencies ...
|
||||
> pod 'podspecWithFilesExample', :path => '../cocoapods/kotlin-with-cocoapods-sample/pod_dependency'
|
||||
> end
|
||||
> ```
|
||||
>
|
||||
> </div>
|
||||
>
|
||||
{:.note}
|
||||
|
||||
To use these dependencies from the Kotlin code, import the packages `cocoapods.<library-name>`.
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
import cocoapods.pod_dependency.*
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
You can find a sample project [here](https://github.com/Kotlin/kotlin-with-cocoapods-sample).
|
||||
|
||||
### Add a dependency on a Pod library from a custom Podspec repository
|
||||
|
||||
You can add dependencies on a Pod library from a custom Podspec repository with `pod()` and `specRepos` to `build.gradle.kts`
|
||||
(`build.gradle`) of your project:
|
||||
|
||||
1. Specify the HTTP address to the custom Podspec repository using the `url()` inside the `specRepos` block.
|
||||
|
||||
2. Specify the name of a Pod library in the `pod()` function.
|
||||
|
||||
3. Specify the minimum deployment target version for the Pod library.
|
||||
|
||||
> If you don't specify the minimum deployment target version and a dependency Pod requires a higher deployment target, you will get an error.
|
||||
{:.note}
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
kotlin {
|
||||
ios()
|
||||
|
||||
cocoapods {
|
||||
summary = "CocoaPods test library"
|
||||
homepage = "https://github.com/JetBrains/kotlin"
|
||||
|
||||
ios.deploymentTarget = "13.5"
|
||||
|
||||
specRepos {
|
||||
url("https://github.com/Kotlin/kotlin-cocoapods-spec.git")
|
||||
}
|
||||
pod("example")
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
4. Re-import the project.
|
||||
|
||||
> To work correctly with Xcode, you should specify the location of specs at the beginning of your Podfile.
|
||||
> For example:
|
||||
>
|
||||
> <div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
>
|
||||
> ```ruby
|
||||
> source 'https://github.com/Kotlin/kotlin-cocoapods-spec.git'
|
||||
> ```
|
||||
>
|
||||
> </div>
|
||||
>
|
||||
> You should also specify the path to the Podspec in your Podfile.
|
||||
> For example:
|
||||
>
|
||||
> <div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
>
|
||||
> ```ruby
|
||||
> target 'ios-app' do
|
||||
> # ... other pod depedencies ...
|
||||
> pod 'podspecWithFilesExample', :path => '../cocoapods/kotlin-with-cocoapods-sample/pod_dependency'
|
||||
> end
|
||||
> ```
|
||||
>
|
||||
> </div>
|
||||
>
|
||||
{:.note}
|
||||
|
||||
To use these dependencies from the Kotlin code, import the packages `cocoapods.<library-name>`.
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
import cocoapods.example.*
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
You can find a sample project [here](https://github.com/Kotlin/kotlin-with-cocoapods-sample).
|
||||
|
||||
### Add a dependency on a Pod library with custom cinterop options
|
||||
|
||||
You can add dependencies on a Pod library with custom cinterop options with `pod()` to `build.gradle.kts`
|
||||
(`build.gradle`) of your project:
|
||||
|
||||
1. Specify the name of a Pod library in the `pod()` function.
|
||||
In the configuration block specify the cinterop options:
|
||||
|
||||
* `extraOpts` – to specify the list of options for a Pod library. For example, specific flags: `extraOpts = listOf("-compiler-option")`
|
||||
* `packageName` – to specify the package name. If you specify this, you can import the library using the package name: `import <packageName>`.
|
||||
|
||||
2. Specify the minimum deployment target version for the Pod library.
|
||||
|
||||
> If you don't specify the minimum deployment target version and a dependency Pod requires a higher deployment target, you will get an error.
|
||||
{:.note}
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
kotlin {
|
||||
ios()
|
||||
|
||||
cocoapods {
|
||||
summary = "CocoaPods test library"
|
||||
homepage = "https://github.com/JetBrains/kotlin"
|
||||
|
||||
ios.deploymentTarget = "13.5"
|
||||
|
||||
useLibraries()
|
||||
|
||||
pod("YandexMapKit") {
|
||||
packageName = "YandexMK"
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
3. Re-import the project.
|
||||
|
||||
To use these dependencies from the Kotlin code, import the packages `cocoapods.<library-name>`.
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
import cocoapods.YandexMapKit.*
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
If you use the `packageName` parameter, you can import the library using the package name: `import <packageName>`:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
import YandexMK.YMKPoint
|
||||
import YandexMK.YMKDistance
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
### Add a dependency on a static Pod library
|
||||
|
||||
You can add dependencies on a static Pod library with `pod()` and `useLibraries()` to `build.gradle.kts`
|
||||
(`build.gradle`) of your project:
|
||||
|
||||
1. Specify the name of the library using the `pod()` function.
|
||||
|
||||
2. Call the `useLibraries()` function: it enables a special flag for static libraries.
|
||||
|
||||
3. Specify the minimum deployment target version for the Pod library.
|
||||
|
||||
> If you don't specify the minimum deployment target version and a dependency Pod requires a higher deployment target, you will get an error.
|
||||
{:.note}
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
kotlin {
|
||||
ios()
|
||||
|
||||
cocoapods {
|
||||
summary = "CocoaPods test library"
|
||||
homepage = "https://github.com/JetBrains/kotlin"
|
||||
|
||||
ios.deploymentTarget = "13.5"
|
||||
|
||||
pod("YandexMapKit") {
|
||||
version = "~> 3.2"
|
||||
}
|
||||
useLibraries()
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
4. Re-import the project.
|
||||
|
||||
To use these dependencies from the Kotlin code, import the packages `cocoapods.<library-name>`.
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
import cocoapods.YandexMapKit.*
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
### Update Podfile for Xcode
|
||||
|
||||
If you want to import your Kotlin project in an Xcode project, you’ll need to make some changes to your Podfile for it to work correctly:
|
||||
|
||||
* If your project has any Git, HTTP, or custom Podspec repository dependencies, you should also specify the path to the Podspec in the Podfile.
|
||||
|
||||
For example, if you add a dependency on `podspecWithFilesExample`, declare the path to the Podspec in the Podfile:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```ruby
|
||||
target 'ios-app' do
|
||||
# ... other depedencies ...
|
||||
pod 'podspecWithFilesExample', :path => 'cocoapods/externalSources/url/podspecWithFilesExample'
|
||||
end
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
The `:path` should contain the filepath to the Pod.
|
||||
|
||||
* When you add a library from the custom Podspec repository, you should also specify the [location](https://guides.cocoapods.org/syntax/podfile.html#source) of specs at the beginning of your Podfile:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```ruby
|
||||
source 'https://github.com/Kotlin/kotlin-cocoapods-spec.git'
|
||||
|
||||
target 'kotlin-cocoapods-xcproj' do
|
||||
# ... other depedencies ...
|
||||
pod 'example'
|
||||
end
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
> Re-import the project after making changes in Podfile.
|
||||
{:.note}
|
||||
|
||||
If you don't make these changes to the Podfile, the `podInstall` task will fail and the CocoaPods plugin will show an error message in the log.
|
||||
|
||||
Check out the `withXcproject` branch of the [sample project](https://github.com/Kotlin/kotlin-with-cocoapods-sample), which contains an example of Xcode integration with an existing Xcode project named `kotlin-cocoapods-xcproj`.
|
||||
|
||||
## Use a Kotlin Gradle project as a CocoaPods dependency
|
||||
|
||||
You can use a Kotlin Multiplatform project with native targets as a CocoaPods dependency (Kotlin Pod). You can include such a dependency
|
||||
in the Podfile of the Xcode project by its name and path to the project directory containing the generated Podspec.
|
||||
This dependency will be automatically built (and rebuilt) along with this project.
|
||||
Such an approach simplifies importing to Xcode by removing a need to write the corresponding Gradle tasks and Xcode build steps manually.
|
||||
|
||||
You can add dependencies between:
|
||||
* [A Kotlin Pod and an Xcode project with one target](#add-a-dependency-between-a-kotlin-pod-and-xcode-project-with-one-target)
|
||||
* [A Kotlin Pod and an Xcode project with several targets](#add-a-dependency-between-a-kotlin-pod-with-an-xcode-project-with-several-targets)
|
||||
|
||||
> To correctly import the dependencies into the Kotlin/Native module, the
|
||||
`Podfile` must contain either [`use_modular_headers!`](https://guides.cocoapods.org/syntax/podfile.html#use_modular_headers_bang)
|
||||
or [`use_frameworks!`](https://guides.cocoapods.org/syntax/podfile.html#use_frameworks_bang)
|
||||
directive.
|
||||
{:.note}
|
||||
|
||||
### Add a dependency between a Kotlin Pod and Xcode project with one target
|
||||
|
||||
1. Create an Xcode project with a `Podfile` if you haven’t done so yet.
|
||||
2. Add the path to your Xcode project `Podfile` with `podfile = project.file(..)` to `build.gradle.kts` (`build.gradle`)
|
||||
of your Kotlin project.
|
||||
This step helps synchronize your Xcode project with Kotlin Pod dependencies by calling `pod install` for your `Podfile`.
|
||||
3. Specify the minimum deployment target version for the Pod library.
|
||||
> If you don't specify the minimum deployment target version and a dependency Pod requires a higher deployment target, you will get an error.
|
||||
{:.note}
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
kotlin {
|
||||
ios()
|
||||
|
||||
cocoapods {
|
||||
summary = "CocoaPods test library"
|
||||
homepage = "https://github.com/JetBrains/kotlin"
|
||||
ios.deploymentTarget = "13.5"
|
||||
pod("AFNetworking") {
|
||||
version = "~> 4.0.0"
|
||||
}
|
||||
podfile = project.file("../ios-app/Podfile")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
4. Add the name and path of the Kotlin Pod you want to include in the Xcode project to `Podfile`.
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="ruby" data-highlight-only>
|
||||
|
||||
```ruby
|
||||
use_frameworks!
|
||||
|
||||
platform :ios, '13.5'
|
||||
|
||||
target 'ios-app' do
|
||||
pod 'kotlin_library', :path => '../kotlin-library'
|
||||
end
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
5. Re-import the project.
|
||||
|
||||
### Add a dependency between a Kotlin Pod with an Xcode project with several targets
|
||||
|
||||
1. Create an Xcode project with a `Podfile` if you haven’t done so yet.
|
||||
2. Add the path to your Xcode project `Podfile` with `podfile = project.file(..)` to `build.gradle.kts` (`build.gradle`) of
|
||||
your Kotlin project.
|
||||
This step helps synchronize your Xcode project with Kotlin Pod dependencies by calling `pod install` for your `Podfile`.
|
||||
3. Add dependencies to the Pod libraries that you want to use in your project with `pod()`.
|
||||
4. For each target, specify the minimum deployment target version for the Pod library.
|
||||
> If you don't specify the minimum deployment target version and a dependency Pod requires a higher deployment target, you will get an error.
|
||||
{:.note}
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
kotlin {
|
||||
ios()
|
||||
tvos()
|
||||
|
||||
cocoapods {
|
||||
summary = "CocoaPods test library"
|
||||
homepage = "https://github.com/JetBrains/kotlin"
|
||||
ios.deploymentTarget = "13.5"
|
||||
tvos.deploymentTarget = "13.4"
|
||||
|
||||
pod("AFNetworking") {
|
||||
version = "~> 4.0.0"
|
||||
}
|
||||
podfile = project.file("../severalTargetsXcodeProject/Podfile") // specify the path to Podfile
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
5. Add the name and path of the Kotlin Pod you want to include in the Xcode project to the `Podfile`.
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="ruby" data-highlight-only>
|
||||
|
||||
```ruby
|
||||
target 'iosApp' do
|
||||
use_frameworks!
|
||||
platform :ios, '13.5'
|
||||
# Pods for iosApp
|
||||
pod 'kotlin_library', :path => '../kotlin-library'
|
||||
end
|
||||
|
||||
target 'TVosApp' do
|
||||
use_frameworks!
|
||||
platform :tvos, '13.4'
|
||||
|
||||
# Pods for TVosApp
|
||||
pod 'kotlin_library', :path => '../kotlin-library'
|
||||
end
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
6. Re-import the project.
|
||||
|
||||
You can find a sample project [here](https://github.com/Kotlin/multitarget-xcode-with-kotlin-cocoapods-sample).
|
||||
The content of this page is moved to https://kotlinlang.org/docs/native-cocoapods.html
|
||||
@@ -1,222 +1,3 @@
|
||||
## Concurrency in Kotlin/Native
|
||||
|
||||
Kotlin/Native runtime doesn't encourage a classical thread-oriented concurrency
|
||||
model with mutually exclusive code blocks and conditional variables, as this model is
|
||||
known to be error-prone and unreliable. Instead, we suggest a collection of
|
||||
alternative approaches, allowing you to use hardware concurrency and implement blocking IO.
|
||||
Those approaches are as follows, and they will be elaborated on in further sections:
|
||||
* Workers with message passing
|
||||
* Object subgraph ownership transfer
|
||||
* Object subgraph freezing
|
||||
* Object subgraph detachment
|
||||
* Raw shared memory using C globals
|
||||
* Atomic primitives and references
|
||||
* Coroutines for blocking operations (not covered in this document)
|
||||
|
||||
### Workers
|
||||
|
||||
Instead of threads Kotlin/Native runtime offers the concept of workers: concurrently executed
|
||||
control flow streams with an associated request queue. Workers are very similar to the actors
|
||||
in the Actor Model. A worker can exchange Kotlin objects with another worker, so that at any moment
|
||||
each mutable object is owned by a single worker, but ownership can be transferred.
|
||||
See section [Object transfer and freezing](#transfer).
|
||||
|
||||
Once a worker is started with the `Worker.start` function call, it can be addressed with its own unique integer
|
||||
worker id. Other workers, or non-worker concurrency primitives, such as OS threads, can send a message
|
||||
to the worker with the `execute` call.
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
val future = execute(TransferMode.SAFE, { SomeDataForWorker() }) {
|
||||
// data returned by the second function argument comes to the
|
||||
// worker routine as 'input' parameter.
|
||||
input ->
|
||||
// Here we create an instance to be returned when someone consumes result future.
|
||||
WorkerResult(input.stringParam + " result")
|
||||
}
|
||||
|
||||
future.consume {
|
||||
// Here we see result returned from routine above. Note that future object or
|
||||
// id could be transferred to another worker, so we don't have to consume future
|
||||
// in same execution context it was obtained.
|
||||
result -> println("result is $result")
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
The call to `execute` uses a function passed as its second parameter to produce an object subgraph
|
||||
(i.e. set of mutually referring objects) which is then passed as a whole to that worker, it is then no longer
|
||||
available to the thread that initiated the request. This property is checked if the first parameter
|
||||
is `TransferMode.SAFE` by graph traversal and is just assumed to be true, if it is `TransferMode.UNSAFE`.
|
||||
The last parameter to `execute` is a special Kotlin lambda, which is not allowed to capture any state,
|
||||
and is actually invoked in the target worker's context. Once processed, the result is transferred to whatever consumes
|
||||
it in the future, and it is attached to the object graph of that worker/thread.
|
||||
|
||||
If an object is transferred in `UNSAFE` mode and is still accessible from multiple concurrent executors,
|
||||
program will likely crash unexpectedly, so consider that last resort in optimizing, not a general purpose
|
||||
mechanism.
|
||||
|
||||
For a more complete example please refer to the [workers example](https://github.com/JetBrains/kotlin-native/tree/master/samples/workers)
|
||||
in the Kotlin/Native repository.
|
||||
|
||||
<a name="transfer"></a>
|
||||
### Object transfer and freezing
|
||||
|
||||
An important invariant that Kotlin/Native runtime maintains is that the object is either owned by a single
|
||||
thread/worker, or it is immutable (_shared XOR mutable_). This ensures that the same data has a single mutator,
|
||||
and so there is no need for locking to exist. To achieve such an invariant, we use the concept of not externally
|
||||
referred object subgraphs.
|
||||
This is a subgraph which has no external references from outside of the subgraph, which could be checked
|
||||
algorithmically with O(N) complexity (in ARC systems), where N is the number of elements in such a subgraph.
|
||||
Such subgraphs are usually produced as a result of a lambda expression, for example some builder, and may not
|
||||
contain objects, referred to externally.
|
||||
|
||||
Freezing is a runtime operation making a given object subgraph immutable, by modifying the object header
|
||||
so that future mutation attempts throw an `InvalidMutabilityException`. It is deep, so
|
||||
if an object has a pointer to other objects - transitive closure of such objects will be frozen.
|
||||
Freezing is a one way transformation, frozen objects cannot be unfrozen. Frozen objects have a nice
|
||||
property that due to their immutability, they can be freely shared between multiple workers/threads
|
||||
without breaking the "mutable XOR shared" invariant.
|
||||
|
||||
If an object is frozen it can be checked with an extension property `isFrozen`, and if it is, object sharing
|
||||
is allowed. Currently, Kotlin/Native runtime only freezes the enum objects after creation, although additional
|
||||
autofreezing of certain provably immutable objects could be implemented in the future.
|
||||
|
||||
<a name="detach"></a>
|
||||
### Object subgraph detachment
|
||||
|
||||
An object subgraph without external references can be disconnected using `DetachedObjectGraph<T>` to
|
||||
a `COpaquePointer` value, which could be stored in `void*` data, so the disconnected object subgraphs
|
||||
can be stored in a C data structure, and later attached back with `DetachedObjectGraph<T>.attach()` in an arbitrary thread
|
||||
or a worker. Combining it with [raw memory sharing](#shared) it allows side channel object transfer between
|
||||
concurrent threads, if the worker mechanisms are insufficient for a particular task. Note, that object detachment
|
||||
may require explicit leaving function holding object references and then performing cyclic garbage collection.
|
||||
For example, code like:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
val graph = DetachedObjectGraph {
|
||||
val map = mutableMapOf<String, String>()
|
||||
for (entry in map.entries) {
|
||||
// ...
|
||||
}
|
||||
map
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
will not work as expected and will throw runtime exception, as there are uncollected cycles in the detached graph, while:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
val graph = DetachedObjectGraph {
|
||||
{
|
||||
val map = mutableMapOf<String, String>()
|
||||
for (entry in map.entries) {
|
||||
// ...
|
||||
}
|
||||
map
|
||||
}().also {
|
||||
kotlin.native.internal.GC.collect()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
will work properly, as holding references will be released, and then cyclic garbage affecting reference counter is
|
||||
collected.
|
||||
|
||||
<a name="shared"></a>
|
||||
### Raw shared memory
|
||||
|
||||
Considering the strong ties between Kotlin/Native and C via interoperability, in conjunction with the other mechanisms
|
||||
mentioned above it is possible to build popular data structures, like concurrent hashmap or shared cache with
|
||||
Kotlin/Native. It is possible to rely upon shared C data, and store in it references to detached object subgraphs.
|
||||
Consider the following .def file:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="c">
|
||||
|
||||
```c
|
||||
package = global
|
||||
|
||||
---
|
||||
typedef struct {
|
||||
int version;
|
||||
void* kotlinObject;
|
||||
} SharedData;
|
||||
|
||||
SharedData sharedData;
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
After running the cinterop tool it can share Kotlin data in a versionized global structure,
|
||||
and interact with it from Kotlin transparently via autogenerated Kotlin like this:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
class SharedData(rawPtr: NativePtr) : CStructVar(rawPtr) {
|
||||
var version: Int
|
||||
var kotlinObject: COpaquePointer?
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
So in combination with the top level variable declared above, it can allow looking at the same memory from different
|
||||
threads and building traditional concurrent structures with platform-specific synchronization primitives.
|
||||
|
||||
<a name="top_level"></a>
|
||||
### Global variables and singletons
|
||||
|
||||
Frequently, global variables are a source of unintended concurrency issues, so _Kotlin/Native_ implements
|
||||
the following mechanisms to prevent the unintended sharing of state via global objects:
|
||||
|
||||
* global variables, unless specially marked, can be only accessed from the main thread (that is, the thread
|
||||
_Kotlin/Native_ runtime was first initialized), if other thread access such a global, `IncorrectDereferenceException` is thrown
|
||||
* for global variables marked with the `@kotlin.native.ThreadLocal` annotation each threads keeps thread-local copy,
|
||||
so changes are not visible between threads
|
||||
* for global variables marked with the `@kotlin.native.SharedImmutable` annotation value is shared, but frozen
|
||||
before publishing, so each threads sees the same value
|
||||
* singleton objects unless marked with `@kotlin.native.ThreadLocal` are frozen and shared, lazy values allowed,
|
||||
unless cyclic frozen structures were attempted to be created
|
||||
* enums are always frozen
|
||||
|
||||
Combined, these mechanisms allow natural race-free programming with code reuse across platforms in MPP projects.
|
||||
|
||||
<a name="atomic_references"></a>
|
||||
### Atomic primitives and references
|
||||
|
||||
Kotlin/Native standard library provides primitives for safe working with concurrently mutable data, namely
|
||||
`AtomicInt`, `AtomicLong`, `AtomicNativePtr`, `AtomicReference` and `FreezableAtomicReference` in the package
|
||||
`kotlin.native.concurrent`.
|
||||
Atomic primitives allows concurrency-safe update operations, such as increment, decrement and compare-and-swap,
|
||||
along with value setters and getters. Atomic primitives are considered always frozen by the runtime, and
|
||||
while their fields can be updated with the regular `field.value += 1`, it is not concurrency safe.
|
||||
Value must be be changed using dedicated operations, so it is possible to perform concurrent-safe
|
||||
global counters and similar data structures.
|
||||
|
||||
Some algorithms require shared mutable references across the multiple workers, for example global mutable
|
||||
configuration could be implemented as an immutable instance of properties list atomically replaced with the
|
||||
new version on configuration update as the whole in a single transaction. This way no inconsistent configuration
|
||||
could be seen, and at the same time configuration could be updated as needed.
|
||||
To achieve such functionality Kotlin/Native runtime provides two related classes:
|
||||
`kotlin.native.concurrent.AtomicReference` and `kotlin.native.concurrent.FreezableAtomicReference`.
|
||||
Atomic reference holds reference to a frozen or immutable object, and its value could be updated by set
|
||||
or compare-and-swap operation. Thus, dedicated set of objects could be used to create mutable shared object graphs
|
||||
(of immutable objects). Cycles in the shared memory could be created using atomic references.
|
||||
Kotlin/Native runtime doesn't support garbage collecting cyclic data when reference cycle goes through
|
||||
`AtomicReference` or frozen `FreezableAtomicReference`. So to avoid memory leaks atomic references
|
||||
that are potentially parts of shared cyclic data should be zeroed out once no longer needed.
|
||||
|
||||
If atomic reference value is attempted to be set to non-frozen value runtime exception is thrown.
|
||||
|
||||
Freezable atomic reference is similar to the regular atomic reference, but until frozen behaves like regular box
|
||||
for a reference. After freezing it behaves like an atomic reference, and can only hold a reference to a frozen object.
|
||||
The content of this page is moved to https://kotlinlang.org/docs/native-concurrency.html
|
||||
+1
-261
@@ -1,263 +1,3 @@
|
||||
## Debugging
|
||||
|
||||
Currently the Kotlin/Native compiler produces debug info compatible with the DWARF 2 specification, so modern debugger tools can
|
||||
perform the following operations:
|
||||
- breakpoints
|
||||
- stepping
|
||||
- inspection of type information
|
||||
- variable inspection
|
||||
|
||||
### Producing binaries with debug info with Kotlin/Native compiler
|
||||
|
||||
To produce binaries with the Kotlin/Native compiler it's sufficient to use the ``-g`` option on the command line.<br/>
|
||||
_Example:_
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="shell">
|
||||
|
||||
```bash
|
||||
0:b-debugger-fixes:minamoto@unit-703(0)# cat - > hello.kt
|
||||
fun main(args: Array<String>) {
|
||||
println("Hello world")
|
||||
println("I need your clothes, your boots and your motocycle")
|
||||
}
|
||||
0:b-debugger-fixes:minamoto@unit-703(0)# dist/bin/konanc -g hello.kt -o terminator
|
||||
KtFile: hello.kt
|
||||
0:b-debugger-fixes:minamoto@unit-703(0)# lldb terminator.kexe
|
||||
(lldb) target create "terminator.kexe"
|
||||
Current executable set to 'terminator.kexe' (x86_64).
|
||||
(lldb) b kfun:main(kotlin.Array<kotlin.String>)
|
||||
Breakpoint 1: where = terminator.kexe`kfun:main(kotlin.Array<kotlin.String>) + 4 at hello.kt:2, address = 0x00000001000012e4
|
||||
(lldb) r
|
||||
Process 28473 launched: '/Users/minamoto/ws/.git-trees/debugger-fixes/terminator.kexe' (x86_64)
|
||||
Process 28473 stopped
|
||||
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
|
||||
frame #0: 0x00000001000012e4 terminator.kexe`kfun:main(kotlin.Array<kotlin.String>) at hello.kt:2
|
||||
1 fun main(args: Array<String>) {
|
||||
-> 2 println("Hello world")
|
||||
3 println("I need your clothes, your boots and your motocycle")
|
||||
4 }
|
||||
(lldb) n
|
||||
Hello world
|
||||
Process 28473 stopped
|
||||
* thread #1, queue = 'com.apple.main-thread', stop reason = step over
|
||||
frame #0: 0x00000001000012f0 terminator.kexe`kfun:main(kotlin.Array<kotlin.String>) at hello.kt:3
|
||||
1 fun main(args: Array<String>) {
|
||||
2 println("Hello world")
|
||||
-> 3 println("I need your clothes, your boots and your motocycle")
|
||||
4 }
|
||||
(lldb)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
### Breakpoints
|
||||
Modern debuggers provide several ways to set a breakpoint, see below for a tool-by-tool breakdown:
|
||||
|
||||
#### lldb
|
||||
- by name
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="shell">
|
||||
|
||||
```bash
|
||||
(lldb) b -n kfun:main(kotlin.Array<kotlin.String>)
|
||||
Breakpoint 4: where = terminator.kexe`kfun:main(kotlin.Array<kotlin.String>) + 4 at hello.kt:2, address = 0x00000001000012e4
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
_``-n`` is optional, this flag is applied by default_
|
||||
- by location (filename, line number)
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="shell">
|
||||
|
||||
```bash
|
||||
(lldb) b -f hello.kt -l 1
|
||||
Breakpoint 1: where = terminator.kexe`kfun:main(kotlin.Array<kotlin.String>) + 4 at hello.kt:2, address = 0x00000001000012e4
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
- by address
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="shell">
|
||||
|
||||
```bash
|
||||
(lldb) b -a 0x00000001000012e4
|
||||
Breakpoint 2: address = 0x00000001000012e4
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
- by regex, you might find it useful for debugging generated artifacts, like lambda etc. (where used ``#`` symbol in name).
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="shell">
|
||||
|
||||
```bash
|
||||
3: regex = 'main\(', locations = 1
|
||||
3.1: where = terminator.kexe`kfun:main(kotlin.Array<kotlin.String>) + 4 at hello.kt:2, address = terminator.kexe[0x00000001000012e4], unresolved, hit count = 0
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
#### gdb
|
||||
- by regex
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="shell">
|
||||
|
||||
```bash
|
||||
(gdb) rbreak main(
|
||||
Breakpoint 1 at 0x1000109b4
|
||||
struct ktype:kotlin.Unit &kfun:main(kotlin.Array<kotlin.String>);
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
- by name __unusable__, because ``:`` is a separator for the breakpoint by location
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="shell">
|
||||
|
||||
```bash
|
||||
(gdb) b kfun:main(kotlin.Array<kotlin.String>)
|
||||
No source file named kfun.
|
||||
Make breakpoint pending on future shared library load? (y or [n]) y
|
||||
Breakpoint 1 (kfun:main(kotlin.Array<kotlin.String>)) pending
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
- by location
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="shell">
|
||||
|
||||
```bash
|
||||
(gdb) b hello.kt:1
|
||||
Breakpoint 2 at 0x100001704: file /Users/minamoto/ws/.git-trees/hello.kt, line 1.
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
- by address
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="shell">
|
||||
|
||||
```bash
|
||||
(gdb) b *0x100001704
|
||||
Note: breakpoint 2 also set at pc 0x100001704.
|
||||
Breakpoint 3 at 0x100001704: file /Users/minamoto/ws/.git-trees/hello.kt, line 2.
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
### Stepping
|
||||
Stepping functions works mostly the same way as for C/C++ programs
|
||||
|
||||
### Variable inspection
|
||||
|
||||
Variable inspections for var variables works out of the box for primitive types.
|
||||
For non-primitive types there are custom pretty printers for lldb in
|
||||
`konan_lldb.py`:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="shell">
|
||||
|
||||
```bash
|
||||
λ cat main.kt | nl
|
||||
1 fun main(args: Array<String>) {
|
||||
2 var x = 1
|
||||
3 var y = 2
|
||||
4 var p = Point(x, y)
|
||||
5 println("p = $p")
|
||||
6 }
|
||||
|
||||
7 data class Point(val x: Int, val y: Int)
|
||||
|
||||
λ lldb ./program.kexe -o 'b main.kt:5' -o
|
||||
(lldb) target create "./program.kexe"
|
||||
Current executable set to './program.kexe' (x86_64).
|
||||
(lldb) b main.kt:5
|
||||
Breakpoint 1: where = program.kexe`kfun:main(kotlin.Array<kotlin.String>) + 289 at main.kt:5, address = 0x000000000040af11
|
||||
(lldb) r
|
||||
Process 4985 stopped
|
||||
* thread #1, name = 'program.kexe', stop reason = breakpoint 1.1
|
||||
frame #0: program.kexe`kfun:main(kotlin.Array<kotlin.String>) at main.kt:5
|
||||
2 var x = 1
|
||||
3 var y = 2
|
||||
4 var p = Point(x, y)
|
||||
-> 5 println("p = $p")
|
||||
6 }
|
||||
7
|
||||
8 data class Point(val x: Int, val y: Int)
|
||||
|
||||
Process 4985 launched: './program.kexe' (x86_64)
|
||||
(lldb) fr var
|
||||
(int) x = 1
|
||||
(int) y = 2
|
||||
(ObjHeader *) p = 0x00000000007643d8
|
||||
(lldb) command script import dist/tools/konan_lldb.py
|
||||
(lldb) fr var
|
||||
(int) x = 1
|
||||
(int) y = 2
|
||||
(ObjHeader *) p = [x: ..., y: ...]
|
||||
(lldb) p p
|
||||
(ObjHeader *) $2 = [x: ..., y: ...]
|
||||
(lldb) script lldb.frame.FindVariable("p").GetChildMemberWithName("x").Dereference().GetValue()
|
||||
'1'
|
||||
(lldb)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
Getting representation of the object variable (var) could also be done using the
|
||||
built-in runtime function `Konan_DebugPrint` (this approach also works for gdb,
|
||||
using a module of command syntax):
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="shell">
|
||||
|
||||
```bash
|
||||
0:b-debugger-fixes:minamoto@unit-703(0)# cat ../debugger-plugin/1.kt | nl -p
|
||||
1 fun foo(a:String, b:Int) = a + b
|
||||
2 fun one() = 1
|
||||
3 fun main(arg:Array<String>) {
|
||||
4 var a_variable = foo("(a_variable) one is ", 1)
|
||||
5 var b_variable = foo("(b_variable) two is ", 2)
|
||||
6 var c_variable = foo("(c_variable) two is ", 3)
|
||||
7 var d_variable = foo("(d_variable) two is ", 4)
|
||||
8 println(a_variable)
|
||||
9 println(b_variable)
|
||||
10 println(c_variable)
|
||||
11 println(d_variable)
|
||||
12 }
|
||||
0:b-debugger-fixes:minamoto@unit-703(0)# lldb ./program.kexe -o 'b -f 1.kt -l 9' -o r
|
||||
(lldb) target create "./program.kexe"
|
||||
Current executable set to './program.kexe' (x86_64).
|
||||
(lldb) b -f 1.kt -l 9
|
||||
Breakpoint 1: where = program.kexe`kfun:main(kotlin.Array<kotlin.String>) + 463 at 1.kt:9, address = 0x0000000100000dbf
|
||||
(lldb) r
|
||||
(a_variable) one is 1
|
||||
Process 80496 stopped
|
||||
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
|
||||
frame #0: 0x0000000100000dbf program.kexe`kfun:main(kotlin.Array<kotlin.String>) at 1.kt:9
|
||||
6 var c_variable = foo("(c_variable) two is ", 3)
|
||||
7 var d_variable = foo("(d_variable) two is ", 4)
|
||||
8 println(a_variable)
|
||||
-> 9 println(b_variable)
|
||||
10 println(c_variable)
|
||||
11 println(d_variable)
|
||||
12 }
|
||||
|
||||
Process 80496 launched: './program.kexe' (x86_64)
|
||||
(lldb) expression -- (int32_t)Konan_DebugPrint(a_variable)
|
||||
(a_variable) one is 1(int32_t) $0 = 0
|
||||
(lldb)
|
||||
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
### Known issues
|
||||
- performance of Python bindings.
|
||||
|
||||
_Note:_ Supporting the DWARF 2 specification means that the debugger tool recognizes Kotlin as C89, because before the DWARF 5 specification, there is no identifier for the Kotlin language type in specification.
|
||||
|
||||
The content of this page is moved to https://kotlinlang.org/docs/native-debugging.html
|
||||
+1
-206
@@ -1,206 +1 @@
|
||||
### Q: How do I run my program?
|
||||
|
||||
A: Define a top level function `fun main(args: Array<String>)` or just `fun main()` if you are not interested
|
||||
in passed arguments, please ensure it's not in a package.
|
||||
Also compiler switch `-entry` could be used to make any function taking `Array<String>` or no arguments
|
||||
and return `Unit` as an entry point.
|
||||
|
||||
|
||||
### Q: What is Kotlin/Native memory management model?
|
||||
|
||||
A: Kotlin/Native provides an automated memory management scheme, similar to what Java or Swift provides.
|
||||
The current implementation includes an automated reference counter with a cycle collector to collect cyclical
|
||||
garbage.
|
||||
|
||||
|
||||
### Q: How do I create a shared library?
|
||||
|
||||
A: Use the `-produce dynamic` compiler switch, or `binaries.sharedLib()` in Gradle, i.e.
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="kotlin" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
kotlin {
|
||||
iosArm64("mylib") {
|
||||
binaries.sharedLib()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
It will produce a platform-specific shared object (.so on Linux, .dylib on macOS, and .dll on Windows targets) and a
|
||||
C language header, allowing the use of all public APIs available in your Kotlin/Native program from C/C++ code.
|
||||
See `samples/python_extension` for an example of using such a shared object to provide a bridge between Python and
|
||||
Kotlin/Native.
|
||||
|
||||
|
||||
### Q: How do I create a static library or an object file?
|
||||
|
||||
A: Use the `-produce static` compiler switch, or `binaries.staticLib()` in Gradle, i.e.
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="kotlin" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
kotlin {
|
||||
iosArm64("mylib") {
|
||||
binaries.staticLib()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
It will produce a platform-specific static object (.a library format) and a C language header, allowing you to
|
||||
use all the public APIs available in your Kotlin/Native program from C/C++ code.
|
||||
|
||||
|
||||
### Q: How do I run Kotlin/Native behind a corporate proxy?
|
||||
|
||||
A: As Kotlin/Native needs to download a platform specific toolchain, you need to specify
|
||||
`-Dhttp.proxyHost=xxx -Dhttp.proxyPort=xxx` as the compiler's or `gradlew` arguments,
|
||||
or set it via the `JAVA_OPTS` environment variable.
|
||||
|
||||
|
||||
### Q: How do I specify a custom Objective-C prefix/name for my Kotlin framework?
|
||||
|
||||
A: Use the `-module-name` compiler option or matching Gradle DSL statement, i.e.
|
||||
|
||||
<div class="multi-language-sample" data-lang="kotlin">
|
||||
<div class="sample" markdown="1" theme="idea" mode="kotlin" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
kotlin {
|
||||
iosArm64("myapp") {
|
||||
binaries.framework {
|
||||
freeCompilerArgs += listOf("-module-name", "TheName")
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="multi-language-sample" data-lang="groovy">
|
||||
<div class="sample" markdown="1" theme="idea" mode="groovy">
|
||||
|
||||
```groovy
|
||||
kotlin {
|
||||
iosArm64("myapp") {
|
||||
binaries.framework {
|
||||
freeCompilerArgs += ["-module-name", "TheName"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
### Q: How do I rename the iOS framework? (default name is _\<project name\>_.framework)
|
||||
|
||||
A: Use the `baseName` option. This will also set the module name.
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="kotlin" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
kotlin {
|
||||
iosArm64("myapp") {
|
||||
binaries {
|
||||
framework {
|
||||
baseName = "TheName"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
### Q: How do I enable bitcode for my Kotlin framework?
|
||||
|
||||
A: By default gradle plugin adds it on iOS target.
|
||||
* For debug build it embeds placeholder LLVM IR data as a marker.
|
||||
* For release build it embeds bitcode as data.
|
||||
|
||||
Or commandline arguments: `-Xembed-bitcode` (for release) and `-Xembed-bitcode-marker` (debug)
|
||||
|
||||
Setting this in a Gradle DSL:
|
||||
<div class="sample" markdown="1" theme="idea" mode="kotlin" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
kotlin {
|
||||
iosArm64("myapp") {
|
||||
binaries {
|
||||
framework {
|
||||
// Use "marker" to embed the bitcode marker (for debug builds).
|
||||
// Use "disable" to disable embedding.
|
||||
embedBitcode("bitcode") // for release binaries.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
These options have nearly the same effect as clang's `-fembed-bitcode`/`-fembed-bitcode-marker`
|
||||
and swiftc's `-embed-bitcode`/`-embed-bitcode-marker`.
|
||||
|
||||
### Q: Why do I see `InvalidMutabilityException`?
|
||||
|
||||
A: It likely happens, because you are trying to mutate a frozen object. An object can transfer to the
|
||||
frozen state either explicitly, as objects reachable from objects on which the `kotlin.native.concurrent.freeze` is called,
|
||||
or implicitly (i.e. reachable from `enum` or global singleton object - see the next question).
|
||||
|
||||
|
||||
### Q: How do I make a singleton object mutable?
|
||||
|
||||
A: Currently, singleton objects are immutable (i.e. frozen after creation), and it's generally considered
|
||||
good practise to have the global state immutable. If for some reason you need a mutable state inside such an
|
||||
object, use the `@konan.ThreadLocal` annotation on the object. Also the `kotlin.native.concurrent.AtomicReference` class could be
|
||||
used to store different pointers to frozen objects in a frozen object and automatically update them.
|
||||
|
||||
### Q: How can I compile my project against the Kotlin/Native master?
|
||||
|
||||
A: One of the following should be done:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>For the CLI, you can compile using gradle as stated in the README (and if you get errors, you can try to do a <code>./gradlew clean</code>):</summary>
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="shell">
|
||||
|
||||
```bash
|
||||
./gradlew dependencies:update
|
||||
./gradlew dist distPlatformLibs
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
You can then set the `KONAN_HOME` env variable to the generated `dist` folder in the git repository.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>For Gradle, you can use <a href="https://docs.gradle.org/current/userguide/composite_builds.html">Gradle composite builds</a> like this:</summary>
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="shell">
|
||||
|
||||
|
||||
```bash
|
||||
# Set with the path of your kotlin-native clone
|
||||
export KONAN_REPO=$PWD/../kotlin-native
|
||||
|
||||
# Run this once since it is costly, you can remove the `clean` task if not big changes were made from the last time you did this
|
||||
pushd $KONAN_REPO && git pull && ./gradlew clean dependencies:update dist distPlatformLibs && popd
|
||||
|
||||
# In your project, you set have to the org.jetbrains.kotlin.native.home property, and include as composite the shared and gradle-plugin builds
|
||||
./gradlew check -Porg.jetbrains.kotlin.native.home=$KONAN_REPO/dist --include-build $KONAN_REPO/shared --include-build $KONAN_REPO/tools/kotlin-native-gradle-plugin
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
</details>
|
||||
The content of this page is moved to https://kotlinlang.org/docs/native-faq.html
|
||||
@@ -3,7 +3,7 @@
|
||||
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/reference/building-mpp-with-gradle.html).
|
||||
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
|
||||
|
||||
@@ -1,31 +1,3 @@
|
||||
# Immutability in Kotlin/Native
|
||||
|
||||
Kotlin/Native implements strict mutability checks, ensuring
|
||||
the important invariant that the object is either immutable or
|
||||
accessible from the single thread at that moment in time (`mutable XOR global`).
|
||||
|
||||
Immutability is a runtime property in Kotlin/Native, and can be applied
|
||||
to an arbitrary object subgraph using the `kotlin.native.concurrent.freeze` function.
|
||||
It makes all the objects reachable from the given one immutable,
|
||||
such a transition is a one-way operation (i.e., objects cannot be unfrozen later).
|
||||
Some naturally immutable objects such as `kotlin.String`, `kotlin.Int`, and
|
||||
other primitive types, along with `AtomicInt` and `AtomicReference` are frozen
|
||||
by default. If a mutating operation is applied to a frozen object,
|
||||
an `InvalidMutabilityException` is thrown.
|
||||
|
||||
To achieve `mutable XOR global` invariant, all globally visible state (currently,
|
||||
`object` singletons and enums) are automatically frozen. If object freezing
|
||||
is not desired, a `kotlin.native.ThreadLocal` annotation can be used, which will make
|
||||
the object state thread local, and so, mutable (but the changed state is not visible to
|
||||
other threads).
|
||||
|
||||
Top level/global variables of non-primitive types are by default accessible in the
|
||||
main thread (i.e., the thread which initialized _Kotlin/Native_ runtime first) only.
|
||||
Access from another thread will lead to an `IncorrectDereferenceException` being thrown.
|
||||
To make such variables accessible in other threads, you can use either the `@ThreadLocal` annotation,
|
||||
and mark the value thread local or `@SharedImmutable`, which will make the value frozen and accessible
|
||||
from other threads.
|
||||
|
||||
Class `AtomicReference` can be used to publish the changed frozen state to
|
||||
other threads, and so build patterns like shared caches.
|
||||
|
||||
The content of this page is moved to https://kotlinlang.org/docs/native-immutability.html
|
||||
+1
-720
@@ -1,722 +1,3 @@
|
||||
# _Kotlin/Native_ interoperability #
|
||||
|
||||
## Introduction ##
|
||||
|
||||
_Kotlin/Native_ follows the general tradition of Kotlin to provide excellent
|
||||
existing platform software interoperability. In the case of a native platform,
|
||||
the most important interoperability target is a C library. So _Kotlin/Native_
|
||||
comes with a `cinterop` tool, which can be used to quickly generate
|
||||
everything needed to interact with an external library.
|
||||
|
||||
The following workflow is expected when interacting with the native library.
|
||||
* create a `.def` file describing what to include into bindings
|
||||
* use the `cinterop` tool to produce Kotlin bindings
|
||||
* run _Kotlin/Native_ compiler on an application to produce the final executable
|
||||
|
||||
The interoperability tool analyses C headers and produces a "natural" mapping of
|
||||
the types, functions, and constants into the Kotlin world. The generated stubs can be
|
||||
imported into an IDE for the purpose of code completion and navigation.
|
||||
|
||||
Interoperability with Swift/Objective-C is provided too and covered in a
|
||||
separate document [OBJC_INTEROP.md](OBJC_INTEROP.md).
|
||||
|
||||
## Platform libraries ##
|
||||
|
||||
Note that in many cases there's no need to use custom interoperability library creation mechanisms described below,
|
||||
as for APIs available on the platform standardized bindings called [platform libraries](PLATFORM_LIBS.md)
|
||||
could be used. For example, POSIX on Linux/macOS platforms, Win32 on Windows platform, or Apple frameworks
|
||||
on macOS/iOS are available this way.
|
||||
|
||||
## Simple example ##
|
||||
|
||||
Install libgit2 and prepare stubs for the git library:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="shell">
|
||||
|
||||
```bash
|
||||
|
||||
cd samples/gitchurn
|
||||
../../dist/bin/cinterop -def src/nativeInterop/cinterop/libgit2.def \
|
||||
-compiler-option -I/usr/local/include -o libgit2
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Compile the client:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="shell">
|
||||
|
||||
```bash
|
||||
../../dist/bin/kotlinc src/gitChurnMain/kotlin \
|
||||
-library libgit2 -o GitChurn
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Run the client:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="shell">
|
||||
|
||||
```bash
|
||||
./GitChurn.kexe ../..
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
## Creating bindings for a new library ##
|
||||
|
||||
To create bindings for a new library, start by creating a `.def` file.
|
||||
Structurally it's a simple property file, which looks like this:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="c">
|
||||
|
||||
```c
|
||||
headers = png.h
|
||||
headerFilter = png.h
|
||||
package = png
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
Then run the `cinterop` tool with something like this (note that for host libraries that are not included
|
||||
in the sysroot search paths, headers may be needed):
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="shell">
|
||||
|
||||
```bash
|
||||
cinterop -def png.def -compiler-option -I/usr/local/include -o png
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
This command will produce a `png.klib` compiled library and
|
||||
`png-build/kotlin` directory containing Kotlin source code for the library.
|
||||
|
||||
If the behavior for a certain platform needs to be modified, you can use a format like
|
||||
`compilerOpts.osx` or `compilerOpts.linux` to provide platform-specific values
|
||||
to the options.
|
||||
|
||||
Note, that the generated bindings are generally platform-specific, so if you are developing for
|
||||
multiple targets, the bindings need to be regenerated.
|
||||
|
||||
After the generation of bindings, they can be used by the IDE as a proxy view of the
|
||||
native library.
|
||||
|
||||
For a typical Unix library with a config script, the `compilerOpts` will likely contain
|
||||
the output of a config script with the `--cflags` flag (maybe without exact paths).
|
||||
|
||||
The output of a config script with `--libs` will be passed as a `-linkedArgs` `kotlinc`
|
||||
flag value (quoted) when compiling.
|
||||
|
||||
### Selecting library headers
|
||||
|
||||
When library headers are imported to a C program with the `#include` directive,
|
||||
all of the headers included by these headers are also included in the program.
|
||||
So all header dependencies are included in generated stubs as well.
|
||||
|
||||
This behavior is correct but it can be very inconvenient for some libraries. So
|
||||
it is possible to specify in the `.def` file which of the included headers are to
|
||||
be imported. The separate declarations from other headers can also be imported
|
||||
in case of direct dependencies.
|
||||
|
||||
#### Filtering headers by globs
|
||||
|
||||
It is possible to filter headers by globs. The `headerFilter` property value
|
||||
from the `.def` file is treated as a space-separated list of globs. If the
|
||||
included header matches any of the globs, then the declarations from this header
|
||||
are included into the bindings.
|
||||
|
||||
The globs are applied to the header paths relative to the appropriate include
|
||||
path elements, e.g. `time.h` or `curl/curl.h`. So if the library is usually
|
||||
included with `#include <SomeLibrary/Header.h>`, then it would probably be
|
||||
correct to filter headers with
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="c">
|
||||
|
||||
```c
|
||||
headerFilter = SomeLibrary/**
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
If a `headerFilter` is not specified, then all headers are included.
|
||||
|
||||
#### Filtering by module maps
|
||||
|
||||
Some libraries have proper `module.modulemap` or `module.map` files in its
|
||||
headers. For example, macOS and iOS system libraries and frameworks do.
|
||||
The [module map file](https://clang.llvm.org/docs/Modules.html#module-map-language)
|
||||
describes the correspondence between header files and modules. When the module
|
||||
maps are available, the headers from the modules that are not included directly
|
||||
can be filtered out using the experimental `excludeDependentModules` option of the
|
||||
`.def` file:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="c">
|
||||
|
||||
```c
|
||||
headers = OpenGL/gl.h OpenGL/glu.h GLUT/glut.h
|
||||
compilerOpts = -framework OpenGL -framework GLUT
|
||||
excludeDependentModules = true
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
When both `excludeDependentModules` and `headerFilter` are used, they are
|
||||
applied as an intersection.
|
||||
|
||||
### C compiler and linker options ###
|
||||
|
||||
Options passed to the C compiler (used to analyze headers, such as preprocessor definitions) and the linker
|
||||
(used to link final executables) can be passed in the definition file as `compilerOpts` and `linkerOpts`
|
||||
respectively. For example
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="c">
|
||||
|
||||
```c
|
||||
compilerOpts = -DFOO=bar
|
||||
linkerOpts = -lpng
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Target-specific options, only applicable to the certain target can be specified as well, such as
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="c">
|
||||
|
||||
```c
|
||||
compilerOpts = -DBAR=bar
|
||||
compilerOpts.linux_x64 = -DFOO=foo1
|
||||
compilerOpts.mac_x64 = -DFOO=foo2
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
and so, C headers on Linux will be analyzed with `-DBAR=bar -DFOO=foo1` and on macOS with `-DBAR=bar -DFOO=foo2`.
|
||||
Note that any definition file option can have both common and the platform-specific part.
|
||||
|
||||
### Adding custom declarations ###
|
||||
|
||||
Sometimes it is required to add custom C declarations to the library before
|
||||
generating bindings (e.g., for [macros](#macros)). Instead of creating an
|
||||
additional header file with these declarations, you can include them directly
|
||||
to the end of the `.def` file, after a separating line, containing only the
|
||||
separator sequence `---`:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="c">
|
||||
|
||||
```c
|
||||
headers = errno.h
|
||||
|
||||
---
|
||||
|
||||
static inline int getErrno() {
|
||||
return errno;
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Note that this part of the `.def` file is treated as part of the header file, so
|
||||
functions with the body should be declared as `static`.
|
||||
The declarations are parsed after including the files from the `headers` list.
|
||||
|
||||
### Including static library in your klib
|
||||
|
||||
Sometimes it is more convenient to ship a static library with your product,
|
||||
rather than assume it is available within the user's environment.
|
||||
To include a static library into `.klib` use `staticLibrary` and `libraryPaths`
|
||||
clauses. For example:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="c">
|
||||
|
||||
```c
|
||||
headers = foo.h
|
||||
staticLibraries = libfoo.a
|
||||
libraryPaths = /opt/local/lib /usr/local/opt/curl/lib
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
When given the above snippet the `cinterop` tool will search `libfoo.a` in
|
||||
`/opt/local/lib` and `/usr/local/opt/curl/lib`, and if it is found include the
|
||||
library binary into `klib`.
|
||||
|
||||
When using such `klib` in your program, the library is linked automatically.
|
||||
|
||||
## Using bindings ##
|
||||
|
||||
### Basic interop types ###
|
||||
|
||||
All the supported C types have corresponding representations in Kotlin:
|
||||
|
||||
* Signed, unsigned integral, and floating point types are mapped to their
|
||||
Kotlin counterpart with the same width.
|
||||
* Pointers and arrays are mapped to `CPointer<T>?`.
|
||||
* Enums can be mapped to either Kotlin enum or integral values, depending on
|
||||
heuristics and the [definition file hints](#definition-file-hints).
|
||||
* Structs / unions are mapped to types having fields available via the dot notation,
|
||||
i.e. `someStructInstance.field1`.
|
||||
* `typedef` are represented as `typealias`.
|
||||
|
||||
Also, any C type has the Kotlin type representing the lvalue of this type,
|
||||
i.e., the value located in memory rather than a simple immutable self-contained
|
||||
value. Think C++ references, as a similar concept.
|
||||
For structs (and `typedef`s to structs) this representation is the main one
|
||||
and has the same name as the struct itself, for Kotlin enums it is named
|
||||
`${type}Var`, for `CPointer<T>` it is `CPointerVar<T>`, and for most other
|
||||
types it is `${type}Var`.
|
||||
|
||||
For types that have both representations, the one with a "lvalue" has a mutable
|
||||
`.value` property for accessing the value.
|
||||
|
||||
#### Pointer types ####
|
||||
|
||||
The type argument `T` of `CPointer<T>` must be one of the "lvalue" types
|
||||
described above, e.g., the C type `struct S*` is mapped to `CPointer<S>`,
|
||||
`int8_t*` is mapped to `CPointer<int_8tVar>`, and `char**` is mapped to
|
||||
`CPointer<CPointerVar<ByteVar>>`.
|
||||
|
||||
C null pointer is represented as Kotlin's `null`, and the pointer type
|
||||
`CPointer<T>` is not nullable, but the `CPointer<T>?` is. The values of this
|
||||
type support all the Kotlin operations related to handling `null`, e.g. `?:`, `?.`,
|
||||
`!!` etc.:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
val path = getenv("PATH")?.toKString() ?: ""
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Since the arrays are also mapped to `CPointer<T>`, it supports the `[]` operator
|
||||
for accessing values by index:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
fun shift(ptr: CPointer<BytePtr>, length: Int) {
|
||||
for (index in 0 .. length - 2) {
|
||||
ptr[index] = ptr[index + 1]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
The `.pointed` property for `CPointer<T>` returns the lvalue of type `T`,
|
||||
pointed by this pointer. The reverse operation is `.ptr`: it takes the lvalue
|
||||
and returns the pointer to it.
|
||||
|
||||
`void*` is mapped to `COpaquePointer` – the special pointer type which is the
|
||||
supertype for any other pointer type. So if the C function takes `void*`, then
|
||||
the Kotlin binding accepts any `CPointer`.
|
||||
|
||||
Casting a pointer (including `COpaquePointer`) can be done with
|
||||
`.reinterpret<T>`, e.g.:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
val intPtr = bytePtr.reinterpret<IntVar>()
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
or
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
val intPtr: CPointer<IntVar> = bytePtr.reinterpret()
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
As is with C, these reinterpret casts are unsafe and can potentially lead to
|
||||
subtle memory problems in the application.
|
||||
|
||||
Also there are unsafe casts between `CPointer<T>?` and `Long` available,
|
||||
provided by the `.toLong()` and `.toCPointer<T>()` extension methods:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
val longValue = ptr.toLong()
|
||||
val originalPtr = longValue.toCPointer<T>()
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Note that if the type of the result is known from the context, the type argument
|
||||
can be omitted as usual due to the type inference.
|
||||
|
||||
### Memory allocation ###
|
||||
|
||||
The native memory can be allocated using the `NativePlacement` interface, e.g.
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
val byteVar = placement.alloc<ByteVar>()
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
or
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
val bytePtr = placement.allocArray<ByteVar>(5)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
The most "natural" placement is in the object `nativeHeap`.
|
||||
It corresponds to allocating native memory with `malloc` and provides an additional
|
||||
`.free()` operation to free allocated memory:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
val buffer = nativeHeap.allocArray<ByteVar>(size)
|
||||
<use buffer>
|
||||
nativeHeap.free(buffer)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
However, the lifetime of allocated memory is often bound to the lexical scope.
|
||||
It is possible to define such scope with `memScoped { ... }`.
|
||||
Inside the braces, the temporary placement is available as an implicit receiver,
|
||||
so it is possible to allocate native memory with `alloc` and `allocArray`,
|
||||
and the allocated memory will be automatically freed after leaving the scope.
|
||||
|
||||
For example, the C function returning values through pointer parameters can be
|
||||
used like
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
val fileSize = memScoped {
|
||||
val statBuf = alloc<stat>()
|
||||
val error = stat("/", statBuf.ptr)
|
||||
statBuf.st_size
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
### Passing pointers to bindings ###
|
||||
|
||||
Although C pointers are mapped to the `CPointer<T>` type, the C function
|
||||
pointer-typed parameters are mapped to `CValuesRef<T>`. When passing
|
||||
`CPointer<T>` as the value of such a parameter, it is passed to the C function as is.
|
||||
However, the sequence of values can be passed instead of a pointer. In this case
|
||||
the sequence is passed "by value", i.e., the C function receives the pointer to
|
||||
the temporary copy of that sequence, which is valid only until the function returns.
|
||||
|
||||
The `CValuesRef<T>` representation of pointer parameters is designed to support
|
||||
C array literals without explicit native memory allocation.
|
||||
To construct the immutable self-contained sequence of C values, the following
|
||||
methods are provided:
|
||||
|
||||
* `${type}Array.toCValues()`, where `type` is the Kotlin primitive type
|
||||
* `Array<CPointer<T>?>.toCValues()`, `List<CPointer<T>?>.toCValues()`
|
||||
* `cValuesOf(vararg elements: ${type})`, where `type` is a primitive or pointer
|
||||
|
||||
For example:
|
||||
|
||||
C:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="c">
|
||||
|
||||
```c
|
||||
void foo(int* elements, int count);
|
||||
...
|
||||
int elements[] = {1, 2, 3};
|
||||
foo(elements, 3);
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Kotlin:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
foo(cValuesOf(1, 2, 3), 3)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
### Working with the strings ###
|
||||
|
||||
Unlike other pointers, the parameters of type `const char*` are represented as
|
||||
a Kotlin `String`. So it is possible to pass any Kotlin string to a binding
|
||||
expecting a C string.
|
||||
|
||||
There are also some tools available to convert between Kotlin and C strings
|
||||
manually:
|
||||
|
||||
* `fun CPointer<ByteVar>.toKString(): String`
|
||||
* `val String.cstr: CValuesRef<ByteVar>`.
|
||||
|
||||
To get the pointer, `.cstr` should be allocated in native memory, e.g.
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```
|
||||
val cString = kotlinString.cstr.getPointer(nativeHeap)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
In all cases, the C string is supposed to be encoded as UTF-8.
|
||||
|
||||
To skip automatic conversion and ensure raw pointers are used in the bindings, a `noStringConversion`
|
||||
statement in the `.def` file could be used, i.e.
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="c">
|
||||
|
||||
```c
|
||||
noStringConversion = LoadCursorA LoadCursorW
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
This way any value of type `CPointer<ByteVar>` can be passed as an argument of `const char*` type.
|
||||
If a Kotlin string should be passed, code like this could be used:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
memScoped {
|
||||
LoadCursorA(null, "cursor.bmp".cstr.ptr) // for ASCII version
|
||||
LoadCursorW(null, "cursor.bmp".wcstr.ptr) // for Unicode version
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
### Scope-local pointers ###
|
||||
|
||||
It is possible to create a scope-stable pointer of C representation of `CValues<T>`
|
||||
instance using the `CValues<T>.ptr` extension property, available under `memScoped { ... }`.
|
||||
It allows using the APIs which require C pointers with a lifetime bound to a certain `MemScope`. For example:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
memScoped {
|
||||
items = arrayOfNulls<CPointer<ITEM>?>(6)
|
||||
arrayOf("one", "two").forEachIndexed { index, value -> items[index] = value.cstr.ptr }
|
||||
menu = new_menu("Menu".cstr.ptr, items.toCValues().ptr)
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
In this example, all values passed to the C API `new_menu()` have a lifetime of the innermost `memScope`
|
||||
it belongs to. Once the control flow leaves the `memScoped` scope the C pointers become invalid.
|
||||
|
||||
### Passing and receiving structs by value ###
|
||||
|
||||
When a C function takes or returns a struct / union `T` by value, the corresponding
|
||||
argument type or return type is represented as `CValue<T>`.
|
||||
|
||||
`CValue<T>` is an opaque type, so the structure fields cannot be accessed with
|
||||
the appropriate Kotlin properties. It should be possible, if an API uses structures
|
||||
as handles, but if field access is required, there are the following conversion
|
||||
methods available:
|
||||
|
||||
* `fun T.readValue(): CValue<T>`. Converts (the lvalue) `T` to a `CValue<T>`.
|
||||
So to construct the `CValue<T>`, `T` can be allocated, filled, and then
|
||||
converted to `CValue<T>`.
|
||||
|
||||
* `CValue<T>.useContents(block: T.() -> R): R`. Temporarily places the
|
||||
`CValue<T>` to memory, and then runs the passed lambda with this placed
|
||||
value `T` as receiver. So to read a single field, the following code can be
|
||||
used:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
val fieldValue = structValue.useContents { field }
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
### Callbacks ###
|
||||
|
||||
To convert a Kotlin function to a pointer to a C function,
|
||||
`staticCFunction(::kotlinFunction)` can be used. It is also able to provide
|
||||
the lambda instead of a function reference. The function or lambda must not
|
||||
capture any values.
|
||||
|
||||
If the callback doesn't run in the main thread, it is mandatory to init the _Kotlin/Native_
|
||||
runtime by calling `kotlin.native.initRuntimeIfNeeded()`.
|
||||
|
||||
#### Passing user data to callbacks ####
|
||||
|
||||
Often C APIs allow passing some user data to callbacks. Such data is usually
|
||||
provided by the user when configuring the callback. It is passed to some C function
|
||||
(or written to the struct) as e.g. `void*`.
|
||||
However, references to Kotlin objects can't be directly passed to C.
|
||||
So they require wrapping before configuring the callback and then unwrapping in
|
||||
the callback itself, to safely swim from Kotlin to Kotlin through the C world.
|
||||
Such wrapping is possible with `StableRef` class.
|
||||
|
||||
To wrap the reference:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
val stableRef = StableRef.create(kotlinReference)
|
||||
val voidPtr = stableRef.asCPointer()
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
where the `voidPtr` is a `COpaquePointer` and can be passed to the C function.
|
||||
|
||||
To unwrap the reference:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
val stableRef = voidPtr.asStableRef<KotlinClass>()
|
||||
val kotlinReference = stableRef.get()
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
where `kotlinReference` is the original wrapped reference.
|
||||
|
||||
The created `StableRef` should eventually be manually disposed using
|
||||
the `.dispose()` method to prevent memory leaks:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
stableRef.dispose()
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
After that it becomes invalid, so `voidPtr` can't be unwrapped anymore.
|
||||
|
||||
See the `samples/libcurl` for more details.
|
||||
|
||||
### Macros ###
|
||||
|
||||
Every C macro that expands to a constant is represented as a Kotlin property.
|
||||
Other macros are not supported. However, they can be exposed manually by
|
||||
wrapping them with supported declarations. E.g. function-like macro `FOO` can be
|
||||
exposed as function `foo` by
|
||||
[adding the custom declaration](#adding-custom-declarations) to the library:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="c">
|
||||
|
||||
```c
|
||||
headers = library/base.h
|
||||
|
||||
---
|
||||
|
||||
static inline int foo(int arg) {
|
||||
return FOO(arg);
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
### Definition file hints ###
|
||||
|
||||
The `.def` file supports several options for adjusting the generated bindings.
|
||||
|
||||
* `excludedFunctions` property value specifies a space-separated list of the names
|
||||
of functions that should be ignored. This may be required because a function
|
||||
declared in the C header is not generally guaranteed to be really callable, and
|
||||
it is often hard or impossible to figure this out automatically. This option
|
||||
can also be used to workaround a bug in the interop itself.
|
||||
|
||||
* `strictEnums` and `nonStrictEnums` properties values are space-separated
|
||||
lists of the enums that should be generated as a Kotlin enum or as integral
|
||||
values correspondingly. If the enum is not included into any of these lists,
|
||||
then it is generated according to the heuristics.
|
||||
|
||||
* `noStringConversion` property value is space-separated lists of the functions whose
|
||||
`const char*` parameters shall not be autoconverted as Kotlin string
|
||||
|
||||
### Portability ###
|
||||
|
||||
Sometimes the C libraries have function parameters or struct fields of a
|
||||
platform-dependent type, e.g. `long` or `size_t`. Kotlin itself doesn't provide
|
||||
neither implicit integer casts nor C-style integer casts (e.g.
|
||||
`(size_t) intValue`), so to make writing portable code in such cases easier,
|
||||
the `convert` method is provided:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
fun ${type1}.convert<${type2}>(): ${type2}
|
||||
```
|
||||
</div>
|
||||
|
||||
where each of `type1` and `type2` must be an integral type, either signed or unsigned.
|
||||
|
||||
`.convert<${type}>` has the same semantics as one of the
|
||||
`.toByte`, `.toShort`, `.toInt`, `.toLong`,
|
||||
`.toUByte`, `.toUShort`, `.toUInt` or `.toULong`
|
||||
methods, depending on `type`.
|
||||
|
||||
The example of using `convert`:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
fun zeroMemory(buffer: COpaquePointer, size: Int) {
|
||||
memset(buffer, 0, size.convert<size_t>())
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Also, the type parameter can be inferred automatically and so may be omitted
|
||||
in some cases.
|
||||
|
||||
|
||||
### Object pinning ###
|
||||
|
||||
Kotlin objects could be pinned, i.e. their position in memory is guaranteed to be stable
|
||||
until unpinned, and pointers to such objects inner data could be passed to the C functions. For example
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
fun readData(fd: Int): String {
|
||||
val buffer = ByteArray(1024)
|
||||
buffer.usePinned { pinned ->
|
||||
while (true) {
|
||||
val length = recv(fd, pinned.addressOf(0), buffer.size.convert(), 0).toInt()
|
||||
|
||||
if (length <= 0) {
|
||||
break
|
||||
}
|
||||
// Now `buffer` has raw data obtained from the `recv()` call.
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Here we use service function `usePinned`, which pins an object, executes block and unpins it on normal and
|
||||
exception paths.
|
||||
The content of this page is moved to https://kotlinlang.org/docs/native-c-interop.html
|
||||
@@ -1,74 +1,3 @@
|
||||
# Symbolicating iOS crash reports
|
||||
|
||||
Debugging an iOS application crash sometimes involves analyzing crash reports.
|
||||
More info about crash reports can be found
|
||||
[in the official documentation](https://developer.apple.com/library/archive/technotes/tn2151/_index.html).
|
||||
|
||||
Crash reports generally require symbolication to become properly readable:
|
||||
symbolication turns machine code addresses into human-readable source locations.
|
||||
The document below describes some specific details of symbolicating crash reports
|
||||
from iOS applications using Kotlin.
|
||||
|
||||
## Producing .dSYM for release Kotlin binaries
|
||||
|
||||
To symbolicate addresses in Kotlin code (e.g. for stack trace elements
|
||||
corresponding to Kotlin code) `.dSYM` bundle for Kotlin code is required.
|
||||
|
||||
By default Kotlin/Native compiler produces `.dSYM` for release
|
||||
(i.e. optimized) binaries on Darwin platforms. This can be disabled with `-Xadd-light-debug=disable`
|
||||
compiler flag. At the same time this option is disabled by default for other platforms, to enable it use `-Xadd-light-debug=enable`.
|
||||
To control option in Gradle, use
|
||||
|
||||
```kotlin
|
||||
kotlin {
|
||||
targets.withType<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget> {
|
||||
binaries.all {
|
||||
freeCompilerArgs += "-Xadd-light-debug={enable|disable}"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
(in Kotlin DSL).
|
||||
|
||||
In projects created from IntelliJ IDEA or AppCode templates these `.dSYM` bundles
|
||||
are then discovered by Xcode automatically.
|
||||
|
||||
## Make frameworks static when using rebuild from bitcode
|
||||
|
||||
Rebuilding Kotlin-produced framework from bitcode invalidates the original `.dSYM`.
|
||||
If it is performed locally, make sure the updated `.dSYM` is used when symbolicating
|
||||
crash reports.
|
||||
|
||||
If rebuilding is performed on App Store side, then `.dSYM` of rebuilt *dynamic* framework
|
||||
seems discarded and not downloadable from App Store Connect.
|
||||
So in this case it may be required to make the framework static, e.g. with
|
||||
|
||||
```kotlin
|
||||
kotlin {
|
||||
targets.withType<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget> {
|
||||
binaries.withType<org.jetbrains.kotlin.gradle.plugin.mpp.Framework> {
|
||||
isStatic = true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
(in Kotlin DSL).
|
||||
|
||||
## Decode inlined stack frames
|
||||
|
||||
Xcode doesn't seem to properly decode stack trace elements of inlined function
|
||||
calls (these aren't only Kotlin `inline` functions but also functions that are
|
||||
inlined when optimizing machine code). So some stack trace elements may be
|
||||
missing. If this is the case, consider using `lldb` to process crash report
|
||||
that is already symbolicated by Xcode, for example:
|
||||
|
||||
```bash
|
||||
$ lldb -b -o "script import lldb.macosx" -o "crashlog file.crash"
|
||||
```
|
||||
|
||||
This command should output crash report that is additionally processed and
|
||||
includes inlined stack trace elements.
|
||||
|
||||
More details can be found in [LLDB documentation](https://lldb.llvm.org/use/symbolication.html).
|
||||
The content of this page is moved to https://kotlinlang.org/docs/native-ios-symbolication.html
|
||||
@@ -21,9 +21,22 @@ import java.io.File
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Paths
|
||||
|
||||
internal fun decodeFromUtf8(bytes: ByteArray) = String(bytes)
|
||||
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)
|
||||
|
||||
|
||||
@@ -507,18 +507,7 @@ public val String.utf32: CValues<IntVar>
|
||||
/**
|
||||
* @return the [kotlin.String] decoded from given zero-terminated UTF-8-encoded C string.
|
||||
*/
|
||||
public fun CPointer<ByteVar>.toKStringFromUtf8(): 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)
|
||||
}
|
||||
public fun CPointer<ByteVar>.toKStringFromUtf8(): String = this.toKStringFromUtf8Impl()
|
||||
|
||||
/**
|
||||
* @return the [kotlin.String] decoded from given zero-terminated UTF-8-encoded C string.
|
||||
|
||||
@@ -20,9 +20,11 @@ import kotlin.native.internal.Intrinsic
|
||||
import kotlin.native.internal.TypedIntrinsic
|
||||
import kotlin.native.internal.IntrinsicType
|
||||
|
||||
internal fun decodeFromUtf8(bytes: ByteArray): String = bytes.decodeToString()
|
||||
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
|
||||
|
||||
|
||||
+10
-2
@@ -40,6 +40,7 @@ import org.jetbrains.kotlin.library.resolver.impl.KotlinLibraryResolverImpl
|
||||
import org.jetbrains.kotlin.library.resolver.impl.libraryResolver
|
||||
import org.jetbrains.kotlin.library.toUnresolvedLibraries
|
||||
import org.jetbrains.kotlin.util.removeSuffixIfPresent
|
||||
import org.jetbrains.kotlin.util.suffixIfNot
|
||||
import java.io.File
|
||||
import java.lang.IllegalArgumentException
|
||||
import java.nio.file.*
|
||||
@@ -376,15 +377,22 @@ private fun processCLib(flavor: KotlinPlatform, cinteropArguments: CInteropArgum
|
||||
noDefaultLibs = true,
|
||||
noEndorsedLibs = true
|
||||
).getFullList()
|
||||
|
||||
val nopack = cinteropArguments.nopack
|
||||
val outputPath = cinteropArguments.output.let {
|
||||
val suffix = CompilerOutputKind.LIBRARY.suffix(tool.target)
|
||||
if (nopack) it.removeSuffixIfPresent(suffix) else it.suffixIfNot(suffix)
|
||||
}
|
||||
|
||||
createInteropLibrary(
|
||||
metadata = stubIrOutput.metadata,
|
||||
nativeBitcodeFiles = compiledFiles + nativeOutputPath,
|
||||
target = tool.target,
|
||||
moduleName = moduleName,
|
||||
outputPath = cinteropArguments.output,
|
||||
outputPath = outputPath,
|
||||
manifest = def.manifestAddendProperties,
|
||||
dependencies = stdlibDependency + imports.requiredLibraries.toList(),
|
||||
nopack = cinteropArguments.nopack,
|
||||
nopack = nopack,
|
||||
shortName = cinteropArguments.shortModuleName,
|
||||
staticLibraries = resolveLibraries(staticLibraries, libraryPaths)
|
||||
)
|
||||
|
||||
+1
-243
@@ -1,245 +1,3 @@
|
||||
# Kotlin/Native libraries
|
||||
|
||||
## Kotlin compiler specifics
|
||||
|
||||
To produce a library with the Kotlin/Native compiler use the `-produce library` or `-p library` flag. For example:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="shell">
|
||||
|
||||
```bash
|
||||
$ kotlinc foo.kt -p library -o bar
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
the above command will produce a `bar.klib` with the compiled contents of `foo.kt`.
|
||||
|
||||
To link to a library use the `-library <name>` or `-l <name>` flag. For example:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="shell">
|
||||
|
||||
```bash
|
||||
$ kotlinc qux.kt -l bar
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
the above command will produce a `program.kexe` out of `qux.kt` and `bar.klib`
|
||||
|
||||
|
||||
## cinterop tool specifics
|
||||
|
||||
The **cinterop** tool produces `.klib` wrappers for native libraries as its main output.
|
||||
For example, using the simple `libgit2.def` native library definition file provided in your Kotlin/Native distribution
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="shell">
|
||||
|
||||
```bash
|
||||
$ cinterop -def samples/gitchurn/src/nativeInterop/cinterop/libgit2.def -compiler-option -I/usr/local/include -o libgit2
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
we will obtain `libgit2.klib`.
|
||||
|
||||
See more details in [INTEROP.md](INTEROP.md)
|
||||
|
||||
|
||||
## klib utility
|
||||
|
||||
The **klib** library management utility allows you to inspect and install the libraries.
|
||||
|
||||
The following commands are available.
|
||||
|
||||
To list library contents:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="shell">
|
||||
|
||||
```bash
|
||||
$ klib contents <name>
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
To inspect the bookkeeping details of the library
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="shell">
|
||||
|
||||
```bash
|
||||
$ klib info <name>
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
To install the library to the default location use
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="shell">
|
||||
|
||||
```bash
|
||||
$ klib install <name>
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
To remove the library from the default repository use
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="shell">
|
||||
|
||||
```bash
|
||||
$ klib remove <name>
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
All of the above commands accept an additional `-repository <directory>` argument for specifying a repository different to the default one.
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="shell">
|
||||
|
||||
```bash
|
||||
$ klib <command> <name> -repository <directory>
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
## Several examples
|
||||
|
||||
First let's create a library.
|
||||
Place the tiny library source code into `kotlinizer.kt`:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="shell">
|
||||
|
||||
```kotlin
|
||||
package kotlinizer
|
||||
val String.kotlinized
|
||||
get() = "Kotlin $this"
|
||||
```
|
||||
|
||||
```bash
|
||||
$ kotlinc kotlinizer.kt -p library -o kotlinizer
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
The library has been created in the current directory:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="shell">
|
||||
|
||||
```bash
|
||||
$ ls kotlinizer.klib
|
||||
kotlinizer.klib
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Now let's check out the contents of the library:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="shell">
|
||||
|
||||
```bash
|
||||
$ klib contents kotlinizer
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
We can install `kotlinizer` to the default repository:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="shell">
|
||||
|
||||
```bash
|
||||
$ klib install kotlinizer
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Remove any traces of it from the current directory:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="shell">
|
||||
|
||||
```bash
|
||||
$ rm kotlinizer.klib
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Create a very short program and place it into a `use.kt` :
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
import kotlinizer.*
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
println("Hello, ${"world".kotlinized}!")
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Now compile the program linking with the library we have just created:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="shell">
|
||||
|
||||
```bash
|
||||
$ kotlinc use.kt -l kotlinizer -o kohello
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
And run the program:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="shell">
|
||||
|
||||
```bash
|
||||
$ ./kohello.kexe
|
||||
Hello, Kotlin world!
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Have fun!
|
||||
|
||||
# Advanced topics
|
||||
|
||||
## Library search sequence
|
||||
|
||||
When given a `-library foo` flag, the compiler searches the `foo` library in the following order:
|
||||
|
||||
* Current compilation directory or an absolute path.
|
||||
|
||||
* All repositories specified with `-repo` flag.
|
||||
|
||||
* Libraries installed in the default repository (For now the default is `~/.konan`, however it could be changed by setting **KONAN_DATA_DIR** environment variable).
|
||||
|
||||
* Libraries installed in `$installation/klib` directory.
|
||||
|
||||
## The library format
|
||||
|
||||
Kotlin/Native libraries are zip files containing a predefined
|
||||
directory structure, with the following layout:
|
||||
|
||||
**foo.klib** when unpacked as **foo/** gives us:
|
||||
|
||||
```yaml
|
||||
- foo/
|
||||
- $component_name/
|
||||
- ir/
|
||||
- Serialized Kotlin IR.
|
||||
- targets/
|
||||
- $platform/
|
||||
- kotlin/
|
||||
- Kotlin compiled to LLVM bitcode.
|
||||
- native/
|
||||
- Bitcode files of additional native objects.
|
||||
- $another_platform/
|
||||
- There can be several platform specific kotlin and native pairs.
|
||||
- linkdata/
|
||||
- A set of ProtoBuf files with serialized linkage metadata.
|
||||
- resources/
|
||||
- General resources such as images. (Not used yet).
|
||||
- manifest - A file in *java property* format describing the library.
|
||||
```
|
||||
|
||||
An example layout can be found in `klib/stdlib` directory of your installation.
|
||||
|
||||
The content of this page is moved to https://kotlinlang.org/docs/native-libraries.html
|
||||
@@ -1,426 +1,3 @@
|
||||
# _Kotlin/Native_ interoperability with Swift/Objective-C
|
||||
|
||||
This document covers some details of Kotlin/Native interoperability with
|
||||
Swift/Objective-C.
|
||||
|
||||
## Usage
|
||||
|
||||
Kotlin/Native provides bidirectional interoperability with Objective-C.
|
||||
Objective-C frameworks and libraries can be used in Kotlin code if
|
||||
properly imported to the build (system frameworks are imported by default).
|
||||
See e.g. "Using cinterop" in
|
||||
[Gradle plugin documentation](GRADLE_PLUGIN.md#using-cinterop).
|
||||
A Swift library can be used in Kotlin code if its API is exported to Objective-C
|
||||
with `@objc`. Pure Swift modules are not yet supported.
|
||||
|
||||
Kotlin modules can be used in Swift/Objective-C code if compiled into a
|
||||
framework (see "Targets and output kinds" section in [Gradle plugin documentation](GRADLE_PLUGIN.md#targets-and-output-kinds)).
|
||||
See [calculator sample](https://github.com/JetBrains/kotlin-native/tree/master/samples/calculator) for an example.
|
||||
|
||||
## Mappings
|
||||
|
||||
The table below shows how Kotlin concepts are mapped to Swift/Objective-C and vice versa.
|
||||
|
||||
"->" and "<-" indicate that mapping only goes one way.
|
||||
|
||||
| Kotlin | Swift | Objective-C | Notes |
|
||||
| ------ | ----- |------------ | ----- |
|
||||
| `class` | `class` | `@interface` | [note](#name-translation) |
|
||||
| `interface` | `protocol` | `@protocol` | |
|
||||
| `constructor`/`create` | Initializer | Initializer | [note](#initializers) |
|
||||
| Property | Property | Property | [note](#top-level-functions-and-properties) [note](#setters)|
|
||||
| Method | Method | Method | [note](#top-level-functions-and-properties) [note](#method-names-translation) |
|
||||
| `suspend` -> | `completionHandler:` | | [note](#errors-and-exceptions) |
|
||||
| `@Throws fun` | `throws` | `error:(NSError**)error` | [note](#errors-and-exceptions) |
|
||||
| Extension | Extension | Category member | [note](#extensions-and-category-members) |
|
||||
| `companion` member <- | Class method or property | Class method or property | |
|
||||
| `null` | `nil` | `nil` | |
|
||||
| `Singleton` | `Singleton()` | `[Singleton singleton]` | [note](#kotlin-singletons) |
|
||||
| Primitive type | Primitive type / `NSNumber` | | [note](#nsnumber) |
|
||||
| `Unit` return type | `Void` | `void` | |
|
||||
| `String` | `String` | `NSString` | |
|
||||
| `String` | `NSMutableString` | `NSMutableString` | [note](#nsmutablestring) |
|
||||
| `List` | `Array` | `NSArray` | |
|
||||
| `MutableList` | `NSMutableArray` | `NSMutableArray` | |
|
||||
| `Set` | `Set` | `NSSet` | |
|
||||
| `MutableSet` | `NSMutableSet` | `NSMutableSet` | [note](#collections) |
|
||||
| `Map` | `Dictionary` | `NSDictionary` | |
|
||||
| `MutableMap` | `NSMutableDictionary` | `NSMutableDictionary` | [note](#collections) |
|
||||
| Function type | Function type | Block pointer type | [note](#function-types) |
|
||||
| Inline classes | Unsupported| Unsupported| [note](#unsupported) |
|
||||
|
||||
|
||||
### Name translation
|
||||
|
||||
Objective-C classes are imported into Kotlin with their original names.
|
||||
Protocols are imported as interfaces with `Protocol` name suffix,
|
||||
i.e. `@protocol Foo` -> `interface FooProtocol`.
|
||||
These classes and interfaces are placed into a package [specified in build configuration](#usage)
|
||||
(`platform.*` packages for preconfigured system frameworks).
|
||||
|
||||
The names of Kotlin classes and interfaces are prefixed when imported to Objective-C.
|
||||
The prefix is derived from the framework name.
|
||||
|
||||
### Initializers
|
||||
|
||||
Swift/Objective-C initializers are imported to Kotlin as constructors and factory methods
|
||||
named `create`. The latter happens with initializers declared in the Objective-C category or
|
||||
as a Swift extension, because Kotlin has no concept of extension constructors.
|
||||
|
||||
Kotlin constructors are imported as initializers to Swift/Objective-C.
|
||||
|
||||
### Setters
|
||||
|
||||
Writeable Objective-C properties overriding read-only properties of the superclass are represented as `setFoo()` method for the property `foo`. Same goes for a protocol's read-only properties that are implemented as mutable.
|
||||
|
||||
### Top-level functions and properties
|
||||
|
||||
Top-level Kotlin functions and properties are accessible as members of special classes.
|
||||
Each Kotlin file is translated into such a class. E.g.
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
// MyLibraryUtils.kt
|
||||
package my.library
|
||||
|
||||
fun foo() {}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
can be called from Swift like
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="swift">
|
||||
|
||||
```swift
|
||||
MyLibraryUtilsKt.foo()
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
### Method names translation
|
||||
|
||||
Generally Swift argument labels and Objective-C selector pieces are mapped to Kotlin
|
||||
parameter names. Anyway these two concepts have different semantics, so sometimes
|
||||
Swift/Objective-C methods can be imported with a clashing Kotlin signature. In this case
|
||||
the clashing methods can be called from Kotlin using named arguments, e.g.:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="swift">
|
||||
|
||||
```swift
|
||||
[player moveTo:LEFT byMeters:17]
|
||||
[player moveTo:UP byInches:42]
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
in Kotlin it would be:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
player.moveTo(LEFT, byMeters = 17)
|
||||
player.moveTo(UP, byInches = 42)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
### Errors and exceptions
|
||||
|
||||
Kotlin has no concept of checked exceptions, all Kotlin exceptions are unchecked.
|
||||
Swift has only checked errors. So if Swift or Objective-C code calls a Kotlin method
|
||||
which throws an exception to be handled, then the Kotlin method should be marked
|
||||
with a `@Throws` annotation specifying a list of "expected" exception classes.
|
||||
|
||||
When compiling to Objective-C/Swift framework, non-`suspend` functions having or inheriting
|
||||
`@Throws` annotation are represented as `NSError*`-producing methods in Objective-C
|
||||
and as `throws` methods in Swift. Representations for `suspend` functions always have
|
||||
`NSError*`/`Error` parameter in completion handler.
|
||||
|
||||
When Kotlin function called from Swift/Objective-C code throws an exception
|
||||
which is an instance of one of the `@Throws`-specified classes or their subclasses,
|
||||
it is propagated as `NSError`. Other Kotlin exceptions reaching Swift/Objective-C
|
||||
are considered unhandled and cause program termination.
|
||||
|
||||
`suspend` functions without `@Throws` propagate only
|
||||
`CancellationException` as `NSError`. Non-`suspend` functions without `@Throws`
|
||||
don't propagate Kotlin exceptions at all.
|
||||
|
||||
Note that the opposite reversed translation is not implemented yet:
|
||||
Swift/Objective-C error-throwing methods aren't imported to Kotlin as
|
||||
exception-throwing.
|
||||
|
||||
### Extensions and category members
|
||||
|
||||
Members of Objective-C categories and Swift extensions are imported to Kotlin
|
||||
as extensions. That's why these declarations can't be overridden in Kotlin.
|
||||
And the extension initializers aren't available as Kotlin constructors.
|
||||
|
||||
Kotlin extensions to "regular" Kotlin classes are imported to Swift and Objective-C as extensions and category members respectively.
|
||||
Kotlin extensions to other types are treated as [top-level declarations](#top-level-functions-and-properties)
|
||||
with an additional receiver parameter. These types include:
|
||||
|
||||
* Kotlin `String` type
|
||||
* Kotlin collection types and subtypes
|
||||
* Kotlin `interface` types
|
||||
* Kotlin primitive types
|
||||
* Kotlin `inline` classes
|
||||
* Kotlin `Any` type
|
||||
* Kotlin function types and subtypes
|
||||
* Objective-C classes and protocols
|
||||
|
||||
### Kotlin singletons
|
||||
|
||||
Kotlin singleton (made with an `object` declaration, including `companion object`)
|
||||
is imported to Swift/Objective-C as a class with a single instance.
|
||||
The instance is available through the factory method, i.e. as
|
||||
`[MySingleton mySingleton]` in Objective-C and `MySingleton()` in Swift.
|
||||
|
||||
### NSNumber
|
||||
|
||||
Kotlin primitive type boxes are mapped to special Swift/Objective-C classes.
|
||||
For example, `kotlin.Int` box is represented as `KotlinInt` class instance in Swift
|
||||
(or `${prefix}Int` instance in Objective-C, where `prefix` is the framework names prefix).
|
||||
These classes are derived from `NSNumber`, so the instances are proper `NSNumber`s
|
||||
supporting all corresponding operations.
|
||||
|
||||
`NSNumber` type is not automatically translated to Kotlin primitive types
|
||||
when used as a Swift/Objective-C parameter type or return value.
|
||||
The reason is that `NSNumber` type doesn't provide enough information
|
||||
about a wrapped primitive value type, i.e. `NSNumber` is statically not known
|
||||
to be a e.g. `Byte`, `Boolean`, or `Double`. So Kotlin primitive values
|
||||
should be cast to/from `NSNumber` manually (see [below](#casting-between-mapped-types)).
|
||||
|
||||
### NSMutableString
|
||||
|
||||
`NSMutableString` Objective-C class is not available from Kotlin.
|
||||
All instances of `NSMutableString` are copied when passed to Kotlin.
|
||||
|
||||
### Collections
|
||||
|
||||
Kotlin collections are converted to Swift/Objective-C collections as described
|
||||
in the table above. Swift/Objective-C collections are mapped to Kotlin in the same way,
|
||||
except for `NSMutableSet` and `NSMutableDictionary`. `NSMutableSet` isn't converted to
|
||||
a Kotlin `MutableSet`. To pass an object for Kotlin `MutableSet`,
|
||||
you can create this kind of Kotlin collection explicitly by either creating it
|
||||
in Kotlin with e.g. `mutableSetOf()`, or using the `KotlinMutableSet` class in Swift
|
||||
(or `${prefix}MutableSet` in Objective-C, where `prefix` is the framework names prefix).
|
||||
The same holds for `MutableMap`.
|
||||
|
||||
### Function types
|
||||
|
||||
Kotlin function-typed objects (e.g. lambdas) are converted to
|
||||
Swift functions / Objective-C blocks. However there is a difference in how
|
||||
types of parameters and return values are mapped when translating a function
|
||||
and a function type. In the latter case primitive types are mapped to their
|
||||
boxed representation. Kotlin `Unit` return value is represented
|
||||
as a corresponding `Unit` singleton in Swift/Objective-C. The value of this singleton
|
||||
can be retrieved in the same way as it is for any other Kotlin `object`
|
||||
(see singletons in the table above).
|
||||
To sum the things up:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
fun foo(block: (Int) -> Unit) { ... }
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
would be represented in Swift as
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="swift">
|
||||
|
||||
```swift
|
||||
func foo(block: (KotlinInt) -> KotlinUnit)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
and can be called like
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
foo {
|
||||
bar($0 as! Int32)
|
||||
return KotlinUnit()
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
### Generics
|
||||
|
||||
Objective-C supports "lightweight generics" defined on classes, with a relatively limited feature set. Swift can import
|
||||
generics defined on classes to help provide additional type information to the compiler.
|
||||
|
||||
Generic feature support for Objective-C and Swift differ from Kotlin, so the translation will inevitably lose some information,
|
||||
but the features supported retain meaningful information.
|
||||
|
||||
#### Limitations
|
||||
|
||||
Objective-C generics do not support all features of either Kotlin or Swift, so there will be some information lost
|
||||
in the translation.
|
||||
|
||||
Generics can only be defined on classes, not on interfaces (protocols in Objective-C and Swift) or functions.
|
||||
|
||||
#### Nullability
|
||||
|
||||
Kotlin and Swift both define nullability as part of the type specification, while Objective-C defines nullability on methods
|
||||
and properties of a type. As such, the following:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
class Sample<T>() {
|
||||
fun myVal(): T
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
will (logically) look like this:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="swift">
|
||||
|
||||
```swift
|
||||
class Sample<T>() {
|
||||
fun myVal(): T?
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
In order to support a potentially nullable type, the Objective-C header needs to define `myVal` with a nullable return value.
|
||||
|
||||
To mitigate this, when defining your generic classes, if the generic type should *never* be null, provide a non-null
|
||||
type constraint:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
class Sample<T : Any>() {
|
||||
fun myVal(): T
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
That will force the Objective-C header to mark `myVal` as non-null.
|
||||
|
||||
#### Variance
|
||||
|
||||
Objective-C allows generics to be declared covariant or contravariant. Swift has no support for variance. Generic classes coming
|
||||
from Objective-C can be force-cast as needed.
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
data class SomeData(val num: Int = 42) : BaseData()
|
||||
class GenVarOut<out T : Any>(val arg: T)
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="swift">
|
||||
|
||||
```swift
|
||||
let variOut = GenVarOut<SomeData>(arg: sd)
|
||||
let variOutAny : GenVarOut<BaseData> = variOut as! GenVarOut<BaseData>
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
#### Constraints
|
||||
|
||||
In Kotlin you can provide upper bounds for a generic type. Objective-C also supports this, but that support is unavailable
|
||||
in more complex cases, and is currently not supported in the Kotlin - Objective-C interop. The exception here being a non-null
|
||||
upper bound will make Objective-C methods/properties non-null.
|
||||
|
||||
### To disable
|
||||
|
||||
To have the framework header written without generics, add the flag to the compiler config:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
binaries.framework {
|
||||
freeCompilerArgs += "-Xno-objc-generics"
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
## Casting between mapped types
|
||||
|
||||
When writing Kotlin code, an object may need to be converted from a Kotlin type
|
||||
to the equivalent Swift/Objective-C type (or vice versa). In this case a plain old
|
||||
Kotlin cast can be used, e.g.
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
val nsArray = listOf(1, 2, 3) as NSArray
|
||||
val string = nsString as String
|
||||
val nsNumber = 42 as NSNumber
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
## Subclassing
|
||||
|
||||
### Subclassing Kotlin classes and interfaces from Swift/Objective-C
|
||||
|
||||
Kotlin classes and interfaces can be subclassed by Swift/Objective-C classes
|
||||
and protocols.
|
||||
|
||||
### Subclassing Swift/Objective-C classes and protocols from Kotlin
|
||||
|
||||
Swift/Objective-C classes and protocols can be subclassed with a Kotlin `final` class.
|
||||
Non-`final` Kotlin classes inheriting Swift/Objective-C types aren't supported yet, so it is
|
||||
not possible to declare a complex class hierarchy inheriting Swift/Objective-C types.
|
||||
|
||||
Normal methods can be overridden using the `override` Kotlin keyword. In this case
|
||||
the overriding method must have the same parameter names as the overridden one.
|
||||
|
||||
Sometimes it is required to override initializers, e.g. when subclassing `UIViewController`.
|
||||
Initializers imported as Kotlin constructors can be overridden by Kotlin constructors
|
||||
marked with the `@OverrideInit` annotation:
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" mode="swift">
|
||||
|
||||
```swift
|
||||
class ViewController : UIViewController {
|
||||
@OverrideInit constructor(coder: NSCoder) : super(coder)
|
||||
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
The overriding constructor must have the same parameter names and types as the overridden one.
|
||||
|
||||
To override different methods with clashing Kotlin signatures, you can add a
|
||||
`@Suppress("CONFLICTING_OVERLOADS")` annotation to the class.
|
||||
|
||||
By default the Kotlin/Native compiler doesn't allow calling a non-designated
|
||||
Objective-C initializer as a `super(...)` constructor. This behaviour can be
|
||||
inconvenient if the designated initializers aren't marked properly in the Objective-C
|
||||
library. Adding a `disableDesignatedInitializerChecks = true` to the `.def` file for
|
||||
this library would disable these compiler checks.
|
||||
|
||||
## C features
|
||||
|
||||
See [INTEROP.md](INTEROP.md) for an example case where the library uses some plain C features
|
||||
(e.g. unsafe pointers, structs etc.).
|
||||
|
||||
## Unsupported
|
||||
|
||||
Some features of Kotlin programming language are not yet mapped into respective features of Objective-C or Swift.
|
||||
Currently, following features are not properly exposed in generated framework headers:
|
||||
* inline classes (arguments are mapped as either underlying primitive type or `id`)
|
||||
* custom classes implementing standard Kotlin collection interfaces (`List`, `Map`, `Set`) and other special classes
|
||||
* Kotlin subclasses of Objective-C classes
|
||||
The content of this page is moved to https://kotlinlang.org/docs/native-objc-interop.html
|
||||
@@ -1,61 +1,3 @@
|
||||
# Platform libraries
|
||||
|
||||
## Overview
|
||||
|
||||
To provide access to user's native operating system services,
|
||||
`Kotlin/Native` distribution includes a set of prebuilt libraries specific to
|
||||
each target. We call them **Platform Libraries**.
|
||||
|
||||
### POSIX bindings
|
||||
|
||||
For all `Unix` or `Windows` based targets (including `Android` and
|
||||
`iOS`) we provide the `posix` platform lib. It contains bindings
|
||||
to platform's implementation of `POSIX` standard.
|
||||
|
||||
To use the library just
|
||||
|
||||
<div class="sample" markdown="1" theme="idea" data-highlight-only>
|
||||
|
||||
```kotlin
|
||||
import platform.posix.*
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
The only target for which it is not available is [WebAssembly](https://en.wikipedia.org/wiki/WebAssembly).
|
||||
|
||||
Note that the content of `platform.posix` is NOT identical on
|
||||
different platforms, in the same way as different `POSIX` implementations
|
||||
are a little different.
|
||||
|
||||
|
||||
### Popular native libraries
|
||||
|
||||
There are many more platform libraries available for host and
|
||||
cross-compilation targets. `Kotlin/Native` distribution provides access to
|
||||
`OpenGL`, `zlib` and other popular native libraries on
|
||||
applicable platforms.
|
||||
|
||||
On Apple platforms `objc` library is provided for interoperability with [Objective-C](https://en.wikipedia.org/wiki/Objective-C).
|
||||
|
||||
Inspect the contents of `dist/klib/platform/$target` of the distribution for the details.
|
||||
|
||||
## Availability by default
|
||||
|
||||
The packages from platform libraries are available by default. No
|
||||
special link flags need to be specified to use them. `Kotlin/Native`
|
||||
compiler automatically detects which of the platform libraries have
|
||||
been accessed and automatically links the needed libraries.
|
||||
|
||||
On the other hand, the platform libs in the distribution are merely
|
||||
just wrappers and bindings to the native libraries. That means the
|
||||
native libraries themselves (`.so`, `.a`, `.dylib`, `.dll` etc)
|
||||
should be installed on the machine.
|
||||
|
||||
## Examples
|
||||
|
||||
`Kotlin/Native` installation provides a wide spectrum of examples
|
||||
demonstrating the use of platform libraries.
|
||||
See [samples](https://github.com/JetBrains/kotlin-native/tree/master/samples) for details.
|
||||
|
||||
|
||||
The content of this page is moved to https://kotlinlang.org/docs/native-platform-libs.html
|
||||
+5
-12
@@ -461,7 +461,7 @@ private class ExportedElement(val kind: ElementKind,
|
||||
"result", cfunction[0], Direction.KOTLIN_TO_C, builder)
|
||||
builder.append(" return $result;\n")
|
||||
}
|
||||
builder.append(" } catch (ExceptionObjHolder& e) { TerminateWithUnhandledException(e.obj()); } \n")
|
||||
builder.append(" } catch (ExceptionObjHolder& e) { TerminateWithUnhandledException(e.GetExceptionObject()); } \n")
|
||||
|
||||
builder.append("}\n")
|
||||
|
||||
@@ -905,7 +905,6 @@ internal class CAdapterGenerator(val context: Context) : DeclarationDescriptorVi
|
||||
|#define RUNTIME_NORETURN __attribute__((noreturn))
|
||||
|
|
||||
|extern "C" {
|
||||
|void UpdateHeapRef(KObjHeader**, const KObjHeader*) RUNTIME_NOTHROW;
|
||||
|void UpdateStackRef(KObjHeader**, const KObjHeader*) RUNTIME_NOTHROW;
|
||||
|KObjHeader* AllocInstance(const KTypeInfo*, KObjHeader**) RUNTIME_NOTHROW;
|
||||
|KObjHeader* DerefStablePointer(void*, KObjHeader**) RUNTIME_NOTHROW;
|
||||
@@ -951,16 +950,10 @@ internal class CAdapterGenerator(val context: Context) : DeclarationDescriptorVi
|
||||
|};
|
||||
|
|
||||
|class ExceptionObjHolder {
|
||||
| public:
|
||||
| explicit ExceptionObjHolder(const KObjHeader* obj): obj_(nullptr) {
|
||||
| ::UpdateHeapRef(&obj_, obj);
|
||||
| }
|
||||
| ~ExceptionObjHolder() {
|
||||
| UpdateHeapRef(&obj_, nullptr);
|
||||
| }
|
||||
| KObjHeader* obj() { return obj_; }
|
||||
| private:
|
||||
| KObjHeader* obj_;
|
||||
|public:
|
||||
| virtual ~ExceptionObjHolder() = default;
|
||||
|
|
||||
| KObjHeader* GetExceptionObject() noexcept;
|
||||
|};
|
||||
|
|
||||
|static void DisposeStablePointerImpl(${prefix}_KNativePtr ptr) {
|
||||
|
||||
-2
@@ -65,8 +65,6 @@ internal abstract class KonanBackendContext(val config: KonanConfig) : CommonBac
|
||||
|
||||
override val mapping: Mapping = DefaultMapping()
|
||||
|
||||
override val extractedLocalClasses: MutableSet<IrClass> = mutableSetOf()
|
||||
|
||||
override val irFactory: IrFactory = IrFactoryImpl
|
||||
}
|
||||
|
||||
|
||||
+37
-6
@@ -150,6 +150,41 @@ internal inline fun generateFunction(
|
||||
return function
|
||||
}
|
||||
|
||||
// TODO: Consider using different abstraction than `FunctionGenerationContext` for `generateFunctionNoRuntime`.
|
||||
internal inline fun <R> generateFunctionNoRuntime(
|
||||
codegen: CodeGenerator,
|
||||
function: LLVMValueRef,
|
||||
code: FunctionGenerationContext.(FunctionGenerationContext) -> R,
|
||||
) {
|
||||
val functionGenerationContext = FunctionGenerationContext(function, codegen, null, null)
|
||||
try {
|
||||
functionGenerationContext.forbidRuntime = true
|
||||
require(!functionGenerationContext.isObjectType(functionGenerationContext.returnType!!)) {
|
||||
"Cannot return object from function without Kotlin runtime"
|
||||
}
|
||||
|
||||
generateFunctionBody(functionGenerationContext, code)
|
||||
} finally {
|
||||
functionGenerationContext.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
internal inline fun generateFunctionNoRuntime(
|
||||
codegen: CodeGenerator,
|
||||
functionType: LLVMTypeRef,
|
||||
name: String,
|
||||
code: FunctionGenerationContext.(FunctionGenerationContext) -> Unit,
|
||||
): LLVMValueRef {
|
||||
val function = addLlvmFunctionWithDefaultAttributes(
|
||||
codegen.context,
|
||||
codegen.context.llvmModule!!,
|
||||
name,
|
||||
functionType
|
||||
)
|
||||
generateFunctionNoRuntime(codegen, function, code)
|
||||
return function
|
||||
}
|
||||
|
||||
private inline fun <R> generateFunctionBody(
|
||||
functionGenerationContext: FunctionGenerationContext,
|
||||
code: FunctionGenerationContext.(FunctionGenerationContext) -> R) {
|
||||
@@ -827,14 +862,10 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
|
||||
val beginCatch = context.llvm.cxaBeginCatchFunction
|
||||
val exceptionRawPtr = call(beginCatch, listOf(exceptionRecord))
|
||||
|
||||
// Pointer to KotlinException instance:
|
||||
val exceptionPtrPtr = bitcast(codegen.kObjHeaderPtrPtr, exceptionRawPtr, "")
|
||||
|
||||
// Pointer to Kotlin exception object:
|
||||
// We do need a slot here, as otherwise exception instance could be freed by _cxa_end_catch.
|
||||
val exceptionPtr = loadSlot(exceptionPtrPtr, true, "exception")
|
||||
val exceptionPtr = call(context.llvm.Kotlin_getExceptionObject, listOf(exceptionRawPtr), Lifetime.GLOBAL)
|
||||
|
||||
// __cxa_end_catch performs some C++ cleanup, including calling `KotlinException` class destructor.
|
||||
// __cxa_end_catch performs some C++ cleanup, including calling `ExceptionObjHolder` class destructor.
|
||||
val endCatch = context.llvm.cxaEndCatchFunction
|
||||
call(endCatch, listOf())
|
||||
|
||||
|
||||
+1
@@ -516,6 +516,7 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
|
||||
val checkLifetimesConstraint = importRtFunction("CheckLifetimesConstraint")
|
||||
val freezeSubgraph = importRtFunction("FreezeSubgraph")
|
||||
val checkGlobalsAccessible = importRtFunction("CheckGlobalsAccessible")
|
||||
val Kotlin_getExceptionObject = importRtFunction("Kotlin_getExceptionObject")
|
||||
|
||||
val kRefSharedHolderInitLocal = importRtFunction("KRefSharedHolder_initLocal")
|
||||
val kRefSharedHolderInit = importRtFunction("KRefSharedHolder_init")
|
||||
|
||||
+6
-22
@@ -505,18 +505,11 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
//-------------------------------------------------------------------------//
|
||||
|
||||
private fun createInitCtor(initNodePtr: LLVMValueRef): LLVMValueRef {
|
||||
val ctorFunction = addLlvmFunctionWithDefaultAttributes(
|
||||
context,
|
||||
context.llvmModule!!,
|
||||
"",
|
||||
kVoidFuncType
|
||||
)
|
||||
LLVMSetLinkage(ctorFunction, LLVMLinkage.LLVMPrivateLinkage)
|
||||
generateFunction(codegen, ctorFunction) {
|
||||
forbidRuntime = true
|
||||
val ctorFunction = generateFunctionNoRuntime(codegen, kVoidFuncType, "") {
|
||||
call(context.llvm.appendToInitalizersTail, listOf(initNodePtr))
|
||||
ret(null)
|
||||
}
|
||||
LLVMSetLinkage(ctorFunction, LLVMLinkage.LLVMPrivateLinkage)
|
||||
return ctorFunction
|
||||
}
|
||||
|
||||
@@ -2413,8 +2406,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
// When some dynamic caches are used, we consider that stdlib is in the dynamic cache as well.
|
||||
// Runtime is linked into stdlib module only, so import runtime global from it.
|
||||
val global = codegen.importGlobal(name, value.llvmType, context.standardLlvmSymbolsOrigin)
|
||||
val initializer = generateFunction(codegen, functionType(voidType, false), "") {
|
||||
forbidRuntime = true
|
||||
val initializer = generateFunctionNoRuntime(codegen, functionType(voidType, false), "") {
|
||||
store(value.llvm, global)
|
||||
ret(null)
|
||||
}
|
||||
@@ -2520,8 +2512,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
}
|
||||
|
||||
private fun appendStaticInitializers(ctorFunction: LLVMValueRef, initializers: List<LLVMValueRef>) {
|
||||
generateFunction(codegen, ctorFunction) {
|
||||
forbidRuntime = true
|
||||
generateFunctionNoRuntime(codegen, ctorFunction) {
|
||||
val initGuardName = ctorFunction.name.orEmpty() + "_guard"
|
||||
val initGuard = LLVMAddGlobal(context.llvmModule, int32Type, initGuardName)
|
||||
LLVMSetInitializer(initGuard, kImmZero)
|
||||
@@ -2553,21 +2544,14 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
private fun appendGlobalCtors(ctorFunctions: List<LLVMValueRef>) {
|
||||
if (context.config.produce.isFinalBinary) {
|
||||
// Generate function calling all [ctorFunctions].
|
||||
val globalCtorFunction = addLlvmFunctionWithDefaultAttributes(
|
||||
context,
|
||||
context.llvmModule!!,
|
||||
"_Konan_constructors",
|
||||
kVoidFuncType
|
||||
)
|
||||
LLVMSetLinkage(globalCtorFunction, LLVMLinkage.LLVMPrivateLinkage)
|
||||
generateFunction(codegen, globalCtorFunction) {
|
||||
forbidRuntime = true
|
||||
val globalCtorFunction = generateFunctionNoRuntime(codegen, kVoidFuncType, "_Konan_constructors") {
|
||||
ctorFunctions.forEach {
|
||||
call(it, emptyList(), Lifetime.IRRELEVANT,
|
||||
exceptionHandler = ExceptionHandler.Caller, verbatim = true)
|
||||
}
|
||||
ret(null)
|
||||
}
|
||||
LLVMSetLinkage(globalCtorFunction, LLVMLinkage.LLVMPrivateLinkage)
|
||||
|
||||
// Append initializers of global variables in "llvm.global_ctors" array.
|
||||
val globalCtors = context.llvm.staticData.placeGlobalArray("llvm.global_ctors", kCtorType,
|
||||
|
||||
+1
-1
@@ -142,7 +142,7 @@ internal class KotlinObjCClassInfoGenerator(override val context: Context) : Con
|
||||
val functionType = functionType(classDataPointer.llvmType, false, int8TypePtr, int8TypePtr)
|
||||
val functionName = "kobjcclassdataimp:${irClass.fqNameForIrSerialization}#internal"
|
||||
|
||||
val function = generateFunction(codegen, functionType, functionName) {
|
||||
val function = generateFunctionNoRuntime(codegen, functionType, functionName) {
|
||||
ret(classDataPointer.llvm)
|
||||
}.also {
|
||||
LLVMSetLinkage(it, LLVMLinkage.LLVMPrivateLinkage)
|
||||
|
||||
+2
-4
@@ -29,7 +29,6 @@ import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.konan.target.Family
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
import org.jetbrains.kotlin.konan.target.LinkerOutputKind
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
@@ -353,8 +352,7 @@ internal class ObjCExportCodeGenerator(
|
||||
private fun emitStaticInitializers() {
|
||||
if (externalGlobalInitializers.isEmpty()) return
|
||||
|
||||
val initializer = generateFunction(codegen, functionType(voidType, false), "initObjCExportGlobals") {
|
||||
forbidRuntime = true
|
||||
val initializer = generateFunctionNoRuntime(codegen, functionType(voidType, false), "initObjCExportGlobals") {
|
||||
externalGlobalInitializers.forEach { (global, value) ->
|
||||
store(value.llvm, global)
|
||||
}
|
||||
@@ -405,7 +403,7 @@ internal class ObjCExportCodeGenerator(
|
||||
|
||||
private fun emitSelectorsHolder() {
|
||||
val impType = functionType(voidType, false, int8TypePtr, int8TypePtr)
|
||||
val imp = generateFunction(codegen, impType, "") {
|
||||
val imp = generateFunctionNoRuntime(codegen, impType, "") {
|
||||
unreachable()
|
||||
}
|
||||
|
||||
|
||||
+19
-1
@@ -32,10 +32,13 @@ import org.jetbrains.kotlin.ir.symbols.impl.IrClassSymbolImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.IrConstructorSymbolImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.types.impl.buildSimpleType
|
||||
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
|
||||
internal class FunctionReferenceLowering(val context: Context): FileLoweringPass {
|
||||
|
||||
@@ -89,6 +92,21 @@ internal class FunctionReferenceLowering(val context: Context): FileLoweringPass
|
||||
return result
|
||||
}
|
||||
|
||||
// TODO: Move to common IR utils.
|
||||
fun IrType.eraseProjections(): IrType {
|
||||
if (this !is IrSimpleType) return this
|
||||
return buildSimpleType {
|
||||
this.classifier = this@eraseProjections.classifier
|
||||
this.hasQuestionMark = this@eraseProjections.hasQuestionMark
|
||||
this.annotations = this@eraseProjections.annotations
|
||||
this.arguments = this@eraseProjections.arguments.map {
|
||||
if (it !is IrTypeProjection)
|
||||
it
|
||||
else makeTypeProjection(it.type.eraseProjections(), Variance.INVARIANT)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle SAM conversions which wrap a function reference:
|
||||
// class sam$n(private val receiver: R) : Interface { override fun method(...) = receiver.target(...) }
|
||||
//
|
||||
@@ -111,7 +129,7 @@ internal class FunctionReferenceLowering(val context: Context): FileLoweringPass
|
||||
return super.visitTypeOperator(expression)
|
||||
}
|
||||
reference.transformChildrenVoid()
|
||||
return transformFunctionReference(reference, expression.typeOperand)
|
||||
return transformFunctionReference(reference, expression.typeOperand.eraseProjections())
|
||||
}
|
||||
return super.visitTypeOperator(expression)
|
||||
}
|
||||
|
||||
+1
-1
@@ -113,7 +113,7 @@ internal class KonanIrLinker(
|
||||
moduleDescriptor: ModuleDescriptor,
|
||||
klib: IrLibrary,
|
||||
strategy: DeserializationStrategy
|
||||
): KotlinIrLinker.BasicIrModuleDeserializer(moduleDescriptor, klib, strategy) {
|
||||
): BasicIrModuleDeserializer(this@KonanIrLinker, moduleDescriptor, klib, strategy){
|
||||
override val moduleFragment: IrModuleFragment = KonanIrModuleFragmentImpl(moduleDescriptor, builtIns, emptyList())
|
||||
}
|
||||
|
||||
|
||||
@@ -2531,12 +2531,30 @@ standaloneTest("kt-37572") {
|
||||
}
|
||||
|
||||
standaloneTest("custom_hook") {
|
||||
enabled = (project.testTarget != 'wasm32') // Uses exceptions.
|
||||
enabled = (project.testTarget != 'wasm32') && // Uses exceptions.
|
||||
!isExperimentalMM // Experimental MM does not support freezing yet.
|
||||
goldValue = "value 42: Error\n"
|
||||
expectedExitStatus = 1
|
||||
source = "runtime/exceptions/custom_hook.kt"
|
||||
}
|
||||
|
||||
standaloneTest("exception_in_global_init") {
|
||||
enabled = (project.testTarget != 'wasm32') // Uses exceptions.
|
||||
source = "runtime/exceptions/exception_in_global_init.kt"
|
||||
expectedExitStatusChecker = { it != 0 }
|
||||
outputChecker = { s -> s.contains("Uncaught Kotlin exception: kotlin.IllegalStateException: FAIL") && !s.contains("in kotlin main") }
|
||||
}
|
||||
|
||||
task rethrow_exception(type: KonanLocalTest) {
|
||||
enabled = (project.testTarget != 'wasm32') // Uses exceptions.
|
||||
source = "runtime/exceptions/rethrow.kt"
|
||||
}
|
||||
|
||||
task throw_from_catch(type: KonanLocalTest) {
|
||||
enabled = (project.testTarget != 'wasm32') // Uses exceptions.
|
||||
source = "runtime/exceptions/throw_from_catch.kt"
|
||||
}
|
||||
|
||||
standaloneTest("runtime_math_exceptions") {
|
||||
enabled = (project.testTarget != 'wasm32')
|
||||
source = "stdlib_external/numbers/MathExceptionTest.kt"
|
||||
@@ -2873,6 +2891,10 @@ task funInterface_implIsNotFunction(type: KonanLocalTest) {
|
||||
source = "codegen/funInterface/implIsNotFunction.kt"
|
||||
}
|
||||
|
||||
task funInterface_nonTrivialProjectionInSuperType(type: KonanLocalTest) {
|
||||
source = "codegen/funInterface/nonTrivialProjectionInSuperType.kt"
|
||||
}
|
||||
|
||||
task objectExpression1(type: KonanLocalTest) {
|
||||
goldValue = "aabb\n"
|
||||
source = "codegen/objectExpression/expr1.kt"
|
||||
@@ -3793,6 +3815,10 @@ createInterop("cunsupported") {
|
||||
it.defFile 'interop/basics/cunsupported.def'
|
||||
}
|
||||
|
||||
createInterop("ctoKString") {
|
||||
it.defFile 'interop/basics/ctoKString.def'
|
||||
}
|
||||
|
||||
createInterop("ctypes") {
|
||||
it.defFile 'interop/basics/ctypes.def'
|
||||
}
|
||||
@@ -4107,6 +4133,12 @@ interopTest("interop_unsupported") {
|
||||
interop = 'cunsupported'
|
||||
}
|
||||
|
||||
interopTest("interop_toKString") {
|
||||
disabled = (project.testTarget == 'wasm32') // No interop for wasm yet.
|
||||
source = "interop/basics/toKString.kt"
|
||||
interop = 'ctoKString'
|
||||
}
|
||||
|
||||
interopTest("interop_types") {
|
||||
disabled = (project.testTarget == 'wasm32') // No interop for wasm yet.
|
||||
source = "interop/basics/types.kt"
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2010-2021 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 codegen.funInterface.nonTrivialProjectionInSuperType
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
fun <T> foo(comparator: kotlin.Comparator<in T>, a: T, b: T) = comparator.compare(a, b)
|
||||
|
||||
fun bar(x: Int, y: Int) = foo<Int> ({ a, b -> a - b}, x, y)
|
||||
|
||||
@Test
|
||||
fun test() {
|
||||
assertTrue(bar(42, 117) < 0)
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
const char* empty() { return ""; }
|
||||
const char* foo() { return "foo"; }
|
||||
const char* kuku() { return "куку"; }
|
||||
const char* invalid_utf8() { return "\x85\xAF"; }
|
||||
const char* zero_in_the_middle() { return "before zero\0after zero"; }
|
||||
@@ -0,0 +1,12 @@
|
||||
import ctoKString.*
|
||||
import kotlinx.cinterop.*
|
||||
import kotlin.native.*
|
||||
import kotlin.test.*
|
||||
|
||||
fun main() {
|
||||
assertEquals("", empty()!!.toKStringFromUtf8())
|
||||
assertEquals("foo", foo()!!.toKStringFromUtf8())
|
||||
assertEquals("куку", kuku()!!.toKStringFromUtf8())
|
||||
assertEquals("\uFFFD\uFFFD", invalid_utf8()!!.toKStringFromUtf8())
|
||||
assertEquals("before zero", zero_in_the_middle()!!.toKStringFromUtf8())
|
||||
}
|
||||
@@ -6,31 +6,17 @@ import kotlin.test.*
|
||||
|
||||
import kotlin.native.concurrent.*
|
||||
|
||||
fun setHookLegacyMM(hook: ReportUnhandledExceptionHook) : ReportUnhandledExceptionHook? {
|
||||
fun main(args : Array<String>) {
|
||||
assertFailsWith<InvalidMutabilityException> {
|
||||
setUnhandledExceptionHook { _ -> println("wrong") }
|
||||
}
|
||||
|
||||
return setUnhandledExceptionHook(hook.freeze())
|
||||
}
|
||||
|
||||
fun setHookNewMM(hook: ReportUnhandledExceptionHook) : ReportUnhandledExceptionHook? {
|
||||
return setUnhandledExceptionHook(hook)
|
||||
}
|
||||
|
||||
fun setHook(hook: ReportUnhandledExceptionHook) : ReportUnhandledExceptionHook? {
|
||||
return when (kotlin.native.Platform.memoryModel) {
|
||||
kotlin.native.MemoryModel.EXPERIMENTAL -> setHookNewMM(hook)
|
||||
else -> setHookLegacyMM(hook)
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val x = 42
|
||||
val old = setHook {
|
||||
val old = setUnhandledExceptionHook({
|
||||
throwable: Throwable -> println("value $x: ${throwable::class.simpleName}")
|
||||
}
|
||||
}.freeze())
|
||||
|
||||
assertNull(old)
|
||||
|
||||
throw Error("an error")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Copyright 2010-2021 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.
|
||||
*/
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
val p: Nothing = error("FAIL")
|
||||
|
||||
fun main() {
|
||||
println("in kotlin main")
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2010-2021 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 runtime.exceptions.rethtow
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
@Test
|
||||
fun runTest() {
|
||||
assertFailsWith<IllegalStateException>("My error") {
|
||||
try {
|
||||
error("My error")
|
||||
} catch (e: Throwable) {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2010-2021 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 runtime.exceptions.throw_from_catch
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
@Test
|
||||
fun runTest() {
|
||||
assertFailsWith<IllegalStateException>("My another error") {
|
||||
try {
|
||||
error("My error")
|
||||
} catch (e: Throwable) {
|
||||
error("My another error")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -215,7 +215,10 @@ class RunExternalTestGroup extends JavaExec implements CompilerRunner {
|
||||
def languageSettings = findLinesWithPrefixesRemoved(text, "// !LANGUAGE: ")
|
||||
if (languageSettings.size() != 0) {
|
||||
languageSettings.forEach { line ->
|
||||
line.split(" ").toList().forEach { flags.add("-XXLanguage:$it") }
|
||||
line.split(" ").toList().forEach {
|
||||
if (it != "+NewInference") // It is on already by default, but passing it explicitly turns on a special "compatibility mode" in FE which is not desirable.
|
||||
flags.add("-XXLanguage:$it")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
@@ -103,6 +103,9 @@ open class GitDownloadTask @Inject constructor(
|
||||
|
||||
// Store info about used revision for the manual up-to-date check.
|
||||
upToDateChecker.storeRevisionInfo()
|
||||
|
||||
// Delete the .git directory of the cloned repo to avoid adding it to IDEA's VCS roots.
|
||||
outputDirectory.resolve(".git").deleteRecursively()
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -18,12 +18,12 @@
|
||||
buildKotlinVersion=1.5.20-dev-372
|
||||
buildKotlinCompilerRepo=https://teamcity.jetbrains.com/guestAuth/app/rest/builds/buildType:(id:Kotlin_KotlinPublic_Compiler),number:1.5.20-dev-372,branch:default:any,pinned:true/artifacts/content/maven
|
||||
remoteRoot=konan_tests
|
||||
kotlinCompilerRepo=https://teamcity.jetbrains.com/guestAuth/app/rest/builds/buildType:(id:Kotlin_KotlinPublic_Compiler),number:1.5.20-dev-576,branch:default:any,pinned:true/artifacts/content/maven
|
||||
kotlinVersion=1.5.20-dev-576
|
||||
kotlinStdlibRepo=https://teamcity.jetbrains.com/guestAuth/app/rest/builds/buildType:(id:Kotlin_KotlinPublic_Compiler),number:1.5.20-dev-576,branch:default:any,pinned:true/artifacts/content/maven
|
||||
kotlinStdlibVersion=1.5.20-dev-576
|
||||
kotlinStdlibTestsVersion=1.5.20-dev-576
|
||||
testKotlinCompilerVersion=1.5.20-dev-576
|
||||
kotlinCompilerRepo=https://teamcity.jetbrains.com/guestAuth/app/rest/builds/buildType:(id:Kotlin_KotlinPublic_Compiler),number:1.5.20-dev-1166,branch:default:any,pinned:true/artifacts/content/maven
|
||||
kotlinVersion=1.5.20-dev-1166
|
||||
kotlinStdlibRepo=https://teamcity.jetbrains.com/guestAuth/app/rest/builds/buildType:(id:Kotlin_KotlinPublic_Compiler),number:1.5.20-dev-1166,branch:default:any,pinned:true/artifacts/content/maven
|
||||
kotlinStdlibVersion=1.5.20-dev-1166
|
||||
kotlinStdlibTestsVersion=1.5.20-dev-1166
|
||||
testKotlinCompilerVersion=1.5.20-dev-1166
|
||||
konanVersion=1.5.20
|
||||
|
||||
# A version of Xcode required to build the Kotlin/Native compiler.
|
||||
|
||||
@@ -91,13 +91,14 @@ clangDebugFlags.macos_x64 = -O0
|
||||
linkerKonanFlags.macos_x64 = -lSystem -lc++ -lobjc -framework Foundation -sdk_version 10.15.6
|
||||
linkerOptimizationFlags.macos_x64 = -dead_strip
|
||||
linkerNoDebugFlags.macos_x64 = -S
|
||||
stripFlags.macos_x64 = -S
|
||||
linkerDynamicFlags.macos_x64 = -dylib
|
||||
|
||||
osVersionMinFlagLd.macos_x64 = -macosx_version_min
|
||||
osVersionMinFlagClang.macos_x64 = -mmacosx-version-min
|
||||
osVersionMin.macos_x64 = 10.11
|
||||
runtimeDefinitions.macos_x64 = KONAN_OSX=1 KONAN_MACOSX=1 KONAN_X64=1 KONAN_OBJC_INTEROP=1 \
|
||||
KONAN_CORE_SYMBOLICATION=1 KONAN_HAS_CXX11_EXCEPTION_FUNCTIONS=1
|
||||
KONAN_CORE_SYMBOLICATION=1
|
||||
dependencies.macos_x64 = \
|
||||
libffi-3.2.1-3-darwin-macos \
|
||||
lldb-3-macos
|
||||
@@ -127,13 +128,14 @@ clangDebugFlags.macos_arm64 = -O0 -mllvm -fast-isel=false -mllvm -global-isel=fa
|
||||
linkerKonanFlags.macos_arm64 = -lSystem -lc++ -lobjc -framework Foundation -sdk_version 11.0.1
|
||||
linkerOptimizationFlags.macos_arm64 = -dead_strip
|
||||
linkerNoDebugFlags.macos_arm64 = -S
|
||||
stripFlags.macos_arm64 = -S
|
||||
linkerDynamicFlags.macos_arm64 = -dylib
|
||||
|
||||
osVersionMinFlagLd.macos_arm64 = -macosx_version_min
|
||||
osVersionMinFlagClang.macos_arm64 = -mmacosx-version-min
|
||||
osVersionMin.macos_arm64 = 11.0
|
||||
runtimeDefinitions.macos_arm64 = KONAN_OSX=1 KONAN_MACOSX=1 KONAN_ARM64=1 KONAN_OBJC_INTEROP=1 \
|
||||
KONAN_CORE_SYMBOLICATION=1 KONAN_HAS_CXX11_EXCEPTION_FUNCTIONS=1
|
||||
KONAN_CORE_SYMBOLICATION=1
|
||||
dependencies.macos_x64-macos_arm64 = \
|
||||
libffi-3.2.1-3-darwin-macos
|
||||
|
||||
@@ -157,6 +159,7 @@ clangNooptFlags.ios_arm32 = -O1
|
||||
clangOptFlags.ios_arm32 = -O3
|
||||
clangDebugFlags.ios_arm32 = -O0
|
||||
linkerNoDebugFlags.ios_arm32 = -S
|
||||
stripFlags.ios_arm32 = -S
|
||||
linkerDynamicFlags.ios_arm32 = -dylib
|
||||
linkerKonanFlags.ios_arm32 = -lSystem -lc++ -lobjc -framework Foundation -sdk_version 14.0
|
||||
linkerOptimizationFlags.ios_arm32 = -dead_strip
|
||||
@@ -169,7 +172,7 @@ osVersionMin.ios_arm32 = 9.0
|
||||
# https://developer.apple.com/library/archive/documentation/Xcode/Conceptual/iPhoneOSABIReference/Articles/ARMv6FunctionCallingConventions.html#//apple_ref/doc/uid/TP40009021-SW1
|
||||
# See https://github.com/ktorio/ktor/issues/941 for the context.
|
||||
runtimeDefinitions.ios_arm32 = KONAN_OBJC_INTEROP=1 KONAN_IOS KONAN_ARM32=1 \
|
||||
KONAN_HAS_CXX11_EXCEPTION_FUNCTIONS=1 KONAN_REPORT_BACKTRACE_TO_IOS_CRASH_LOG=1 MACHSIZE=32 \
|
||||
KONAN_REPORT_BACKTRACE_TO_IOS_CRASH_LOG=1 MACHSIZE=32 \
|
||||
KONAN_NO_64BIT_ATOMIC=1 KONAN_NO_UNALIGNED_ACCESS=1
|
||||
|
||||
# Apple's 64-bit iOS.
|
||||
@@ -192,6 +195,7 @@ clangOptFlags.ios_arm64 = -O3
|
||||
clangDebugFlags.ios_arm64 = -O0 -mllvm -fast-isel=false -mllvm -global-isel=false
|
||||
|
||||
linkerNoDebugFlags.ios_arm64 = -S
|
||||
stripFlags.ios_arm64 = -S
|
||||
linkerDynamicFlags.ios_arm64 = -dylib
|
||||
linkerKonanFlags.ios_arm64 = -lSystem -lc++ -lobjc -framework Foundation -sdk_version 14.0
|
||||
linkerOptimizationFlags.ios_arm64 = -dead_strip
|
||||
@@ -199,7 +203,7 @@ osVersionMinFlagLd.ios_arm64 = -iphoneos_version_min
|
||||
osVersionMinFlagClang.ios_arm64 = -miphoneos-version-min
|
||||
osVersionMin.ios_arm64 = 9.0
|
||||
runtimeDefinitions.ios_arm64 = KONAN_OBJC_INTEROP=1 KONAN_IOS=1 KONAN_ARM64=1 \
|
||||
KONAN_HAS_CXX11_EXCEPTION_FUNCTIONS=1 KONAN_REPORT_BACKTRACE_TO_IOS_CRASH_LOG=1 MACHSIZE=64
|
||||
KONAN_REPORT_BACKTRACE_TO_IOS_CRASH_LOG=1 MACHSIZE=64
|
||||
additionalCacheFlags.ios_arm64 = -Xembed-bitcode-marker
|
||||
|
||||
# Apple's iOS simulator.
|
||||
@@ -221,12 +225,13 @@ clangDebugFlags.ios_x64 = -O0
|
||||
linkerKonanFlags.ios_x64 = -lSystem -lc++ -lobjc -framework Foundation -sdk_version 14.0
|
||||
linkerOptimizationFlags.ios_x64 = -dead_strip
|
||||
linkerNoDebugFlags.ios_x64 = -S
|
||||
stripFlags.ios_x64 = -S
|
||||
linkerDynamicFlags.ios_x64 = -dylib
|
||||
osVersionMinFlagLd.ios_x64 = -ios_simulator_version_min
|
||||
osVersionMinFlagClang.ios_x64 = -mios-simulator-version-min
|
||||
osVersionMin.ios_x64 = 9.0
|
||||
runtimeDefinitions.ios_x64 = KONAN_OBJC_INTEROP=1 KONAN_IOS=1 KONAN_X64=1 \
|
||||
KONAN_CORE_SYMBOLICATION=1 KONAN_HAS_CXX11_EXCEPTION_FUNCTIONS=1
|
||||
KONAN_CORE_SYMBOLICATION=1
|
||||
|
||||
# Apple's tvOS simulator.
|
||||
targetToolchain.macos_x64-tvos_x64 = target-toolchain-xcode_12_2-macos_x64
|
||||
@@ -247,12 +252,13 @@ clangDebugFlags.tvos_x64 = -O0
|
||||
linkerKonanFlags.tvos_x64 = -lSystem -lc++ -lobjc -framework Foundation -sdk_version 14.0
|
||||
linkerOptimizationFlags.tvos_x64 = -dead_strip
|
||||
linkerNoDebugFlags.tvos_x64 = -S
|
||||
stripFlags.tvos_x64 = -S
|
||||
linkerDynamicFlags.tvos_x64 = -dylib
|
||||
osVersionMinFlagLd.tvos_x64 = -tvos_simulator_version_min
|
||||
osVersionMinFlagClang.tvos_x64 = -mtvos-simulator-version-min
|
||||
osVersionMin.tvos_x64 = 9.0
|
||||
runtimeDefinitions.tvos_x64 = KONAN_OBJC_INTEROP=1 KONAN_TVOS=1 KONAN_X64=1 \
|
||||
KONAN_CORE_SYMBOLICATION=1 KONAN_HAS_CXX11_EXCEPTION_FUNCTIONS=1
|
||||
KONAN_CORE_SYMBOLICATION=1
|
||||
|
||||
# Apple's 64-bit tvOS.
|
||||
targetToolchain.macos_x64-tvos_arm64 = target-toolchain-xcode_12_2-macos_x64
|
||||
@@ -271,6 +277,7 @@ clangOptFlags.tvos_arm64 = -O3
|
||||
clangDebugFlags.tvos_arm64 = -O0 -mllvm -fast-isel=false -mllvm -global-isel=false
|
||||
|
||||
linkerNoDebugFlags.tvos_arm64 = -S
|
||||
stripFlags.tvos_arm64 = -S
|
||||
linkerDynamicFlags.tvos_arm64 = -dylib
|
||||
linkerKonanFlags.tvos_arm64 = -lSystem -lc++ -lobjc -framework Foundation -sdk_version 14.0
|
||||
linkerOptimizationFlags.tvos_arm64 = -dead_strip
|
||||
@@ -278,7 +285,7 @@ osVersionMinFlagLd.tvos_arm64 = -tvos_version_min
|
||||
osVersionMinFlagClang.tvos_arm64 = -mtvos-version-min
|
||||
osVersionMin.tvos_arm64 = 9.0
|
||||
runtimeDefinitions.tvos_arm64 = KONAN_OBJC_INTEROP=1 KONAN_TVOS=1 KONAN_ARM64=1 \
|
||||
KONAN_HAS_CXX11_EXCEPTION_FUNCTIONS=1 KONAN_REPORT_BACKTRACE_TO_IOS_CRASH_LOG=1 MACHSIZE=64
|
||||
KONAN_REPORT_BACKTRACE_TO_IOS_CRASH_LOG=1 MACHSIZE=64
|
||||
|
||||
# watchOS armv7k
|
||||
targetToolchain.macos_x64-watchos_arm32 = target-toolchain-xcode_12_2-macos_x64
|
||||
@@ -299,13 +306,14 @@ clangDebugFlags.watchos_arm32 = -O0
|
||||
linkerKonanFlags.watchos_arm32 = -lSystem -lc++ -lobjc -framework Foundation -sdk_version 7.0
|
||||
linkerOptimizationFlags.watchos_arm32 = -dead_strip
|
||||
linkerNoDebugFlags.watchos_arm32 = -S
|
||||
stripFlags.watchos_arm32 = -S
|
||||
linkerDynamicFlags.watchos_arm32 = -dylib
|
||||
osVersionMinFlagLd.watchos_arm32 = -watchos_version_min
|
||||
osVersionMinFlagClang.watchos_arm32 = -mwatchos-version-min
|
||||
osVersionMin.watchos_arm32 = 5.0
|
||||
# Regarding KONAN_NO_64BIT_ATOMIC=1: see explanation for ios_arm32 above.
|
||||
runtimeDefinitions.watchos_arm32 = KONAN_OBJC_INTEROP=1 KONAN_WATCHOS KONAN_ARM32=1 \
|
||||
KONAN_HAS_CXX11_EXCEPTION_FUNCTIONS=1 KONAN_REPORT_BACKTRACE_TO_IOS_CRASH_LOG=1 \
|
||||
KONAN_REPORT_BACKTRACE_TO_IOS_CRASH_LOG=1 \
|
||||
MACHSIZE=32 KONAN_NO_64BIT_ATOMIC=1 KONAN_NO_UNALIGNED_ACCESS=1
|
||||
|
||||
# watchOS arm64_32
|
||||
@@ -328,13 +336,14 @@ clangDebugFlags.watchos_arm64 = -O0
|
||||
linkerKonanFlags.watchos_arm64 = -lSystem -lc++ -lobjc -framework Foundation -sdk_version 7.0
|
||||
linkerOptimizationFlags.watchos_arm64 = -dead_strip
|
||||
linkerNoDebugFlags.watchos_arm64 = -S
|
||||
stripFlags.watchos_arm64 = -S
|
||||
linkerDynamicFlags.watchos_arm64 = -dylib
|
||||
osVersionMinFlagLd.watchos_arm64 = -watchos_version_min
|
||||
osVersionMinFlagClang.watchos_arm64 = -mwatchos-version-min
|
||||
osVersionMin.watchos_arm64 = 5.0
|
||||
# Regarding KONAN_NO_64BIT_ATOMIC=1: see explanation for ios_arm32 above.
|
||||
runtimeDefinitions.watchos_arm64 = KONAN_OBJC_INTEROP=1 KONAN_WATCHOS KONAN_ARM32=1 \
|
||||
KONAN_HAS_CXX11_EXCEPTION_FUNCTIONS=1 KONAN_REPORT_BACKTRACE_TO_IOS_CRASH_LOG=1 \
|
||||
KONAN_REPORT_BACKTRACE_TO_IOS_CRASH_LOG=1 \
|
||||
MACHSIZE=32 KONAN_NO_64BIT_ATOMIC=1 KONAN_NO_UNALIGNED_ACCESS=1
|
||||
|
||||
# Apple's watchOS i386 simulator.
|
||||
@@ -360,12 +369,13 @@ clangDebugFlags.watchos_x86 = -O0
|
||||
linkerKonanFlags.watchos_x86 = -lSystem -lc++ -lobjc -framework Foundation -sdk_version 7.0
|
||||
linkerOptimizationFlags.watchos_x86 = -dead_strip
|
||||
linkerNoDebugFlags.watchos_x86 = -S
|
||||
stripFlags.watchos_x86 = -S
|
||||
linkerDynamicFlags.watchos_x86 = -dylib
|
||||
osVersionMinFlagLd.watchos_x86 = -watchos_simulator_version_min
|
||||
osVersionMinFlagClang.watchos_x86 = -mwatchos-simulator-version-min
|
||||
osVersionMin.watchos_x86 = 5.0
|
||||
runtimeDefinitions.watchos_x86 = KONAN_OBJC_INTEROP=1 KONAN_WATCHOS=1 KONAN_NO_64BIT_ATOMIC=1 \
|
||||
KONAN_X86=1 KONAN_CORE_SYMBOLICATION=1 KONAN_HAS_CXX11_EXCEPTION_FUNCTIONS=1
|
||||
KONAN_X86=1 KONAN_CORE_SYMBOLICATION=1
|
||||
|
||||
# watchOS x86_64 simulator.
|
||||
targetToolchain.macos_x64-watchos_x64 = target-toolchain-xcode_12_2-macos_x64
|
||||
@@ -386,12 +396,13 @@ clangDebugFlags.watchos_x64 = -O0
|
||||
linkerKonanFlags.watchos_x64 = -lSystem -lc++ -lobjc -framework Foundation -sdk_version 7.0
|
||||
linkerOptimizationFlags.watchos_x64 = -dead_strip
|
||||
linkerNoDebugFlags.watchos_x64 = -S
|
||||
stripFlags.watchos_x64 = -S
|
||||
linkerDynamicFlags.watchos_x64 = -dylib
|
||||
osVersionMinFlagLd.watchos_x64 = -watchos_simulator_version_min
|
||||
osVersionMinFlagClang.watchos_x64 = -mwatchos-simulator-version-min
|
||||
osVersionMin.watchos_x64 = 7.0
|
||||
runtimeDefinitions.watchos_x64 = KONAN_OBJC_INTEROP=1 KONAN_WATCHOS=1 \
|
||||
KONAN_X64=1 KONAN_CORE_SYMBOLICATION=1 KONAN_HAS_CXX11_EXCEPTION_FUNCTIONS=1
|
||||
KONAN_X64=1 KONAN_CORE_SYMBOLICATION=1
|
||||
|
||||
# Linux x86-64.
|
||||
llvmHome.linux_x64 = $llvm.linux_x64.dev
|
||||
@@ -445,7 +456,7 @@ abiSpecificLibraries.linux_x64 = lib usr/lib ../lib64 lib64 usr/lib64
|
||||
# targetSysRoot relative
|
||||
crtFilesLocation.linux_x64 = usr/lib
|
||||
runtimeDefinitions.linux_x64 = USE_GCC_UNWIND=1 KONAN_LINUX=1 KONAN_X64=1 \
|
||||
USE_ELF_SYMBOLS=1 ELFSIZE=64 KONAN_HAS_CXX11_EXCEPTION_FUNCTIONS=1
|
||||
USE_ELF_SYMBOLS=1 ELFSIZE=64
|
||||
|
||||
# Raspberry Pi
|
||||
gccToolchain.linux_arm32_hfp = arm-unknown-linux-gnueabihf-gcc-8.3.0-glibc-2.19-kernel-4.9
|
||||
@@ -689,7 +700,7 @@ targetSysRoot.android_arm64 = target-sysroot-1-android_ndk
|
||||
linkerKonanFlags.android_arm64 = -lm -lc++_static -lc++abi -landroid -llog -latomic
|
||||
linkerNoDebugFlags.android_arm64 = -Wl,-S
|
||||
runtimeDefinitions.android_arm64 = __ANDROID__ USE_GCC_UNWIND=1 USE_ELF_SYMBOLS=1 \
|
||||
ELFSIZE=64 KONAN_ANDROID=1 KONAN_ARM64=1 KONAN_HAS_CXX11_EXCEPTION_FUNCTIONS=1
|
||||
ELFSIZE=64 KONAN_ANDROID=1 KONAN_ARM64=1
|
||||
|
||||
# Android X86, based on NDK.
|
||||
targetToolchain.macos_x64-android_x86 = target-toolchain-2-osx-android_ndk
|
||||
@@ -721,7 +732,7 @@ targetSysRoot.android_x86 = target-sysroot-1-android_ndk
|
||||
linkerKonanFlags.android_x86 = -lm -lc++_static -lc++abi -landroid -llog -latomic
|
||||
linkerNoDebugFlags.android_x86 = -Wl,-S
|
||||
runtimeDefinitions.android_x86 = __ANDROID__ USE_GCC_UNWIND=1 USE_ELF_SYMBOLS=1 \
|
||||
ELFSIZE=32 KONAN_ANDROID=1 KONAN_X86=1 KONAN_HAS_CXX11_EXCEPTION_FUNCTIONS=1
|
||||
ELFSIZE=32 KONAN_ANDROID=1 KONAN_X86=1
|
||||
|
||||
# Android X64, based on NDK.
|
||||
targetToolchain.macos_x64-android_x64 = target-toolchain-2-osx-android_ndk
|
||||
@@ -749,7 +760,7 @@ targetSysRoot.android_x64 = target-sysroot-1-android_ndk
|
||||
linkerKonanFlags.android_x64 = -lm -lc++_static -lc++abi -landroid -llog -latomic
|
||||
linkerNoDebugFlags.android_x64 = -Wl,-S
|
||||
runtimeDefinitions.android_x64 = __ANDROID__ USE_GCC_UNWIND=1 USE_ELF_SYMBOLS=1 \
|
||||
ELFSIZE=64 KONAN_ANDROID=1 KONAN_X64=1 KONAN_HAS_CXX11_EXCEPTION_FUNCTIONS=1
|
||||
ELFSIZE=64 KONAN_ANDROID=1 KONAN_X64=1
|
||||
|
||||
# Windows x86-64, based on mingw-w64.
|
||||
llvmHome.mingw_x64 = $llvm.mingw_x64.dev
|
||||
@@ -785,7 +796,7 @@ linkerKonanFlags.mingw_x64 =-static-libgcc -static-libstdc++ \
|
||||
linkerOptimizationFlags.mingw_x64 = -Wl,--gc-sections
|
||||
mimallocLinkerDependencies.mingw_x64 = -lbcrypt
|
||||
runtimeDefinitions.mingw_x64 = USE_GCC_UNWIND=1 USE_PE_COFF_SYMBOLS=1 KONAN_WINDOWS=1 \
|
||||
UNICODE KONAN_X64=1 KONAN_NO_MEMMEM=1 KONAN_HAS_CXX11_EXCEPTION_FUNCTIONS=1
|
||||
UNICODE KONAN_X64=1 KONAN_NO_MEMMEM=1
|
||||
|
||||
# Windows i686, based on mingw-w64.
|
||||
targetToolchain.mingw_x64-mingw_x86 = msys2-mingw-w64-i686-clang-llvm-lld-compiler_rt-8.0.1
|
||||
@@ -823,7 +834,7 @@ linkerKonanFlags.mingw_x86 = -static-libgcc -static-libstdc++ \
|
||||
mimallocLinkerDependencies.mingw_x86 = -lbcrypt
|
||||
linkerOptimizationFlags.mingw_x86 = -Wl,--gc-sections
|
||||
runtimeDefinitions.mingw_x86 = USE_GCC_UNWIND=1 USE_PE_COFF_SYMBOLS=1 KONAN_WINDOWS=1 \
|
||||
UNICODE KONAN_X86=1 KONAN_NO_MEMMEM=1 KONAN_HAS_CXX11_EXCEPTION_FUNCTIONS=1
|
||||
UNICODE KONAN_X86=1 KONAN_NO_MEMMEM=1
|
||||
|
||||
# WebAssembly 32-bit.
|
||||
targetToolchain.macos_x64-wasm32 = target-toolchain-3-macos-wasm
|
||||
@@ -858,4 +869,4 @@ runtimeDefinitions.wasm32 = KONAN_WASM=1 KONAN_NO_FFI=1 KONAN_NO_THREADS=1 \
|
||||
KONAN_INTERNAL_NOW=1 KONAN_NO_MEMMEM KONAN_NO_CTORS_SECTION=1
|
||||
|
||||
# The version of Kotlin/Native compiler
|
||||
compilerVersion=1.5-dev
|
||||
compilerVersion=1.5-dev
|
||||
|
||||
@@ -440,6 +440,20 @@ void setContainerFor(ObjHeader* obj, ContainerHeader* container) {
|
||||
obj->typeInfoOrMeta_ = setPointerBits(obj->typeInfoOrMeta_, OBJECT_TAG_NONTRIVIAL_CONTAINER);
|
||||
}
|
||||
|
||||
#if !KONAN_NO_EXCEPTIONS
|
||||
class ExceptionObjHolderImpl : public ExceptionObjHolder {
|
||||
public:
|
||||
explicit ExceptionObjHolderImpl(ObjHeader* obj) noexcept { ::SetHeapRef(&obj_, obj); }
|
||||
|
||||
~ExceptionObjHolderImpl() override { ZeroHeapRef(&obj_); }
|
||||
|
||||
ObjHeader* obj() noexcept { return obj_; }
|
||||
|
||||
private:
|
||||
ObjHeader* obj_;
|
||||
};
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
ContainerHeader* containerFor(const ObjHeader* obj) {
|
||||
@@ -3691,3 +3705,14 @@ ALWAYS_INLINE RUNTIME_NOTHROW void Kotlin_mm_safePointExceptionUnwind() {
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
#if !KONAN_NO_EXCEPTIONS
|
||||
// static
|
||||
ALWAYS_INLINE RUNTIME_NORETURN void ExceptionObjHolder::Throw(ObjHeader* exception) {
|
||||
throw ExceptionObjHolderImpl(exception);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE ObjHeader* ExceptionObjHolder::GetExceptionObject() noexcept {
|
||||
return static_cast<ExceptionObjHolderImpl*>(this)->obj();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -48,14 +48,6 @@
|
||||
|
||||
namespace {
|
||||
|
||||
// RuntimeUtils.kt
|
||||
extern "C" void ReportUnhandledException(KRef throwable);
|
||||
extern "C" void ExceptionReporterLaunchpad(KRef reporter, KRef throwable);
|
||||
|
||||
KRef currentUnhandledExceptionHook = nullptr;
|
||||
int32_t currentUnhandledExceptionHookLock = 0;
|
||||
int32_t currentUnhandledExceptionHookCookie = 0;
|
||||
|
||||
#if USE_GCC_UNWIND
|
||||
struct Backtrace {
|
||||
Backtrace(int count, int skip) : index(0), skipCount(skip) {
|
||||
@@ -216,27 +208,10 @@ void ThrowException(KRef exception) {
|
||||
PrintThrowable(exception);
|
||||
RuntimeCheck(false, "Exceptions unsupported");
|
||||
#else
|
||||
throw ExceptionObjHolder(exception);
|
||||
ExceptionObjHolder::Throw(exception);
|
||||
#endif
|
||||
}
|
||||
|
||||
OBJ_GETTER(Kotlin_setUnhandledExceptionHook, KRef hook) {
|
||||
RETURN_RESULT_OF(SwapHeapRefLocked,
|
||||
¤tUnhandledExceptionHook, currentUnhandledExceptionHook, hook, ¤tUnhandledExceptionHookLock,
|
||||
¤tUnhandledExceptionHookCookie);
|
||||
}
|
||||
|
||||
void OnUnhandledException(KRef throwable) {
|
||||
ObjHolder handlerHolder;
|
||||
auto* handler = SwapHeapRefLocked(¤tUnhandledExceptionHook, currentUnhandledExceptionHook, nullptr,
|
||||
¤tUnhandledExceptionHookLock, ¤tUnhandledExceptionHookCookie, handlerHolder.slot());
|
||||
if (handler == nullptr) {
|
||||
ReportUnhandledException(throwable);
|
||||
} else {
|
||||
ExceptionReporterLaunchpad(handler, throwable);
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class {
|
||||
@@ -277,9 +252,15 @@ RUNTIME_NORETURN void TerminateWithUnhandledException(KRef throwable) {
|
||||
});
|
||||
}
|
||||
|
||||
// Some libstdc++-based targets has limited support for std::current_exception and other C++11 functions.
|
||||
// This restriction can be lifted later when toolchains will be updated.
|
||||
#if KONAN_HAS_CXX11_EXCEPTION_FUNCTIONS
|
||||
ALWAYS_INLINE RUNTIME_NOTHROW OBJ_GETTER(Kotlin_getExceptionObject, void* holder) {
|
||||
#if !KONAN_NO_EXCEPTIONS
|
||||
RETURN_OBJ(static_cast<ExceptionObjHolder*>(holder)->GetExceptionObject());
|
||||
#else
|
||||
RETURN_OBJ(nullptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !KONAN_NO_EXCEPTIONS
|
||||
|
||||
namespace {
|
||||
// Copy, move and assign would be safe, but not much useful, so let's delete all (rule of 5)
|
||||
@@ -293,7 +274,7 @@ class TerminateHandler : private kotlin::Pinned {
|
||||
try {
|
||||
std::rethrow_exception(currentException);
|
||||
} catch (ExceptionObjHolder& e) {
|
||||
processUnhandledKotlinException(e.obj());
|
||||
processUnhandledKotlinException(e.GetExceptionObject());
|
||||
konan::abort();
|
||||
} catch (...) {
|
||||
// Not a Kotlin exception - call default handler
|
||||
@@ -333,13 +314,13 @@ void SetKonanTerminateHandler() {
|
||||
TerminateHandler::install();
|
||||
}
|
||||
|
||||
#else // KONAN_OBJC_INTEROP
|
||||
#else // !KONAN_NO_EXCEPTIONS
|
||||
|
||||
void SetKonanTerminateHandler() {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
#endif // KONAN_OBJC_INTEROP
|
||||
#endif // !KONAN_NO_EXCEPTIONS
|
||||
|
||||
void DisallowSourceInfo() {
|
||||
disallowSourceInfo = true;
|
||||
|
||||
@@ -28,17 +28,18 @@ OBJ_GETTER0(Kotlin_getCurrentStackTrace);
|
||||
|
||||
OBJ_GETTER(GetStackTraceStrings, KConstRef stackTrace);
|
||||
|
||||
OBJ_GETTER(Kotlin_setUnhandledExceptionHook, KRef hook);
|
||||
|
||||
// Throws arbitrary exception.
|
||||
void ThrowException(KRef exception);
|
||||
|
||||
// RuntimeUtils.kt
|
||||
void OnUnhandledException(KRef throwable);
|
||||
|
||||
RUNTIME_NORETURN void TerminateWithUnhandledException(KRef exception);
|
||||
|
||||
void SetKonanTerminateHandler();
|
||||
|
||||
RUNTIME_NOTHROW OBJ_GETTER(Kotlin_getExceptionObject, void* holder);
|
||||
|
||||
// The functions below are implemented in Kotlin (at package kotlin.native.internal).
|
||||
|
||||
// Throws null pointer exception. Context is evaluated from caller's address.
|
||||
|
||||
@@ -17,8 +17,10 @@
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "Alloc.h"
|
||||
#include "KString.h"
|
||||
#include "Memory.h"
|
||||
#include "MemorySharedRefs.hpp"
|
||||
#include "Types.h"
|
||||
@@ -42,4 +44,8 @@ OBJ_GETTER(Kotlin_Interop_derefStablePointer, KNativePtr pointer) {
|
||||
RETURN_OBJ(holder->ref<ErrorPolicy::kThrow>());
|
||||
}
|
||||
|
||||
OBJ_GETTER(Kotlin_CString_toKStringFromUtf8Impl, const char* cstring) {
|
||||
RETURN_RESULT_OF(StringFromUtf8Buffer, cstring, strlen(cstring));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -511,6 +511,13 @@ OBJ_GETTER(Kotlin_ByteArray_unsafeStringFromUtf8, KConstRef thiz, KInt start, KI
|
||||
RETURN_RESULT_OF(utf8ToUtf16, rawString, size);
|
||||
}
|
||||
|
||||
OBJ_GETTER(StringFromUtf8Buffer, const char* start, size_t size) {
|
||||
if (size == 0) {
|
||||
RETURN_RESULT_OF0(TheEmptyString);
|
||||
}
|
||||
RETURN_RESULT_OF(utf8ToUtf16, start, size);
|
||||
}
|
||||
|
||||
OBJ_GETTER(Kotlin_String_unsafeStringToUtf8, KString thiz, KInt start, KInt size) {
|
||||
RETURN_RESULT_OF(unsafeUtf16ToUtf8Impl<utf8::with_replacement::utf16to8>, thiz, start, size);
|
||||
}
|
||||
|
||||
@@ -31,6 +31,8 @@ OBJ_GETTER(CreateStringFromUtf8, const char* utf8, uint32_t lengthBytes);
|
||||
char* CreateCStringFromString(KConstRef kstring);
|
||||
void DisposeCString(char* cstring);
|
||||
|
||||
OBJ_GETTER(StringFromUtf8Buffer, const char* start, size_t size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "TypeInfo.h"
|
||||
#include "Atomic.h"
|
||||
#include "PointerBits.h"
|
||||
#include "Utils.hpp"
|
||||
|
||||
typedef enum {
|
||||
// Must match to permTag() in Kotlin.
|
||||
@@ -356,23 +357,16 @@ class ObjHolder {
|
||||
ObjHeader* obj_;
|
||||
};
|
||||
|
||||
//! TODO Follow the Rule of Zero to prevent dangling on unintented copy ctor
|
||||
class ExceptionObjHolder {
|
||||
public:
|
||||
explicit ExceptionObjHolder(const ObjHeader* obj) {
|
||||
::SetHeapRef(&obj_, obj);
|
||||
}
|
||||
public:
|
||||
#if !KONAN_NO_EXCEPTIONS
|
||||
static void Throw(ObjHeader* exception) RUNTIME_NORETURN;
|
||||
|
||||
~ExceptionObjHolder() {
|
||||
ZeroHeapRef(&obj_);
|
||||
}
|
||||
ObjHeader* GetExceptionObject() noexcept;
|
||||
#endif
|
||||
|
||||
ObjHeader* obj() { return obj_; }
|
||||
|
||||
const ObjHeader* obj() const { return obj_; }
|
||||
|
||||
private:
|
||||
ObjHeader* obj_;
|
||||
// Exceptions are not on a hot path, so having virtual dispatch is fine.
|
||||
virtual ~ExceptionObjHolder() = default;
|
||||
};
|
||||
|
||||
#endif // RUNTIME_MEMORY_H
|
||||
|
||||
@@ -144,6 +144,11 @@ public:
|
||||
deletionQueue_ = std::move(remainingDeletions);
|
||||
}
|
||||
|
||||
void ClearForTests() noexcept {
|
||||
queue_.clear();
|
||||
deletionQueue_.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
// Using `KStdList` as it allows to implement `Collect` without memory allocations,
|
||||
// which is important for GC mark phase.
|
||||
|
||||
@@ -959,7 +959,7 @@ JobKind Worker::processQueueElement(bool blocking) {
|
||||
WorkerLaunchpad(obj, dummyHolder.slot());
|
||||
} catch (ExceptionObjHolder& e) {
|
||||
if (errorReporting())
|
||||
ReportUnhandledException(e.obj());
|
||||
ReportUnhandledException(e.GetExceptionObject());
|
||||
}
|
||||
DisposeStablePointer(job.executeAfter.operation);
|
||||
break;
|
||||
@@ -979,7 +979,7 @@ JobKind Worker::processQueueElement(bool blocking) {
|
||||
} catch (ExceptionObjHolder& e) {
|
||||
ok = false;
|
||||
if (errorReporting())
|
||||
ReportUnhandledException(e.obj());
|
||||
ReportUnhandledException(e.GetExceptionObject());
|
||||
}
|
||||
// Notify the future.
|
||||
job.regularJob.future->storeResultUnlocked(result, ok);
|
||||
|
||||
@@ -4,9 +4,8 @@
|
||||
*/
|
||||
package kotlin.native
|
||||
|
||||
import kotlin.native.concurrent.isFrozen
|
||||
import kotlin.native.concurrent.InvalidMutabilityException
|
||||
import kotlin.native.internal.Escapes
|
||||
import kotlin.native.internal.UnhandledExceptionHookHolder
|
||||
|
||||
/**
|
||||
* Initializes Kotlin runtime for the current thread, if not inited already.
|
||||
@@ -45,19 +44,16 @@ public typealias ReportUnhandledExceptionHook = Function1<Throwable, Unit>
|
||||
* with custom exception hooks.
|
||||
*/
|
||||
public fun setUnhandledExceptionHook(hook: ReportUnhandledExceptionHook): ReportUnhandledExceptionHook? {
|
||||
if (Platform.memoryModel != MemoryModel.EXPERIMENTAL && !hook.isFrozen) {
|
||||
try {
|
||||
return UnhandledExceptionHookHolder.hook.swap(hook)
|
||||
} catch (e: InvalidMutabilityException) {
|
||||
throw InvalidMutabilityException("Unhandled exception hook must be frozen")
|
||||
}
|
||||
return setUnhandledExceptionHook0(hook)
|
||||
}
|
||||
|
||||
@SymbolName("Kotlin_setUnhandledExceptionHook")
|
||||
@Escapes(0b01) // <hook> escapes
|
||||
external private fun setUnhandledExceptionHook0(hook: ReportUnhandledExceptionHook): ReportUnhandledExceptionHook?
|
||||
|
||||
/**
|
||||
* Compute stable wrt potential object relocations by the memory manager identity hash code.
|
||||
* @return 0 for `null` object, identity hash code otherwise.
|
||||
*/
|
||||
@SymbolName("Kotlin_Any_hashCode")
|
||||
public external fun Any?.identityHashCode(): Int
|
||||
public external fun Any?.identityHashCode(): Int
|
||||
|
||||
@@ -283,6 +283,19 @@ public class AtomicReference<T> {
|
||||
public override fun toString(): String =
|
||||
"${debugString(this)} -> ${debugString(value)}"
|
||||
|
||||
// TODO: Consider making this public.
|
||||
internal fun swap(new: T): T {
|
||||
while (true) {
|
||||
val old = value
|
||||
if (old === new) {
|
||||
return old
|
||||
}
|
||||
if (compareAndSet(old, new)) {
|
||||
return old
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Implementation details.
|
||||
@SymbolName("Kotlin_AtomicReference_set")
|
||||
private external fun setImpl(new: Any?): Unit
|
||||
|
||||
@@ -7,6 +7,7 @@ package kotlin.native.internal
|
||||
|
||||
import kotlin.internal.getProgressionLastElement
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.native.concurrent.AtomicReference
|
||||
|
||||
@ExportForCppRuntime
|
||||
fun ThrowNullPointerException(): Nothing {
|
||||
@@ -118,10 +119,23 @@ internal fun ReportUnhandledException(throwable: Throwable) {
|
||||
@SymbolName("TerminateWithUnhandledException")
|
||||
internal external fun TerminateWithUnhandledException(throwable: Throwable)
|
||||
|
||||
// Using object to make sure that `hook` is initialized when it's needed instead of
|
||||
// in a normal global initialization flow. This is important if some global happens
|
||||
// to throw an exception during it's initialization before this hook would've been initialized.
|
||||
internal object UnhandledExceptionHookHolder {
|
||||
internal val hook: AtomicReference<ReportUnhandledExceptionHook?> = AtomicReference(null)
|
||||
}
|
||||
|
||||
@PublishedApi
|
||||
@ExportForCppRuntime
|
||||
internal fun ExceptionReporterLaunchpad(reporter: (Throwable) -> Unit, throwable: Throwable) {
|
||||
internal fun OnUnhandledException(throwable: Throwable) {
|
||||
val handler = UnhandledExceptionHookHolder.hook.swap(null)
|
||||
if (handler == null) {
|
||||
ReportUnhandledException(throwable);
|
||||
return
|
||||
}
|
||||
try {
|
||||
reporter(throwable)
|
||||
handler(throwable)
|
||||
} catch (t: Throwable) {
|
||||
ReportUnhandledException(t)
|
||||
}
|
||||
@@ -210,8 +224,3 @@ internal fun <T> listOfInternal(vararg elements: T): List<T> {
|
||||
result.add(elements[i])
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
@PublishedApi
|
||||
@SymbolName("OnUnhandledException")
|
||||
external internal fun OnUnhandledException(throwable: Throwable)
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2010-2021 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.
|
||||
*/
|
||||
|
||||
#include "Memory.h"
|
||||
|
||||
#include "StableRefRegistry.hpp"
|
||||
#include "ThreadData.hpp"
|
||||
#include "ThreadRegistry.hpp"
|
||||
|
||||
using namespace kotlin;
|
||||
|
||||
namespace {
|
||||
|
||||
#if !KONAN_NO_EXCEPTIONS
|
||||
class ExceptionObjHolderImpl : public ExceptionObjHolder {
|
||||
public:
|
||||
explicit ExceptionObjHolderImpl(ObjHeader* obj) noexcept {
|
||||
auto* threadData = mm::ThreadRegistry::Instance().CurrentThreadData();
|
||||
stableRef_ = threadData->stableRefThreadQueue().Insert(obj);
|
||||
}
|
||||
|
||||
~ExceptionObjHolderImpl() override {
|
||||
auto* threadData = mm::ThreadRegistry::Instance().CurrentThreadData();
|
||||
threadData->stableRefThreadQueue().Erase(stableRef_);
|
||||
}
|
||||
|
||||
ObjHeader* obj() noexcept { return **stableRef_; }
|
||||
|
||||
private:
|
||||
mm::StableRefRegistry::Node* stableRef_;
|
||||
};
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
#if !KONAN_NO_EXCEPTIONS
|
||||
// static
|
||||
RUNTIME_NORETURN void ExceptionObjHolder::Throw(ObjHeader* exception) {
|
||||
throw ExceptionObjHolderImpl(exception);
|
||||
}
|
||||
|
||||
ObjHeader* ExceptionObjHolder::GetExceptionObject() noexcept {
|
||||
return static_cast<ExceptionObjHolderImpl*>(this)->obj();
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright 2010-2021 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.
|
||||
*/
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "Memory.h"
|
||||
#include "TestSupport.hpp"
|
||||
#include "ThreadData.hpp"
|
||||
#include "Types.h"
|
||||
|
||||
using namespace kotlin;
|
||||
|
||||
namespace {
|
||||
|
||||
class ExceptionObjHolderTest : public ::testing::Test {
|
||||
public:
|
||||
~ExceptionObjHolderTest() {
|
||||
auto& stableRefs = mm::StableRefRegistry::Instance();
|
||||
stableRefs.ClearForTests();
|
||||
}
|
||||
|
||||
static KStdVector<ObjHeader*> Collect(mm::ThreadData& threadData) {
|
||||
auto& stableRefs = mm::StableRefRegistry::Instance();
|
||||
stableRefs.ProcessThread(&threadData);
|
||||
stableRefs.ProcessDeletions();
|
||||
KStdVector<ObjHeader*> result;
|
||||
for (const auto& obj : stableRefs.Iter()) {
|
||||
result.push_back(obj);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_F(ExceptionObjHolderTest, NothingByDefault) {
|
||||
mm::RunInNewThread([](mm::ThreadData& threadData) { EXPECT_THAT(Collect(threadData), testing::IsEmpty()); });
|
||||
}
|
||||
|
||||
TEST_F(ExceptionObjHolderTest, Throw) {
|
||||
mm::RunInNewThread([](mm::ThreadData& threadData) {
|
||||
ASSERT_THAT(Collect(threadData), testing::IsEmpty());
|
||||
|
||||
ObjHeader exception;
|
||||
try {
|
||||
ExceptionObjHolder::Throw(&exception);
|
||||
} catch (...) {
|
||||
EXPECT_THAT(Collect(threadData), testing::ElementsAre(&exception));
|
||||
}
|
||||
EXPECT_THAT(Collect(threadData), testing::IsEmpty());
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(ExceptionObjHolderTest, ThrowInsideCatch) {
|
||||
mm::RunInNewThread([](mm::ThreadData& threadData) {
|
||||
ASSERT_THAT(Collect(threadData), testing::IsEmpty());
|
||||
|
||||
ObjHeader exception1;
|
||||
try {
|
||||
ExceptionObjHolder::Throw(&exception1);
|
||||
} catch (...) {
|
||||
ObjHeader exception2;
|
||||
try {
|
||||
ExceptionObjHolder::Throw(&exception2);
|
||||
} catch (...) {
|
||||
EXPECT_THAT(Collect(threadData), testing::ElementsAre(&exception1, &exception2));
|
||||
}
|
||||
EXPECT_THAT(Collect(threadData), testing::ElementsAre(&exception1));
|
||||
}
|
||||
EXPECT_THAT(Collect(threadData), testing::IsEmpty());
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(ExceptionObjHolderTest, StoreException) {
|
||||
mm::RunInNewThread([](mm::ThreadData& threadData) {
|
||||
ASSERT_THAT(Collect(threadData), testing::IsEmpty());
|
||||
|
||||
ObjHeader exception1;
|
||||
std::exception_ptr storedException1;
|
||||
try {
|
||||
ExceptionObjHolder::Throw(&exception1);
|
||||
} catch (...) {
|
||||
storedException1 = std::current_exception();
|
||||
}
|
||||
EXPECT_THAT(Collect(threadData), testing::ElementsAre(&exception1));
|
||||
|
||||
ObjHeader exception2;
|
||||
std::exception_ptr storedException2;
|
||||
try {
|
||||
ExceptionObjHolder::Throw(&exception2);
|
||||
} catch (...) {
|
||||
storedException2 = std::current_exception();
|
||||
}
|
||||
EXPECT_THAT(Collect(threadData), testing::ElementsAre(&exception1, &exception2));
|
||||
|
||||
storedException1 = std::exception_ptr();
|
||||
EXPECT_THAT(Collect(threadData), testing::ElementsAre(&exception2));
|
||||
|
||||
storedException2 = std::exception_ptr();
|
||||
EXPECT_THAT(Collect(threadData), testing::IsEmpty());
|
||||
});
|
||||
}
|
||||
@@ -48,6 +48,8 @@ public:
|
||||
// much of a problem is it.
|
||||
Iterable Iter() noexcept { return stableRefs_.Iter(); }
|
||||
|
||||
void ClearForTests() noexcept { stableRefs_.ClearForTests(); }
|
||||
|
||||
private:
|
||||
// Current approach optimizes for creating and disposing of stable refs:
|
||||
// * creation just enqueues ref, disposing either queues or deletes the ref immediately (if it still resides in the current queue).
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.
|
||||
*/
|
||||
|
||||
#include "../../main/cpp/TestSupport.hpp"
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include "ThreadData.hpp"
|
||||
#include "ThreadRegistry.hpp"
|
||||
|
||||
namespace kotlin {
|
||||
namespace mm {
|
||||
|
||||
template <typename F>
|
||||
void RunInNewThread(F f) {
|
||||
std::thread([&f]() {
|
||||
class ScopedRegistration : private kotlin::Pinned {
|
||||
public:
|
||||
ScopedRegistration() : node_(mm::ThreadRegistry::Instance().RegisterCurrentThread()) {}
|
||||
|
||||
~ScopedRegistration() { mm::ThreadRegistry::Instance().Unregister(node_); }
|
||||
|
||||
mm::ThreadData& threadData() { return *node_->Get(); }
|
||||
|
||||
private:
|
||||
mm::ThreadRegistry::Node* node_;
|
||||
} registration;
|
||||
|
||||
f(registration.threadData());
|
||||
}).join();
|
||||
}
|
||||
|
||||
} // namespace mm
|
||||
} // namespace kotlin
|
||||
@@ -7,69 +7,58 @@
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "TestSupport.hpp"
|
||||
#include "ThreadData.hpp"
|
||||
#include "ThreadRegistry.hpp"
|
||||
#include "ThreadState.hpp"
|
||||
|
||||
using namespace kotlin;
|
||||
|
||||
TEST(ThreadStateTest, StateSwitch) {
|
||||
std::thread t([]() {
|
||||
mm::ThreadRegistry::Instance().RegisterCurrentThread();
|
||||
|
||||
auto* threadData = mm::ThreadRegistry::Instance().CurrentThreadData();
|
||||
auto initialState = threadData->state();
|
||||
mm::RunInNewThread([](mm::ThreadData& threadData) {
|
||||
auto initialState = threadData.state();
|
||||
EXPECT_EQ(mm::ThreadState::kRunnable, initialState);
|
||||
|
||||
mm::ThreadState oldState = mm::SwitchThreadState(threadData, mm::ThreadState::kNative);
|
||||
mm::ThreadState oldState = mm::SwitchThreadState(&threadData, mm::ThreadState::kNative);
|
||||
EXPECT_EQ(initialState, oldState);
|
||||
EXPECT_EQ(mm::ThreadState::kNative, threadData->state());
|
||||
EXPECT_EQ(mm::ThreadState::kNative, threadData.state());
|
||||
|
||||
// Check functions exported for the compiler too.
|
||||
Kotlin_mm_switchThreadStateRunnable();
|
||||
EXPECT_EQ(mm::ThreadState::kRunnable, threadData->state());
|
||||
EXPECT_EQ(mm::ThreadState::kRunnable, threadData.state());
|
||||
|
||||
Kotlin_mm_switchThreadStateNative();
|
||||
EXPECT_EQ(mm::ThreadState::kNative, threadData->state());
|
||||
EXPECT_EQ(mm::ThreadState::kNative, threadData.state());
|
||||
});
|
||||
t.join();
|
||||
}
|
||||
|
||||
TEST(ThreadStateTest, StateGuard) {
|
||||
std::thread t([]() {
|
||||
mm::ThreadRegistry::Instance().RegisterCurrentThread();
|
||||
auto* threadData = mm::ThreadRegistry::Instance().CurrentThreadData();
|
||||
auto initialState = threadData->state();
|
||||
mm::RunInNewThread([](mm::ThreadData& threadData) {
|
||||
auto initialState = threadData.state();
|
||||
EXPECT_EQ(mm::ThreadState::kRunnable, initialState);
|
||||
{
|
||||
mm::ThreadStateGuard guard(threadData, mm::ThreadState::kNative);
|
||||
EXPECT_EQ(mm::ThreadState::kNative, threadData->state());
|
||||
mm::ThreadStateGuard guard(&threadData, mm::ThreadState::kNative);
|
||||
EXPECT_EQ(mm::ThreadState::kNative, threadData.state());
|
||||
}
|
||||
EXPECT_EQ(initialState, threadData->state());
|
||||
EXPECT_EQ(initialState, threadData.state());
|
||||
});
|
||||
t.join();
|
||||
}
|
||||
|
||||
TEST(ThreadStateDeathTest, StateAsserts) {
|
||||
std::thread t([]() {
|
||||
auto* threadData = mm::ThreadRegistry::Instance().RegisterCurrentThread()->Get();
|
||||
EXPECT_DEATH(mm::AssertThreadState(threadData, mm::ThreadState::kNative),
|
||||
mm::RunInNewThread([](mm::ThreadData& threadData) {
|
||||
EXPECT_DEATH(mm::AssertThreadState(&threadData, mm::ThreadState::kNative),
|
||||
"runtime assert: Unexpected thread state. Expected: NATIVE. Actual: RUNNABLE");
|
||||
});
|
||||
t.join();
|
||||
}
|
||||
|
||||
TEST(ThreadStateDeathTest, IncorrectStateSwitch) {
|
||||
std::thread t([]() {
|
||||
auto* threadData = mm::ThreadRegistry::Instance().RegisterCurrentThread()->Get();
|
||||
EXPECT_DEATH(mm::SwitchThreadState(threadData, kotlin::mm::ThreadState::kRunnable),
|
||||
mm::RunInNewThread([](mm::ThreadData& threadData) {
|
||||
EXPECT_DEATH(mm::SwitchThreadState(&threadData, kotlin::mm::ThreadState::kRunnable),
|
||||
"runtime assert: Illegal thread state switch. Old state: RUNNABLE. New state: RUNNABLE");
|
||||
EXPECT_DEATH(Kotlin_mm_switchThreadStateRunnable(),
|
||||
"runtime assert: Illegal thread state switch. Old state: RUNNABLE. New state: RUNNABLE");
|
||||
|
||||
mm::SwitchThreadState(threadData, kotlin::mm::ThreadState::kNative);
|
||||
mm::SwitchThreadState(&threadData, kotlin::mm::ThreadState::kNative);
|
||||
EXPECT_DEATH(Kotlin_mm_switchThreadStateNative(),
|
||||
"runtime assert: Illegal thread state switch. Old state: NATIVE. New state: NATIVE");
|
||||
});
|
||||
t.join();
|
||||
}
|
||||
|
||||
@@ -170,7 +170,7 @@ RUNTIME_NORETURN OBJ_GETTER(DescribeObjectForDebugging, KConstNativePtr typeInfo
|
||||
throw std::runtime_error("Not implemented for tests");
|
||||
}
|
||||
|
||||
void ExceptionReporterLaunchpad(KRef reporter, KRef throwable) {
|
||||
void OnUnhandledException(KRef throwable) {
|
||||
throw std::runtime_error("Not implemented for tests");
|
||||
}
|
||||
|
||||
|
||||
@@ -64,6 +64,8 @@ kotlin {
|
||||
}
|
||||
}
|
||||
|
||||
android()
|
||||
|
||||
sourceSets {
|
||||
val x86Main by getting
|
||||
if (!simulatorOnly) {
|
||||
|
||||
+1
@@ -98,6 +98,7 @@ interface AppleConfigurables : Configurables, ClangFlags {
|
||||
val arch get() = targetString("arch")!!
|
||||
val osVersionMin get() = targetString("osVersionMin")!!
|
||||
val osVersionMinFlagLd get() = targetString("osVersionMinFlagLd")!!
|
||||
val stripFlags get() = targetList("stripFlags")
|
||||
val additionalToolsDir get() = hostString("additionalToolsDir")
|
||||
val absoluteAdditionalToolsDir get() = absolute(additionalToolsDir)
|
||||
}
|
||||
|
||||
@@ -265,7 +265,7 @@ class MacOSBasedLinker(targetProperties: AppleConfigurables)
|
||||
if (debug) {
|
||||
result += dsymUtilCommand(executable, outputDsymBundle)
|
||||
if (optimize) {
|
||||
result += Command(strip, "-S", executable)
|
||||
result += Command(strip, *stripFlags.toTypedArray(), executable)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user