[Kotlin/Native][Interop] Skia interop plugin for cinterop
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
Skia interop support
|
||||
====================
|
||||
|
||||
This is a plugin for cinterop that allows to interop with `Skia Graphics Library`.
|
||||
|
||||
Primarily targeted to be used by Skiko project.
|
||||
|
||||
Usage:
|
||||
------
|
||||
|
||||
Add to the .def file the following clauses:
|
||||
|
||||
```
|
||||
plugin = org.jetbrains.kotlin.native.cinterop.plugin.skia
|
||||
language = C++
|
||||
```
|
||||
|
||||
Implementation details
|
||||
======================
|
||||
|
||||
Limited C++ interop provided via plain C wrappers and cinterop mechanism.
|
||||
WARNING: this is by no means a general support for C++ by cinterop.
|
||||
|
||||
C++ features
|
||||
------------
|
||||
|
||||
* C++ class:
|
||||
+ Virtual and non-virtual methods
|
||||
* Static methods mapped as companion ones
|
||||
* Fields
|
||||
* Static fields as companion ones
|
||||
* Constructors are mapped to `__init__(args)` companion methods
|
||||
* Destructor is mapped to `__destroy__(this)` companion method
|
||||
* LValueReference parameters and return value internally handled as pointers
|
||||
* Namespaces provided as simple mangled class name. It works but awfully ugly. Shall be fixed by mapping to packages.
|
||||
* Nested C++ classes: same as namespaces, simple mangling. Shall be fixed.
|
||||
* Access modifiers: only public members exposed to Kotlin. Anonymous namespaces are silently ignored.
|
||||
|
||||
Known issues
|
||||
------------
|
||||
|
||||
* C++ object with nontrivial copy constructor may work incorrect when used as by value parameter or return typr.
|
||||
"Nontrivial" in this context relates to objects which bahavior depends on memory location. Most of other non-POD objects may be handled well.
|
||||
In fact, "by value" return type is mapped to CValue which is immutrable movable block. Particularly, non-const methods invoked with CValue receiver
|
||||
via useContents mechanism run on temporary object and therefore does not modify the original copy. TBD.
|
||||
|
||||
Limitations
|
||||
-----------
|
||||
|
||||
* Operators are not supported yet (silently ignored)
|
||||
* LVReference is mapped to CPointer<T>? which is incorrect (should be notNull). This may cause segmentation fault in case of null would be sent as a parameter. TBD
|
||||
* const overload not supported and cause compilation error. That is, two class methods with the same signature (`const` and `non-const`) can't be compiled. The same for function parameters: if two functions differ only in `const*` modifier of parameter, this will cause "conflicting overloads" error. TBD.
|
||||
* C++ lambda type is not supported yet.
|
||||
* Member pointer, member reference, rvalue reference and some other types are not wupported.
|
||||
* Inheritance is not implemented yet. C++-style callbacks (overriding virtual method in Kotlin) may be implemented via plain C bridge (this can be done by hand as a workaround). TBD.
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
buildscript {
|
||||
apply from: "$rootDir/kotlin-native/gradle/kotlinGradlePlugin.gradle"
|
||||
}
|
||||
|
||||
apply plugin: 'kotlin'
|
||||
|
||||
dependencies {
|
||||
implementation project(":kotlin-native:Interop:Indexer")
|
||||
implementation project(":kotlin-native:Interop:StubGenerator")
|
||||
}
|
||||
|
||||
compileKotlin {
|
||||
kotlinOptions {
|
||||
allWarningsAsErrors=true
|
||||
}
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
package org.jetbrains.kotlin.native.interop.skia
|
||||
|
||||
import clang.*
|
||||
import kotlinx.cinterop.CValue
|
||||
import org.jetbrains.kotlin.native.interop.indexer.*
|
||||
|
||||
fun buildSkiaNativeIndexImpl(library: NativeLibrary, verbose: Boolean): IndexerResult {
|
||||
val result = SkiaNativeIndexImpl(library, verbose)
|
||||
return buildNativeIndexImpl(result)
|
||||
}
|
||||
|
||||
class SkiaNativeIndexImpl(library: NativeLibrary, verbose: Boolean) : NativeIndexImpl(library, verbose) {
|
||||
override fun convertType(type: CValue<CXType>, typeAttributes: CValue<CXTypeAttributes>?): Type {
|
||||
if (type.kind == CXTypeKind.CXType_Record) {
|
||||
val decl: StructDecl = getStructDeclAt(clang_getTypeDeclaration(type))
|
||||
if (decl.isSkiaSharedPointer) {
|
||||
return ManagedType(decl)
|
||||
}
|
||||
}
|
||||
return super.convertType(type, typeAttributes)
|
||||
}
|
||||
|
||||
// Skip functions which parameter or return type is TemplateRef
|
||||
override fun isFuncDeclEligible(cursor: CValue<CXCursor>): Boolean =
|
||||
cursor.containsOnlySkiaSharedPointerTemplates()
|
||||
|
||||
override fun String.isUnknownTemplate() = // TODO: this is a hack.
|
||||
this.isCppTemplate && !this.isSkiaSharedPointer
|
||||
}
|
||||
|
||||
fun CValue<CXCursor>.containsTemplates(): Boolean {
|
||||
var ret = false
|
||||
visitChildren(this) { childCursor, _ ->
|
||||
when (childCursor.kind) {
|
||||
CXCursorKind.CXCursor_TemplateRef -> {
|
||||
ret = true
|
||||
CXChildVisitResult.CXChildVisit_Break
|
||||
}
|
||||
else -> CXChildVisitResult.CXChildVisit_Recurse
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
fun CValue<CXCursor>.containsOnlySkiaSharedPointerTemplates(): Boolean {
|
||||
var ret = true
|
||||
visitChildren(this) { childCursor, _ ->
|
||||
when (childCursor.kind) {
|
||||
CXCursorKind.CXCursor_TemplateRef ->
|
||||
if (childCursor.spelling == "sk_sp" && !childCursor.containsTemplates()) {
|
||||
CXChildVisitResult.CXChildVisit_Continue
|
||||
} else {
|
||||
ret = false
|
||||
CXChildVisitResult.CXChildVisit_Break
|
||||
}
|
||||
else -> CXChildVisitResult.CXChildVisit_Recurse
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
val StructDecl.isSkiaSharedPointer: Boolean
|
||||
get() = spelling.isSkiaSharedPointer
|
||||
|
||||
val StructDecl.stripSkiaSharedPointer: String
|
||||
get() {
|
||||
assert(this.isSkiaSharedPointer)
|
||||
return this.spelling.drop(6).dropLast(1).let { // TODO: this is a hack.
|
||||
if (it.startsWith("const ")) it.drop(6) else it
|
||||
}
|
||||
}
|
||||
|
||||
private val String.isCppTemplate: Boolean // TODO: this is a hack.
|
||||
get() = this.contains("<") && this.endsWith(">")
|
||||
|
||||
private val String.isSkiaSharedPointer: Boolean // TODO: this is a hack.
|
||||
get() = this.startsWith("sk_sp<") && this.endsWith(">")
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.native.interop.skia
|
||||
|
||||
import org.jetbrains.kotlin.native.interop.gen.ManagedTypePassing
|
||||
import org.jetbrains.kotlin.native.interop.gen.StubIrContext
|
||||
import org.jetbrains.kotlin.native.interop.gen.getStringRepresentation
|
||||
import org.jetbrains.kotlin.native.interop.gen.jvm.Plugin
|
||||
import org.jetbrains.kotlin.native.interop.indexer.IndexerResult
|
||||
import org.jetbrains.kotlin.native.interop.indexer.ManagedType
|
||||
import org.jetbrains.kotlin.native.interop.indexer.NativeLibrary
|
||||
import org.jetbrains.kotlin.native.interop.indexer.Type
|
||||
|
||||
class SkiaPlugin : Plugin {
|
||||
override val name = "Skia"
|
||||
override fun buildNativeIndex(library: NativeLibrary, verbose: Boolean): IndexerResult =
|
||||
buildSkiaNativeIndexImpl(library, verbose)
|
||||
|
||||
override val managedTypePassing = object : ManagedTypePassing() {
|
||||
override val ManagedType.passValue: String get() = "sk_ref_sp<${this.decl.stripSkiaSharedPointer}>"
|
||||
override val ManagedType.returnValue: String get() = ".release()"
|
||||
}
|
||||
|
||||
override val ManagedType.stringRepresentation: String get() {
|
||||
assert(this.decl.isSkiaSharedPointer)
|
||||
return "${this.decl.stripSkiaSharedPointer}*"
|
||||
}
|
||||
override fun stubsBuildingContext(stubIrContext: StubIrContext) = SkiaStubsBuildingContextImpl(stubIrContext)
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.native.interop.skia
|
||||
|
||||
import org.jetbrains.kotlin.native.interop.gen.Classifier
|
||||
import org.jetbrains.kotlin.native.interop.gen.StubIrContext
|
||||
import org.jetbrains.kotlin.native.interop.gen.StubsBuildingContextImpl
|
||||
import org.jetbrains.kotlin.native.interop.indexer.StructDecl
|
||||
|
||||
class SkiaStubsBuildingContextImpl(stubIrContext: StubIrContext) : StubsBuildingContextImpl(stubIrContext) {
|
||||
override val declarationMapper = SkiaDeclarationMapperImpl()
|
||||
|
||||
inner class SkiaDeclarationMapperImpl : DeclarationMapperImpl() {
|
||||
override fun getKotlinClassForManaged(structDecl: StructDecl): Classifier {
|
||||
assert(structDecl.isSkiaSharedPointer)
|
||||
val struct = structDecl.stripSkiaSharedPointer
|
||||
val structArgument = nativeIndex.structs.singleOrNull {
|
||||
it.spelling == struct && it.def != null
|
||||
} ?: error("Expected to find a single template arg struct by name: ${struct}")
|
||||
return getKotlinClassForPointed(structArgument)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user