Unverified Commit be90a3fa authored by Shagen Ogandzhanian's avatar Shagen Ogandzhanian Committed by GitHub

Build skiko with kotlin 2.2.20 (#1179)

This is backport of https://github.com/JetBrains/skiko/pull/1173 to
Kotlin 2.2.20

---------
Co-authored-by: 's avatarIvan Matkov <ivan.matkov@jetbrains.com>
parent 89888a47
...@@ -62,13 +62,27 @@ jobs: ...@@ -62,13 +62,27 @@ jobs:
name: 'Setup Prerequisites' name: 'Setup Prerequisites'
- uses: ./.github/actions/docker-skiko-run - uses: ./.github/actions/docker-skiko-run
name: 'Publish Maven Local' name: 'Publish KMP and AWT Maven Local'
with: with:
image_name: linux-compat image_name: linux-compat
command: | command: |
./gradlew --no-daemon --stacktrace \ ./gradlew --no-daemon --stacktrace \
-Pskiko.native.enabled=true \ -Pskiko.native.enabled=true \
:skiko:publishToMavenLocal -Pskiko.awt.enabled=true \
:skiko:publishKotlinMultiplatformPublicationToMavenLocal \
:skiko:publishAwtPublicationToMavenLocal \
:skiko:publishAwtRuntimeElementsPublicationToMavenLocal
- uses: ./.github/actions/docker-skiko-run
name: 'Publish Native Maven Local'
with:
image_name: linux-amd64
command: |
./gradlew --no-daemon --stacktrace \
-Pskiko.native.enabled=true \
-Pskiko.awt.enabled=false \
:skiko:publishLinuxArm64PublicationToMavenLocal \
:skiko:publishLinuxX64PublicationToMavenLocal
- uses: ./.github/actions/docker-skiko-run - uses: ./.github/actions/docker-skiko-run
name: 'Check AWT Sample' name: 'Check AWT Sample'
......
...@@ -169,7 +169,7 @@ jobs: ...@@ -169,7 +169,7 @@ jobs:
- uses: ./.github/actions/docker-skiko-run - uses: ./.github/actions/docker-skiko-run
name: 'Run Linux (x64) Tests' name: 'Run Linux (x64) Tests'
with: with:
image_name: linux-compat image_name: linux-amd64
virtual-display: true virtual-display: true
command: | command: |
./gradlew --no-daemon --stacktrace \ ./gradlew --no-daemon --stacktrace \
......
[versions] [versions]
kotlin = "2.0.10" kotlin = "2.2.20"
kotlinxBrowser = "0.5.0"
coroutines = "1.8.0" coroutines = "1.8.0"
jetbrainsRuntime-api = "1.5.0" jetbrainsRuntime-api = "1.5.0"
androidGradlePlugin = "7.4.2" androidGradlePlugin = "8.2.2"
dokka = "1.9.10" dokka = "1.9.10"
buildHelpers-publishing = "0.1.28" buildHelpers-publishing = "0.1.28"
...@@ -22,6 +24,8 @@ coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", ve ...@@ -22,6 +24,8 @@ coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", ve
jetbrainsRuntime-api = { module = "org.jetbrains.runtime:jbr-api", version.ref = "jetbrainsRuntime-api" } jetbrainsRuntime-api = { module = "org.jetbrains.runtime:jbr-api", version.ref = "jetbrainsRuntime-api" }
kotlinx-browser = { module = "org.jetbrains.kotlinx:kotlinx-browser", version.ref = "kotlinxBrowser" }
android-gradlePlugin = { module = "com.android.tools.build:gradle", version.ref = "androidGradlePlugin" } android-gradlePlugin = { module = "com.android.tools.build:gradle", version.ref = "androidGradlePlugin" }
dokka-gradlePlugin = { module = "org.jetbrains.dokka:dokka-gradle-plugin", version.ref = "dokka" } dokka-gradlePlugin = { module = "org.jetbrains.dokka:dokka-gradle-plugin", version.ref = "dokka" }
gradleDownloadTask-gradlePlugin = { module = "de.undercouch:gradle-download-task", version.ref = "gradleDownloadTask" } gradleDownloadTask-gradlePlugin = { module = "de.undercouch:gradle-download-task", version.ref = "gradleDownloadTask" }
......
...@@ -23,7 +23,7 @@ repositories { ...@@ -23,7 +23,7 @@ repositories {
plugins { plugins {
id("com.android.application") version "8.9.0" id("com.android.application") version "8.9.0"
kotlin("android") version "1.9.21" kotlin("android") version "2.2.20"
} }
val skikoNativeX64 by configurations.creating val skikoNativeX64 by configurations.creating
......
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins { plugins {
kotlin("jvm") version "1.9.21" kotlin("jvm") version "2.2.20"
application application
} }
...@@ -96,5 +96,5 @@ tasks.register("runInterop") { ...@@ -96,5 +96,5 @@ tasks.register("runInterop") {
} }
tasks.withType<KotlinCompile>().configureEach { tasks.withType<KotlinCompile>().configureEach {
kotlinOptions.freeCompilerArgs += "-opt-in=kotlin.RequiresOptIn" compilerOptions.freeCompilerArgs.add("-opt-in=kotlin.RequiresOptIn")
} }
...@@ -102,7 +102,11 @@ kotlin { ...@@ -102,7 +102,11 @@ kotlin {
jvm("awt") { jvm("awt") {
compilations.all { compilations.all {
kotlinOptions.jvmTarget = "11" compileTaskProvider.configure {
compilerOptions {
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11)
}
}
} }
} }
...@@ -310,7 +314,7 @@ if (hostOs == "macos") { ...@@ -310,7 +314,7 @@ if (hostOs == "macos") {
} else { } else {
// Otherwise copy the executable into the Xcode output directory. // Otherwise copy the executable into the Xcode output directory.
tasks.create("packForXCode", Copy::class.java) { tasks.create("packForXCode", Copy::class.java) {
dependsOn(kotlinBinary.linkTask) dependsOn(kotlinBinary.linkTaskProvider)
println("Packing for XCode: ${kotlinBinary.target}") println("Packing for XCode: ${kotlinBinary.target}")
...@@ -371,7 +375,5 @@ fun KotlinNativeTarget.configureToLaunchFromXcode() { ...@@ -371,7 +375,5 @@ fun KotlinNativeTarget.configureToLaunchFromXcode() {
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinNativeCompile>().configureEach { tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinNativeCompile>().configureEach {
kotlinOptions { compilerOptions.freeCompilerArgs.add("-opt-in=kotlinx.cinterop.ExperimentalForeignApi")
freeCompilerArgs += "-opt-in=kotlinx.cinterop.ExperimentalForeignApi"
}
} }
kotlin.code.style=official kotlin.code.style=official
org.gradle.jvmargs=-Xmx3G -XX:MaxMetaspaceSize=512m org.gradle.jvmargs=-Xmx3G -XX:MaxMetaspaceSize=512m
kotlin.version=2.0.10 kotlin.version=2.2.20
skiko.version=0.9.24 skiko.version=0.144.0
#skiko.composite.build=1 #skiko.composite.build=1
...@@ -51,7 +51,7 @@ kotlin { ...@@ -51,7 +51,7 @@ kotlin {
binaries.executable() binaries.executable()
} }
wasmJs() { wasmJs {
browser { browser {
commonWebpackConfig { commonWebpackConfig {
outputFileName = "webApp.js" outputFileName = "webApp.js"
...@@ -61,24 +61,16 @@ kotlin { ...@@ -61,24 +61,16 @@ kotlin {
} }
sourceSets { sourceSets {
val commonMain by getting { commonMain.dependencies {
implementation(libs.skiko)
}
webMain {
dependencies { dependencies {
implementation(libs.skiko) implementation(libs.browser)
} }
}
val webMain by creating {
dependsOn(commonMain)
resources.setSrcDirs(resources.srcDirs)
resources.srcDirs(unpackWasmRuntime.map { it.destinationDir }) resources.srcDirs(unpackWasmRuntime.map { it.destinationDir })
} }
val jsMain by getting {
dependsOn(webMain)
}
val wasmJsMain by getting {
dependsOn(webMain)
}
} }
} }
\ No newline at end of file
kotlin.code.style=official kotlin.code.style=official
kotlin.version=2.0.10 kotlin.version=2.2.20
skiko.version=0.9.24 skiko.version=0.144.0
#skiko.composite.build=1 #skiko.composite.build=1
...@@ -19,8 +19,11 @@ dependencyResolutionManagement { ...@@ -19,8 +19,11 @@ dependencyResolutionManagement {
versionCatalogs { versionCatalogs {
create("libs") { create("libs") {
version("skiko", providers.gradleProperty("skiko.version").get()) version("skiko", providers.gradleProperty("skiko.version").get())
version("kotlinxBrowser", "0.5.0")
library("skiko", "org.jetbrains.skiko", "skiko").versionRef("skiko") library("skiko", "org.jetbrains.skiko", "skiko").versionRef("skiko")
library("skiko-wasm-runtime", "org.jetbrains.skiko", "skiko-js-wasm-runtime").versionRef("skiko") library("skiko-wasm-runtime", "org.jetbrains.skiko", "skiko-js-wasm-runtime").versionRef("skiko")
library("browser", "org.jetbrains.kotlinx", "kotlinx-browser").versionRef("kotlinxBrowser")
} }
} }
} }
......
...@@ -7,7 +7,7 @@ fun main() { ...@@ -7,7 +7,7 @@ fun main() {
onWasmReady { onWasmReady {
val description = "Skiko running with ${getPlatform().name}" val description = "Skiko running with ${getPlatform().name}"
document.title = description document.title = description
findElementById("description")?.innerHTML = description document.getElementById("description")?.innerHTML = description
runApp() runApp()
} }
} }
\ No newline at end of file
...@@ -8,7 +8,7 @@ fun main() { ...@@ -8,7 +8,7 @@ fun main() {
onWasmReady { onWasmReady {
val description = "Skiko running with ${getPlatform().name}" val description = "Skiko running with ${getPlatform().name}"
document.title = description document.title = description
findElementById("description")?.innerHTML = description document.getElementById("description")?.innerHTML = description
runApp() runApp()
} }
} }
\ No newline at end of file
package org.jetbrains.skiko.sample.js package org.jetbrains.skiko.sample.js
import kotlinx.browser.document
import org.jetbrains.skia.Canvas import org.jetbrains.skia.Canvas
import org.jetbrains.skia.Paint import org.jetbrains.skia.Paint
import org.jetbrains.skia.Rect import org.jetbrains.skia.Rect
import org.jetbrains.skiko.SkiaLayer import org.jetbrains.skiko.SkiaLayer
import org.jetbrains.skiko.SkiaLayerRenderDelegate import org.jetbrains.skiko.SkiaLayerRenderDelegate
import org.jetbrains.skiko.SkikoRenderDelegate import org.jetbrains.skiko.SkikoRenderDelegate
import org.w3c.dom.Element import kotlinx.browser.document
private class DemoApp: SkikoRenderDelegate { private class DemoApp: SkikoRenderDelegate {
private val paint = Paint() private val paint = Paint()
...@@ -25,7 +24,7 @@ private class DemoApp: SkikoRenderDelegate { ...@@ -25,7 +24,7 @@ private class DemoApp: SkikoRenderDelegate {
internal fun runApp() { internal fun runApp() {
for (index in 1 .. 3) { for (index in 1 .. 3) {
val skiaLayer = SkiaLayer() val skiaLayer = SkiaLayer()
val canvas = findElementById("c$index")!! val canvas = document.getElementById("c$index")!!
val app = if (index == 3) { val app = if (index == 3) {
DemoApp() DemoApp()
} else { } else {
...@@ -35,6 +34,4 @@ internal fun runApp() { ...@@ -35,6 +34,4 @@ internal fun runApp() {
skiaLayer.attachTo(canvas) skiaLayer.attachTo(canvas)
skiaLayer.needRedraw() skiaLayer.needRedraw()
} }
} }
\ No newline at end of file
fun findElementById(id: String): Element? = document.getElementById(id)
\ No newline at end of file
@file:OptIn(ExperimentalKotlinGradlePluginApi::class) @file:OptIn(ExperimentalKotlinGradlePluginApi::class, ExperimentalWasmDsl::class)
import com.android.build.gradle.LibraryExtension import com.android.build.gradle.LibraryExtension
import com.android.build.gradle.LibraryPlugin import com.android.build.gradle.LibraryPlugin
...@@ -8,10 +8,11 @@ import org.jetbrains.compose.internal.publishing.MavenCentralProperties ...@@ -8,10 +8,11 @@ import org.jetbrains.compose.internal.publishing.MavenCentralProperties
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.dsl.KotlinJsCompile import org.jetbrains.kotlin.gradle.dsl.KotlinJsCompile
import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask
import org.jetbrains.kotlin.gradle.tasks.KotlinNativeCompile import org.jetbrains.kotlin.gradle.tasks.KotlinNativeCompile
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
import tasks.configuration.* import tasks.configuration.*
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
plugins { plugins {
kotlin("multiplatform") kotlin("multiplatform")
...@@ -58,6 +59,11 @@ repositories { ...@@ -58,6 +59,11 @@ repositories {
} }
kotlin { kotlin {
compilerOptions {
languageVersion.set(KotlinVersion.KOTLIN_2_2)
apiVersion.set(KotlinVersion.KOTLIN_2_2)
}
applyHierarchyTemplate(skikoSourceSetHierarchyTemplate) applyHierarchyTemplate(skikoSourceSetHierarchyTemplate)
skikoProjectContext.declareSkiaTasks() skikoProjectContext.declareSkiaTasks()
...@@ -96,7 +102,7 @@ kotlin { ...@@ -96,7 +102,7 @@ kotlin {
skikoProjectContext.declareWasmTasks() skikoProjectContext.declareWasmTasks()
js { js {
moduleName = "skiko-kjs" // override the name to avoid name collision with a different skiko.js file outputModuleName.set("skiko-kjs") // override the name to avoid name collision with a different skiko.js file
browser { browser {
testTask { testTask {
useKarma { useKarma {
...@@ -120,7 +126,7 @@ kotlin { ...@@ -120,7 +126,7 @@ kotlin {
@OptIn(ExperimentalWasmDsl::class) @OptIn(ExperimentalWasmDsl::class)
wasmJs { wasmJs {
moduleName = "skiko-kjs-wasm" // override the name to avoid name collision with a different skiko.js file outputModuleName.set("skiko-kjs-wasm") // override the name to avoid name collision with a different skiko.js file
browser { browser {
testTask { testTask {
useKarma { useKarma {
...@@ -183,6 +189,10 @@ kotlin { ...@@ -183,6 +189,10 @@ kotlin {
implementation(libs.coroutines.core.jvm) implementation(libs.coroutines.core.jvm)
} }
skikoProjectContext.webMainSourceSet?.dependencies {
implementation(libs.kotlinx.browser)
}
skikoProjectContext.awtMainSourceSet?.dependencies { skikoProjectContext.awtMainSourceSet?.dependencies {
implementation(libs.jetbrainsRuntime.api) implementation(libs.jetbrainsRuntime.api)
} }
...@@ -391,8 +401,6 @@ tasks.register("printSkiaVersion") { ...@@ -391,8 +401,6 @@ tasks.register("printSkiaVersion") {
} }
tasks.withType<KotlinNativeCompile>().configureEach { tasks.withType<KotlinNativeCompile>().configureEach {
// https://youtrack.jetbrains.com/issue/KT-56583
compilerOptions.freeCompilerArgs.add("-XXLanguage:+ImplicitSignedToUnsignedIntegerConversion")
compilerOptions.freeCompilerArgs.add("-opt-in=kotlinx.cinterop.ExperimentalForeignApi") compilerOptions.freeCompilerArgs.add("-opt-in=kotlinx.cinterop.ExperimentalForeignApi")
} }
......
kotlin.version=2.0.10 kotlin.version=2.2.20
...@@ -3,13 +3,6 @@ import org.gradle.api.Project ...@@ -3,13 +3,6 @@ import org.gradle.api.Project
val Project.wasmImports val Project.wasmImports
get() = layout.buildDirectory.dir("imports").get().asFile get() = layout.buildDirectory.dir("imports").get().asFile
val Project.setupMjs fun Project.wasmImport(name: String) = wasmImports.resolve(name)
get() = wasmImports.resolve("setup.mjs")
val Project.setupReexportMjs
get() = wasmImports.resolve("js-reexport-symbols.mjs")
val Project.skikoTestMjs
get() = wasmImports.resolve("skiko-test.mjs")
const val IMPORT_GENERATOR = "import-generator" const val IMPORT_GENERATOR = "import-generator"
\ No newline at end of file
...@@ -13,6 +13,8 @@ val SkikoProjectContext.awtTestSourceSet get() = if (project.supportAwt) kotlin. ...@@ -13,6 +13,8 @@ val SkikoProjectContext.awtTestSourceSet get() = if (project.supportAwt) kotlin.
val SkikoProjectContext.androidMainSourceSet get() = if (project.supportAndroid) kotlin.sourceSets.getByName("androidMain") else null val SkikoProjectContext.androidMainSourceSet get() = if (project.supportAndroid) kotlin.sourceSets.getByName("androidMain") else null
val SkikoProjectContext.webMainSourceSet get() = if (project.supportWeb) kotlin.sourceSets.getByName("webMain") else null
val SkikoProjectContext.webTestSourceSet get() = if (project.supportWeb) kotlin.sourceSets.getByName("webTest") else null val SkikoProjectContext.webTestSourceSet get() = if (project.supportWeb) kotlin.sourceSets.getByName("webTest") else null
val SkikoProjectContext.wasmJsTest get() = if (project.supportWeb) kotlin.sourceSets.getByName("wasmJsTest") else null val SkikoProjectContext.wasmJsTest get() = if (project.supportWeb) kotlin.sourceSets.getByName("wasmJsTest") else null
......
...@@ -182,7 +182,7 @@ fun configureCinterop( ...@@ -182,7 +182,7 @@ fun configureCinterop(
} }
target.compilations.getByName("main") { target.compilations.getByName("main") {
cinterops.create(cinteropName).apply { cinterops.create(cinteropName).apply {
defFileProperty.set(writeCInteropDef.map { it.outputFile.get().asFile }) definitionFile.set(writeCInteropDef.flatMap { it.outputFile })
} }
} }
} }
...@@ -294,9 +294,13 @@ fun SkikoProjectContext.configureNativeTarget(os: OS, arch: Arch, target: Kotlin ...@@ -294,9 +294,13 @@ fun SkikoProjectContext.configureNativeTarget(os: OS, arch: Arch, target: Kotlin
target.binaries.all { target.binaries.all {
freeCompilerArgs += allLibraries.map { listOf("-include-binary", it) }.flatten() + linkerFlags freeCompilerArgs += allLibraries.map { listOf("-include-binary", it) }.flatten() + linkerFlags
} }
target.compilations.all { target.compilations.all {
kotlinOptions { compilerOptions.configure {
freeCompilerArgs += allLibraries.map { listOf("-include-binary", it) }.flatten() + linkerFlags freeCompilerArgs.addAll(
allLibraries.flatMap { listOf("-include-binary", it) } + linkerFlags
)
} }
} }
...@@ -343,7 +347,7 @@ fun KotlinMultiplatformExtension.configureIOSTestsWithMetal(project: Project) { ...@@ -343,7 +347,7 @@ fun KotlinMultiplatformExtension.configureIOSTestsWithMetal(project: Project) {
if (targets.names.contains(target)) { if (targets.names.contains(target)) {
val testBinary = targets.getByName<KotlinNativeTarget>(target).binaries.getTest("DEBUG") val testBinary = targets.getByName<KotlinNativeTarget>(target).binaries.getTest("DEBUG")
project.tasks.register(target + "TestWithMetal") { project.tasks.register(target + "TestWithMetal") {
dependsOn(testBinary.linkTask) dependsOn(testBinary.linkTaskProvider)
doLast { doLast {
val simulatorIdPropertyKey = "skiko.iosSimulatorUUID" val simulatorIdPropertyKey = "skiko.iosSimulatorUUID"
val simulatorId = project.findProperty(simulatorIdPropertyKey)?.toString() val simulatorId = project.findProperty(simulatorIdPropertyKey)?.toString()
......
@file:OptIn(ExperimentalWasmDsl::class)
package tasks.configuration package tasks.configuration
import Arch import Arch
...@@ -19,16 +17,22 @@ import org.gradle.kotlin.dsl.getting ...@@ -19,16 +17,22 @@ import org.gradle.kotlin.dsl.getting
import org.gradle.kotlin.dsl.provideDelegate import org.gradle.kotlin.dsl.provideDelegate
import org.gradle.kotlin.dsl.registering import org.gradle.kotlin.dsl.registering
import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.plugin.*
import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl
import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsTargetDsl import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsTargetDsl
import projectDirs import projectDirs
import registerOrGetSkiaDirProvider import registerOrGetSkiaDirProvider
import setupMjs
import setupReexportMjs
import skikoTestMjs
import supportWeb import supportWeb
import wasmImport
import java.io.File import java.io.File
private val Project.setupMjs
get() = wasmImport("setup.mjs")
private val Project.setupReexportMjs
get() = wasmImport("js-reexport-symbols.mjs")
private val Project.skikoTestMjs
get() = wasmImport("skiko-test.mjs")
fun SkikoProjectContext.declareWasmTasks() { fun SkikoProjectContext.declareWasmTasks() {
if (!project.supportWeb) { if (!project.supportWeb) {
return return
......
...@@ -26,9 +26,6 @@ internal class ImportGeneratorExtension( ...@@ -26,9 +26,6 @@ internal class ImportGeneratorExtension(
importGenerator.getExportSymbols().forEach { symbolName -> importGenerator.getExportSymbols().forEach { symbolName ->
writer.appendLine("export let ${symbolName} = (...a) => ($symbolName = loadedWasm._[\"${symbolName}\"])(...a)") writer.appendLine("export let ${symbolName} = (...a) => ($symbolName = loadedWasm._[\"${symbolName}\"])(...a)")
if (reexportPath == null) {
writer.appendLine("window['${symbolName}'] = (...a) => loadedWasm._[\"${symbolName}\"](...a)")
}
} }
} }
......
...@@ -20,4 +20,4 @@ class ImportGeneratorRegistrar : CompilerPluginRegistrar() { ...@@ -20,4 +20,4 @@ class ImportGeneratorRegistrar : CompilerPluginRegistrar() {
) )
) )
} }
} }
\ No newline at end of file
package org.jetbrains.skiko package org.jetbrains.skiko
import org.jetbrains.kotlin.DeprecatedForRemovalCompilerApi
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.declarations.IrFunction
...@@ -7,6 +8,7 @@ import org.jetbrains.kotlin.ir.expressions.IrConst ...@@ -7,6 +8,7 @@ import org.jetbrains.kotlin.ir.expressions.IrConst
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.fromSymbolOwner
import org.jetbrains.kotlin.ir.symbols.UnsafeDuringIrConstructionAPI import org.jetbrains.kotlin.ir.symbols.UnsafeDuringIrConstructionAPI
import org.jetbrains.kotlin.ir.util.constructors import org.jetbrains.kotlin.ir.util.constructors
import org.jetbrains.kotlin.ir.util.defaultType import org.jetbrains.kotlin.ir.util.defaultType
...@@ -25,12 +27,12 @@ internal class ImportGeneratorTransformer(private val pluginContext: IrPluginCon ...@@ -25,12 +27,12 @@ internal class ImportGeneratorTransformer(private val pluginContext: IrPluginCon
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
private fun IrConstructorCall.getStringValue(value: String): String = private fun IrConstructorCall.getStringValue(value: String): String =
(getValueArgument(Name.identifier(value)) as IrConst<String>).value (getValueArgument(Name.identifier(value)) as IrConst).value as String
@OptIn(UnsafeDuringIrConstructionAPI::class) @OptIn(UnsafeDuringIrConstructionAPI::class, DeprecatedForRemovalCompilerApi::class)
private fun IrFunction.addWasmImportAnnotation(name: String) { private fun IrFunction.addWasmImportAnnotation(name: String) {
val annotationClass = pluginContext.referenceClass( val annotationClass = pluginContext.referenceClass(
ClassId.fromString("kotlin/wasm/WasmImport") // Replace with your fully qualified annotation name ClassId.fromString("kotlin/wasm/WasmImport")
) ?: return ) ?: return
val ctor = annotationClass.owner.constructors.first() val ctor = annotationClass.owner.constructors.first()
...@@ -61,14 +63,40 @@ internal class ImportGeneratorTransformer(private val pluginContext: IrPluginCon ...@@ -61,14 +63,40 @@ internal class ImportGeneratorTransformer(private val pluginContext: IrPluginCon
annotations += annotationCall annotations += annotationCall
} }
@OptIn(UnsafeDuringIrConstructionAPI::class, DeprecatedForRemovalCompilerApi::class)
private fun IrFunction.addJsNameAnnotation(name: String) {
val annotationClass = pluginContext.referenceClass(
ClassId.fromString("kotlin/js/JsName")
) ?: return
val ctor = annotationClass.owner.constructors.first()
val annotationCall = IrConstructorCallImpl.fromSymbolOwner(
startOffset = startOffset,
endOffset = endOffset,
type = annotationClass.owner.defaultType,
constructorSymbol = ctor.symbol,
)
annotationCall.putValueArgument(0, IrConstImpl.string(
startOffset,
endOffset,
pluginContext.irBuiltIns.stringType,
name
))
annotations += annotationCall
}
override fun visitFunction(declaration: IrFunction): IrStatement { override fun visitFunction(declaration: IrFunction): IrStatement {
return super.visitFunction(declaration).apply { return super.visitFunction(declaration).apply {
if (this !is IrFunction) return@apply if (this !is IrFunction) return@apply
val jsNameAnnotation = getAnnotation(FqName("kotlin.js.JsName")) val webImportAnnotation = getAnnotation(FqName("org.jetbrains.skiko.WebImport"))
?: return@apply ?: return@apply
val name = jsNameAnnotation.getStringValue("name") val name = webImportAnnotation.getStringValue("name")
addJsNameAnnotation(name)
addWasmImportAnnotation(name) addWasmImportAnnotation(name)
exportSymbols.add(name) exportSymbols.add(name)
......
...@@ -46,14 +46,14 @@ class CanvasTest { ...@@ -46,14 +46,14 @@ class CanvasTest {
// Hint: use surface.makeImageSnapshot().printBitmap() to print image as kotlin array // Hint: use surface.makeImageSnapshot().printBitmap() to print image as kotlin array
val expected = imageFromIntArray(intArrayOf( val expected = imageFromIntArray(intArrayOf(
0x00000000.toInt(), 0xffbf2020.toInt(), 0xff9f2040.toInt(), 0xff802060.toInt(), 0xff602080.toInt(), 0xff40209f.toInt(), 0xff2020bf.toInt(), 0xff0020df.toInt(), 0x00000000, 0xffbf2020.toInt(), 0xff9f2040.toInt(), 0xff802060.toInt(), 0xff602080.toInt(), 0xff40209f.toInt(), 0xff2020bf.toInt(), 0xff0020df.toInt(),
0x00000000.toInt(), 0x00000000.toInt(), 0xff806020.toInt(), 0xff606040.toInt(), 0xff406060.toInt(), 0xff206080.toInt(), 0xff00609f.toInt(), 0x00000000.toInt(), 0x00000000, 0x00000000, 0xff806020.toInt(), 0xff606040.toInt(), 0xff406060.toInt(), 0xff206080.toInt(), 0xff00609f.toInt(), 0x00000000,
0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0xff409f20.toInt(), 0xff209f40.toInt(), 0xff009f60.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000, 0x00000000, 0x00000000, 0xff409f20.toInt(), 0xff209f40.toInt(), 0xff009f60.toInt(), 0x00000000, 0x00000000,
0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0xff00df20.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xff00df20.toInt(), 0x00000000, 0x00000000, 0x00000000,
0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0xff20ffdf.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xff20ffdf.toInt(), 0x00000000, 0x00000000, 0x00000000,
0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0xff60bfdf.toInt(), 0xff60dfbf.toInt(), 0xff60ff9f.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000, 0x00000000, 0x00000000, 0xff60bfdf.toInt(), 0xff60dfbf.toInt(), 0xff60ff9f.toInt(), 0x00000000, 0x00000000,
0x00000000.toInt(), 0x00000000.toInt(), 0xff9f80df.toInt(), 0xff9f9fbf.toInt(), 0xff9fbf9f.toInt(), 0xff9fdf80.toInt(), 0xff9fff60.toInt(), 0x00000000.toInt(), 0x00000000, 0x00000000, 0xff9f80df.toInt(), 0xff9f9fbf.toInt(), 0xff9fbf9f.toInt(), 0xff9fdf80.toInt(), 0xff9fff60.toInt(), 0x00000000,
0x00000000.toInt(), 0xffdf40df.toInt(), 0xffdf60bf.toInt(), 0xffdf809f.toInt(), 0xffdf9f80.toInt(), 0xffdfbf60.toInt(), 0xffdfdf40.toInt(), 0xffdfff20.toInt(), 0x00000000, 0xffdf40df.toInt(), 0xffdf60bf.toInt(), 0xffdf809f.toInt(), 0xffdf9f80.toInt(), 0xffdfbf60.toInt(), 0xffdfdf40.toInt(), 0xffdfff20.toInt(),
), surface.width) ), surface.width)
assertContentSame(expected, surface.makeImageSnapshot(), 0.25) assertContentSame(expected, surface.makeImageSnapshot(), 0.25)
...@@ -79,14 +79,14 @@ class CanvasTest { ...@@ -79,14 +79,14 @@ class CanvasTest {
) )
val expected = imageFromIntArray(intArrayOf( val expected = imageFromIntArray(intArrayOf(
0xffff0000.toInt(), 0xffff0000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0xffff0000.toInt(), 0xffff0000.toInt(), 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0xffff0000.toInt(), 0xffff0000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0xffff0000.toInt(), 0xffff0000.toInt(), 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0xffff0000.toInt(), 0xffff0000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000, 0x00000000, 0x00000000, 0xffff0000.toInt(), 0xffff0000.toInt(), 0x00000000, 0x00000000, 0x00000000,
0x00000000.toInt(), 0x00000000.toInt(), 0xffff0000.toInt(), 0xffff0000.toInt(), 0xffff0000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000, 0x00000000, 0xffff0000.toInt(), 0xffff0000.toInt(), 0xffff0000.toInt(), 0x00000000, 0x00000000, 0x00000000,
0x00000000.toInt(), 0xffff0000.toInt(), 0xffff0000.toInt(), 0xffff0000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000, 0xffff0000.toInt(), 0xffff0000.toInt(), 0xffff0000.toInt(), 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000.toInt(), 0xffff0000.toInt(), 0xffff0000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000, 0xffff0000.toInt(), 0xffff0000.toInt(), 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0xffff0000.toInt(), 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffff0000.toInt(),
), surface.width) ), surface.width)
assertContentSame(expected, surface.makeImageSnapshot(), 0.25) assertContentSame(expected, surface.makeImageSnapshot(), 0.25)
...@@ -176,14 +176,14 @@ class CanvasTest { ...@@ -176,14 +176,14 @@ class CanvasTest {
val expected = imageFromIntArray( val expected = imageFromIntArray(
pixArray = intArrayOf( pixArray = intArrayOf(
0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0xffcb0634.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000, 0x00000000, 0x00000000, 0xffcb0634.toInt(), 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000.toInt(), 0x00000000.toInt(), 0xffb63b49.toInt(), 0xff9b3064.toInt(), 0xff762b8a.toInt(), 0xff4b1cb4.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000, 0x00000000, 0xffb63b49.toInt(), 0xff9b3064.toInt(), 0xff762b8a.toInt(), 0xff4b1cb4.toInt(), 0x00000000, 0x00000000,
0x00000000.toInt(), 0x00000000.toInt(), 0xff8d6e73.toInt(), 0xff866479.toInt(), 0xff7a6386.toInt(), 0xff675698.toInt(), 0xff3632c9.toInt(), 0x00000000.toInt(), 0x00000000, 0x00000000, 0xff8d6e73.toInt(), 0xff866479.toInt(), 0xff7a6386.toInt(), 0xff675698.toInt(), 0xff3632c9.toInt(), 0x00000000,
0x00000000.toInt(), 0xff36cdc9.toInt(), 0xff67a998.toInt(), 0xff7a9c86.toInt(), 0xff869b79.toInt(), 0xff8d9173.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000, 0xff36cdc9.toInt(), 0xff67a998.toInt(), 0xff7a9c86.toInt(), 0xff869b79.toInt(), 0xff8d9173.toInt(), 0x00000000, 0x00000000,
0x00000000.toInt(), 0x00000000.toInt(), 0xff4be3b4.toInt(), 0xff76d48a.toInt(), 0xff9bcf64.toInt(), 0xffb6c449.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000, 0x00000000, 0xff4be3b4.toInt(), 0xff76d48a.toInt(), 0xff9bcf64.toInt(), 0xffb6c449.toInt(), 0x00000000, 0x00000000,
0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0xffcbf934.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xffcbf934.toInt(), 0x00000000, 0x00000000, 0x00000000,
0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
), ),
imageWidth = 8 imageWidth = 8
......
...@@ -5,14 +5,14 @@ import org.jetbrains.skia.util.imageFromIntArray ...@@ -5,14 +5,14 @@ import org.jetbrains.skia.util.imageFromIntArray
@Suppress("RemoveRedundantCallsOfConversionMethods") @Suppress("RemoveRedundantCallsOfConversionMethods")
val PIXELS_COLORS_8X8 by lazy { intArrayOf( val PIXELS_COLORS_8X8 by lazy { intArrayOf(
0xffff0000.toInt(), 0xffff0000.toInt(), 0xff00ff00.toInt(), 0xff00ff00.toInt(), 0xff0000ff.toInt(), 0xff0000ff.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0xffff0000.toInt(), 0xffff0000.toInt(), 0xff00ff00.toInt(), 0xff00ff00.toInt(), 0xff0000ff.toInt(), 0xff0000ff.toInt(), 0x00000000, 0x00000000,
0xffff0000.toInt(), 0xffff0000.toInt(), 0xff00ff00.toInt(), 0xff00ff00.toInt(), 0xff0000ff.toInt(), 0xff0000ff.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0xffff0000.toInt(), 0xffff0000.toInt(), 0xff00ff00.toInt(), 0xff00ff00.toInt(), 0xff0000ff.toInt(), 0xff0000ff.toInt(), 0x00000000, 0x00000000,
0xff00ffff.toInt(), 0xff00ffff.toInt(), 0xffff00ff.toInt(), 0xffff00ff.toInt(), 0xffffff00.toInt(), 0xffffff00.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0xff00ffff.toInt(), 0xff00ffff.toInt(), 0xffff00ff.toInt(), 0xffff00ff.toInt(), 0xffffff00.toInt(), 0xffffff00.toInt(), 0x00000000, 0x00000000,
0xff00ffff.toInt(), 0xff00ffff.toInt(), 0xffff00ff.toInt(), 0xffff00ff.toInt(), 0xffffff00.toInt(), 0xffffff00.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0xff00ffff.toInt(), 0xff00ffff.toInt(), 0xffff00ff.toInt(), 0xffff00ff.toInt(), 0xffffff00.toInt(), 0xffffff00.toInt(), 0x00000000, 0x00000000,
0xffffffff.toInt(), 0xffffffff.toInt(), 0xff000000.toInt(), 0xff000000.toInt(), 0x80000000.toInt(), 0x80000000.toInt(), 0x03000000.toInt(), 0x00000000.toInt(), 0xffffffff.toInt(), 0xffffffff.toInt(), 0xff000000.toInt(), 0xff000000.toInt(), 0x80000000.toInt(), 0x80000000.toInt(), 0x03000000, 0x00000000,
0xffffffff.toInt(), 0xffffffff.toInt(), 0xff000000.toInt(), 0xff000000.toInt(), 0x80000000.toInt(), 0x80000000.toInt(), 0x03000000.toInt(), 0x00000000.toInt(), 0xffffffff.toInt(), 0xffffffff.toInt(), 0xff000000.toInt(), 0xff000000.toInt(), 0x80000000.toInt(), 0x80000000.toInt(), 0x03000000, 0x00000000,
0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000.toInt(), 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
) } ) }
val IMAGE_COLORS_8X8 by lazy { imageFromIntArray(PIXELS_COLORS_8X8, 8) } val IMAGE_COLORS_8X8 by lazy { imageFromIntArray(PIXELS_COLORS_8X8, 8) }
......
...@@ -20,7 +20,7 @@ object SkikoDispatchers { ...@@ -20,7 +20,7 @@ object SkikoDispatchers {
val Main: CoroutineDispatcher = NsQueueDispatcher(dispatch_get_main_queue()) val Main: CoroutineDispatcher = NsQueueDispatcher(dispatch_get_main_queue())
val IO: CoroutineDispatcher = NsQueueDispatcher( val IO: CoroutineDispatcher = NsQueueDispatcher(
dispatch_get_global_queue( dispatch_get_global_queue(
QOS_CLASS_BACKGROUND.toLong(), 0) QOS_CLASS_BACKGROUND.toLong(), 0UL)
) )
} }
...@@ -91,4 +91,3 @@ private class Timer : DisposableHandle { ...@@ -91,4 +91,3 @@ private class Timer : DisposableHandle {
CFRelease(timer) CFRelease(timer)
} }
} }
...@@ -22,7 +22,7 @@ class BasicIosTests { ...@@ -22,7 +22,7 @@ class BasicIosTests {
val f = fopen(path, "r") ?: throw Error("cannot open $path: ${strerror(errno)?.toKString()}") val f = fopen(path, "r") ?: throw Error("cannot open $path: ${strerror(errno)?.toKString()}")
val buf = ByteArray(200) val buf = ByteArray(200)
buf.usePinned { buf.usePinned {
val rv = fread(it.addressOf(0), 1, buf.size.toULong(), f) val rv = fread(it.addressOf(0), 1UL, buf.size.toULong(), f)
assertTrue { rv != 0UL } assertTrue { rv != 0UL }
} }
assertEquals("This is test data.\n", buf.toKString()) assertEquals("This is test data.\n", buf.toKString())
......
package org.jetbrains.skia package org.jetbrains.skia
actual typealias ExternalSymbolName = kotlin.js.JsName import org.jetbrains.skiko.WebImport
\ No newline at end of file
actual typealias ExternalSymbolName = WebImport
\ No newline at end of file
package org.jetbrains.skia.webext
import kotlinx.coroutines.await
import org.jetbrains.skiko.wasm.awaitSkiko
internal actual suspend fun getSkikoWasm(): SkikoWasm {
return awaitSkiko.await().unsafeCast<SkikoWasm>()
}
internal actual fun skikoArrayBuffer(skikoWasm: SkikoWasm): WebArrayBufferExt =
js("skikoWasm.wasmExports.memory.buffer")
internal actual fun copyBuffer(
src: WebArrayBufferExt,
dst: WebArrayBufferExt,
size: Int,
dstOffset: Int
) {
js("""
var dstView = new Uint8Array(dst);
var srcView = new Uint8Array(src);
dstView.set(srcView, dstOffset);
""")
}
\ No newline at end of file
package org.jetbrains.skiko package org.jetbrains.skiko
internal actual fun getNavigatorInfo(): String =
js("navigator.userAgentData ? navigator.userAgentData.platform : navigator.platform") as String
internal actual external val GL: GLInterface internal actual external val GL: GLInterface
package org.jetbrains.skiko package org.jetbrains.skiko
import kotlinx.browser.window
import kotlinx.coroutines.await
import org.khronos.webgl.Int8Array import org.khronos.webgl.Int8Array
actual suspend fun loadBytesFromPath(path: String): ByteArray { internal actual fun Int8Array.asByteArray(): ByteArray = this.unsafeCast<ByteArray>()
val arrayBuffer = window.fetch(path)
.await()
.arrayBuffer()
.await()
val byteArray = Int8Array(arrayBuffer)
return byteArray.unsafeCast<ByteArray>()
}
...@@ -2,7 +2,7 @@ package org.jetbrains.skiko.wasm ...@@ -2,7 +2,7 @@ package org.jetbrains.skiko.wasm
import org.jetbrains.skia.impl.NativePointer import org.jetbrains.skia.impl.NativePointer
import org.jetbrains.skiko.GL import org.jetbrains.skiko.GL
import org.jetbrains.skiko.w3c.HTMLCanvasElement import org.w3c.dom.HTMLCanvasElement
import kotlin.js.* import kotlin.js.*
/** /**
......
...@@ -5,4 +5,4 @@ package org.jetbrains.skiko.wasm ...@@ -5,4 +5,4 @@ package org.jetbrains.skiko.wasm
import kotlin.js.Promise import kotlin.js.Promise
internal external val awaitSkiko: Promise<Any> internal actual external val awaitSkiko: Promise<JsAny>
...@@ -5,7 +5,6 @@ import kotlinx.coroutines.await ...@@ -5,7 +5,6 @@ import kotlinx.coroutines.await
import kotlinx.coroutines.promise import kotlinx.coroutines.promise
import org.jetbrains.skiko.wasm.awaitSkiko import org.jetbrains.skiko.wasm.awaitSkiko
actual typealias TestReturnType = Any
/** /**
* Awaits for `wasmSetup` and then runs the [block] in a coroutine. * Awaits for `wasmSetup` and then runs the [block] in a coroutine.
*/ */
......
...@@ -35,7 +35,7 @@ actual suspend fun loadBytesFromPath(path: String): ByteArray { ...@@ -35,7 +35,7 @@ actual suspend fun loadBytesFromPath(path: String): ByteArray {
val bytes = ByteArray(size.toInt()) val bytes = ByteArray(size.toInt())
val result = bytes.usePinned { val result = bytes.usePinned {
fread(it.addressOf(0), 1, size.toULong(), file) fread(it.addressOf(0), 1UL, size.toULong(), file)
} }
fclose(file) fclose(file)
......
package org.jetbrains.skia package org.jetbrains.skia
actual typealias ExternalSymbolName = kotlin.js.JsName import org.jetbrains.skiko.WebImport
\ No newline at end of file
actual typealias ExternalSymbolName = WebImport
\ No newline at end of file
package org.jetbrains.skia.webext
import kotlinx.coroutines.await
import org.jetbrains.skiko.wasm.awaitSkiko
internal actual suspend fun getSkikoWasm(): SkikoWasm {
return awaitSkiko.await()
}
internal actual fun skikoArrayBuffer(skikoWasm: SkikoWasm): WebArrayBufferExt =
js("skikoWasm.wasmExports.memory.buffer")
internal actual fun copyBuffer(
src: WebArrayBufferExt,
dst: WebArrayBufferExt,
size: Int,
dstOffset: Int
) {
js("""
var dstView = new Uint8Array(dst);
var srcView = new Uint8Array(src);
dstView.set(srcView, dstOffset);
""")
}
\ No newline at end of file
package org.jetbrains.skiko
import org.w3c.dom.events.UIEvent
internal actual fun getNavigatorInfo(): String =
js("navigator.userAgentData ? navigator.userAgentData.platform : navigator.platform")
package org.jetbrains.skiko
import org.khronos.webgl.Int8Array
import org.khronos.webgl.get
internal actual fun Int8Array.asByteArray(): ByteArray = ByteArray(length) { index -> this[index] }
\ No newline at end of file
package org.jetbrains.skiko
import kotlin.js.*
import kotlinx.coroutines.asDeferred
@JsFun("(path) => window.fetch(path).then((r) => r.arrayBuffer()).then((b) => new Int8Array(b))")
private external fun loadResource(path: String): Promise<JsAny>
@JsFun("(array) => array.length")
private external fun arrayLength(array: JsAny): Int
@JsFun("(array, index) => array[index]")
private external fun arrayGet(array: JsAny, index: Int): Byte
actual suspend fun loadBytesFromPath(path: String): ByteArray {
val resourceArray = loadResource(path).asDeferred<JsAny>().await()
return ByteArray(arrayLength(resourceArray)) {
arrayGet(resourceArray, it)
}
}
package org.jetbrains.skiko.wasm package org.jetbrains.skiko.wasm
import kotlinx.coroutines.await
import org.jetbrains.skia.impl.NativePointer import org.jetbrains.skia.impl.NativePointer
import org.jetbrains.skiko.GL import org.jetbrains.skiko.GL
import org.jetbrains.skiko.w3c.HTMLCanvasElement import org.w3c.dom.HTMLCanvasElement
import org.khronos.webgl.ArrayBuffer
@JsFun( @JsFun(
"""() => { """() => {
......
...@@ -3,4 +3,4 @@ package org.jetbrains.skiko.wasm ...@@ -3,4 +3,4 @@ package org.jetbrains.skiko.wasm
import kotlin.js.Promise import kotlin.js.Promise
internal external val awaitSkiko: Promise<JsAny?> internal actual external val awaitSkiko: Promise<JsAny>
package org.jetbrains.skiko.tests package org.jetbrains.skiko.tests
import kotlinx.coroutines.MainScope import kotlinx.coroutines.MainScope
import kotlinx.coroutines.await
import kotlinx.coroutines.promise import kotlinx.coroutines.promise
import org.jetbrains.skiko.wasm.awaitSkiko
actual annotation class SkipJsTarget actual annotation class SkipJsTarget
...@@ -9,13 +11,11 @@ actual typealias SkipWasmTarget = kotlin.test.Ignore ...@@ -9,13 +11,11 @@ actual typealias SkipWasmTarget = kotlin.test.Ignore
@JsFun("() => ''") @JsFun("() => ''")
private external fun jsRef(): JsAny private external fun jsRef(): JsAny
actual typealias TestReturnType = Any
/** /**
* Runs the [block] in a coroutine. * Runs the [block] in a coroutine.
*/ */
actual fun runTest(block: suspend () -> Unit): TestReturnType = MainScope().promise { actual fun runTest(block: suspend () -> Unit): TestReturnType = MainScope().promise {
awaitSkiko.await<Any>()
block() block()
jsRef() jsRef()
} }
package org.jetbrains.skia package org.jetbrains.skia
import org.jetbrains.skiko.w3c.window import kotlinx.browser.window
internal actual fun <R> commonSynchronized(lock: Any, block: () -> R) { internal actual fun <R> commonSynchronized(lock: Any, block: () -> R) {
block() block()
...@@ -33,4 +33,4 @@ val LANG: String by lazy { ...@@ -33,4 +33,4 @@ val LANG: String by lazy {
actual fun defaultLanguageTag(): String = LANG actual fun defaultLanguageTag(): String = LANG
actual fun compilePattern(regex: String): Pattern = Pattern(regex) actual fun compilePattern(regex: String): Pattern = Pattern(regex)
\ No newline at end of file
...@@ -6,30 +6,34 @@ import org.jetbrains.skia.impl.Native ...@@ -6,30 +6,34 @@ import org.jetbrains.skia.impl.Native
import org.jetbrains.skia.impl.NativePointer import org.jetbrains.skia.impl.NativePointer
import org.jetbrains.skia.impl._malloc import org.jetbrains.skia.impl._malloc
import org.jetbrains.skiko.ExperimentalSkikoApi import org.jetbrains.skiko.ExperimentalSkikoApi
import org.jetbrains.skiko.await
import org.jetbrains.skiko.wasm.awaitSkiko
import org.khronos.webgl.ArrayBuffer
import kotlin.js.JsAny
import kotlin.js.js
internal expect fun skikoArrayBuffer(skikoWasm: SkikoWasm): WebArrayBufferExt internal fun skikoArrayBuffer(skikoWasm: JsAny): ArrayBuffer =
js("skikoWasm.wasmExports.memory.buffer")
/**
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer
*/
@ExperimentalSkikoApi
external interface WebArrayBufferExt {
val byteLength: Int
operator fun set(ix: Int, value: Byte)
}
internal external interface SkikoWasm
internal suspend fun copyBufferToSkiko( internal suspend fun copyBufferToSkiko(
src: WebArrayBufferExt src: ArrayBuffer
): NativePointer { ): NativePointer {
val ptr = _malloc(src.byteLength) val ptr = _malloc(src.byteLength)
if (ptr != Native.NullPointer) { if (ptr != Native.NullPointer) {
val skikoArrayBuffer = skikoArrayBuffer(getSkikoWasm()) val skikoArrayBuffer = skikoArrayBuffer(awaitSkiko.await())
copyBuffer(src, skikoArrayBuffer, src.byteLength, ptr) copyBuffer(src, skikoArrayBuffer, ptr)
} }
return ptr return ptr
} }
internal expect suspend fun getSkikoWasm(): SkikoWasm internal fun copyBuffer(
src: ArrayBuffer,
internal expect fun copyBuffer(src: WebArrayBufferExt, dst: WebArrayBufferExt, size: Int, dstOffset: Int) dst: ArrayBuffer,
\ No newline at end of file dstOffset: Int
) {
js("""
var dstView = new Uint8Array(dst);
var srcView = new Uint8Array(src);
dstView.set(srcView, dstOffset);
""")
}
\ No newline at end of file
...@@ -7,11 +7,12 @@ import org.jetbrains.skia.impl.Native ...@@ -7,11 +7,12 @@ import org.jetbrains.skia.impl.Native
import org.jetbrains.skia.impl.NativePointer import org.jetbrains.skia.impl.NativePointer
import org.jetbrains.skia.impl.getPtr import org.jetbrains.skia.impl.getPtr
import org.jetbrains.skiko.ExperimentalSkikoApi import org.jetbrains.skiko.ExperimentalSkikoApi
import org.khronos.webgl.ArrayBuffer
@ExperimentalSkikoApi @ExperimentalSkikoApi
suspend fun Bitmap.installPixelsFromArrayBuffer( suspend fun Bitmap.installPixelsFromArrayBuffer(
info: ImageInfo, info: ImageInfo,
pixelsArrayBuffer: WebArrayBufferExt, pixelsArrayBuffer: ArrayBuffer,
rowBytes: Int rowBytes: Int
): Boolean { ): Boolean {
val pixelsPtr = copyBufferToSkiko(pixelsArrayBuffer) val pixelsPtr = copyBufferToSkiko(pixelsArrayBuffer)
......
package org.jetbrains.skiko package org.jetbrains.skiko
import org.jetbrains.skiko.w3c.window import kotlinx.browser.window
import org.w3c.dom.HTMLElement
internal actual inline fun <R> maybeSynchronized(lock: Any, block: () -> R): R = internal actual inline fun <R> maybeSynchronized(lock: Any, block: () -> R): R =
block() block()
...@@ -13,4 +14,6 @@ internal actual fun loadOpenGLLibrary() { ...@@ -13,4 +14,6 @@ internal actual fun loadOpenGLLibrary() {
internal actual fun loadAngleLibrary() { internal actual fun loadAngleLibrary() {
// Nothing to do here // Nothing to do here
} }
\ No newline at end of file
annotation class WebImport(val name : String)
package org.jetbrains.skiko package org.jetbrains.skiko
import kotlinx.browser.window
import org.jetbrains.skia.* import org.jetbrains.skia.*
import org.jetbrains.skia.impl.NativePointer import org.jetbrains.skia.impl.NativePointer
import org.jetbrains.skiko.w3c.HTMLCanvasElement
import org.jetbrains.skiko.w3c.window
import org.jetbrains.skiko.wasm.ContextAttributes import org.jetbrains.skiko.wasm.ContextAttributes
import org.w3c.dom.HTMLCanvasElement
/** /**
* CanvasRenderer takes an [HTMLCanvasElement] instance and initializes * CanvasRenderer takes an [HTMLCanvasElement] instance and initializes
......
package org.jetbrains.skiko package org.jetbrains.skiko
import org.jetbrains.skiko.w3c.window import kotlinx.browser.window
import kotlin.js.js
actual val hostOs: OS = detectHostOs() actual val hostOs: OS = detectHostOs()
...@@ -22,7 +23,9 @@ actual val kotlinBackend: KotlinBackend ...@@ -22,7 +23,9 @@ actual val kotlinBackend: KotlinBackend
* See https://developer.mozilla.org/en-US/docs/Web/API/NavigatorUAData/platform - new API, * See https://developer.mozilla.org/en-US/docs/Web/API/NavigatorUAData/platform - new API,
* but not supported in all browsers * but not supported in all browsers
*/ */
internal expect fun getNavigatorInfo(): String internal fun getNavigatorInfo(): String =
js("navigator.userAgentData ? navigator.userAgentData.platform : navigator.platform")
/** /**
* In a browser, user platform can be obtained from different places: * In a browser, user platform can be obtained from different places:
......
package org.jetbrains.skiko
import kotlinx.browser.window
import org.khronos.webgl.ArrayBuffer
import org.khronos.webgl.Int8Array
import org.w3c.fetch.RequestInit
import org.w3c.fetch.Response
import kotlin.js.ExperimentalWasmJsInterop
import kotlin.js.js
internal expect fun Int8Array.asByteArray(): ByteArray
// Without request init passed explicitly, js target fails
// this looks like a bug in kotlinx.browser definitions
// TODO: to discuss with the Kotlin team
private val defaultFetchInit: RequestInit = js("{}")
@OptIn(ExperimentalWasmJsInterop::class)
actual suspend fun loadBytesFromPath(path: String): ByteArray {
val arrayBuffer = window.fetch(path, defaultFetchInit)
.await<Response>()
.arrayBuffer()
.await<ArrayBuffer>()
return Int8Array(arrayBuffer).asByteArray()
}
\ No newline at end of file
package org.jetbrains.skiko package org.jetbrains.skiko
import kotlinx.browser.window
import org.jetbrains.skia.Canvas import org.jetbrains.skia.Canvas
import org.jetbrains.skia.Color import org.jetbrains.skia.Color
import org.jetbrains.skia.PixelGeometry import org.jetbrains.skia.PixelGeometry
import org.jetbrains.skiko.w3c.HTMLCanvasElement
import org.jetbrains.skiko.w3c.window
import org.jetbrains.skiko.wasm.createWebGLContext import org.jetbrains.skiko.wasm.createWebGLContext
import org.w3c.dom.HTMLCanvasElement
/** /**
* Provides a way to render the content and to receive the input events. * Provides a way to render the content and to receive the input events.
......
package org.jetbrains.skiko
import kotlinx.coroutines.CancellableContinuation
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlin.js.ExperimentalWasmJsInterop
import kotlin.js.JsAny
import kotlin.js.Promise
import kotlin.js.js
private fun jsThrow(e: JsAny) {
js("throw e;")
}
@JsFun("function (f) { try { f(); } catch (e) { return e;}; return null; }")
private external fun jsCatch(f: () -> Unit): JsAny?
/**
* For a Dynamic value caught in JS, returns the corresponding [Throwable]
* if it was thrown from Kotlin, or null otherwise.
*/
@ExperimentalWasmJsInterop
private fun JsAny.toThrowableOrNull(): Throwable? {
val thisAny: Any = this
if (thisAny is Throwable) return thisAny
var result: Throwable? = null
jsCatch {
try {
jsThrow(this)
} catch (e: Throwable) {
result = e
}
}
return result
}
@Suppress("UNCHECKED_CAST")
@OptIn(ExperimentalWasmJsInterop::class)
internal suspend fun <T> Promise<JsAny?>.await(): T = suspendCancellableCoroutine { cont: CancellableContinuation<T> ->
this@await.then(
onFulfilled = { cont.resume(it as T); null },
onRejected = { cont.resumeWithException(it.toThrowableOrNull() ?: error("Unexpected non-Kotlin exception $it")); null }
)
}
\ No newline at end of file
package org.jetbrains.skiko.w3c
/**
* The purpose of this file is to facilitate shared W3C type declarations
* for use in both kotlin/js and kotlin/wasm code.
* Given that the Kotlin standard library does not provide common (shared between kotlin/js and kotlin/wasm) W3C API,
* this file serves to fill that void with custom partial declarations.
*
* These declarations do not cover all attributes and methods of the respective W3C types,
* but only those needed by our implementations.
*/
@Suppress("ClassName")
internal external object window {
val devicePixelRatio: Double
val navigator: Navigator
val performance: Performance
fun requestAnimationFrame(block: (Double) -> Unit)
fun open(url: String?, target: String?)
}
internal external interface Performance {
fun now(): Double
}
internal external interface Navigator {
val language: String
val clipboard: Clipboard
val userAgent: String
}
internal external interface Clipboard {
fun writeText(text: String)
}
internal abstract external class HTMLCanvasElement {
internal var width: Int
internal var height: Int
internal val style: CSSStyleDeclaration
}
internal abstract external class CSSStyleDeclaration {
var width: String
var height: String
}
\ No newline at end of file
package org.jetbrains.skiko.wasm package org.jetbrains.skiko.wasm
import org.jetbrains.skia.impl.NativePointer import org.jetbrains.skia.impl.NativePointer
import org.jetbrains.skiko.w3c.HTMLCanvasElement import org.w3c.dom.HTMLCanvasElement
import kotlin.js.JsAny
import kotlin.js.Promise
internal external interface ContextAttributes { internal external interface ContextAttributes {
val alpha: Int? val alpha: Int?
...@@ -20,4 +22,6 @@ internal external interface ContextAttributes { ...@@ -20,4 +22,6 @@ internal external interface ContextAttributes {
internal expect fun createWebGLContext(canvas: HTMLCanvasElement, attr: ContextAttributes? = null): NativePointer internal expect fun createWebGLContext(canvas: HTMLCanvasElement, attr: ContextAttributes? = null): NativePointer
internal expect fun onWasmReady(onReady: () -> Unit) internal expect fun onWasmReady(onReady: () -> Unit)
\ No newline at end of file
internal expect val awaitSkiko: Promise<JsAny>
\ No newline at end of file
// This file is merged with skiko.mjs by emcc // This file is merged with skiko.mjs by emcc
const loadedWasm = { export const loadedWasm = {
_: {} _: {}
} }
......
...@@ -6,6 +6,13 @@ const loadedWasm = { ...@@ -6,6 +6,13 @@ const loadedWasm = {
awaitSkiko.then((module) => { awaitSkiko.then((module) => {
loadedWasm._ = module.wasmExports; loadedWasm._ = module.wasmExports;
for (let exportName of Object.keys(module.wasmExports)) {
if (exportName.startsWith("org_jetbrains_skiko_tests_")) {
window[exportName] = module.wasmExports[exportName]
}
}
return module return module
}); });
......
...@@ -8,6 +8,7 @@ import org.jetbrains.skia.impl.NativePointer ...@@ -8,6 +8,7 @@ import org.jetbrains.skia.impl.NativePointer
import org.jetbrains.skia.impl._free import org.jetbrains.skia.impl._free
import org.jetbrains.skia.impl.skia_memGetByte import org.jetbrains.skia.impl.skia_memGetByte
import org.jetbrains.skiko.tests.runTest import org.jetbrains.skiko.tests.runTest
import org.khronos.webgl.Int8Array
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.time.measureTimedValue import kotlin.time.measureTimedValue
...@@ -17,10 +18,10 @@ class WebExtTest { ...@@ -17,10 +18,10 @@ class WebExtTest {
@Test @Test
fun canCopyArrayBufferToSkikoMemory() = runTest { fun canCopyArrayBufferToSkikoMemory() = runTest {
val bytes = ByteArray(16_000_000) { (it and 0xFF).toByte() } val bytes = ByteArray(16_000_000) { (it and 0xFF).toByte() }
val ba = createInt8ArrayToCopy(bytes.size) val ba = Int8Array(bytes.size)
for (i in bytes.indices) ba[i] = bytes[i] for (i in bytes.indices) ba[i] = bytes[i]
val ptr = measureTimedValue<NativePointer> { copyBufferToSkiko(ba.buffer) } val ptr = measureTimedValue { copyBufferToSkiko(ba.buffer) }
println("copyBufferToSkiko took ${ptr.duration}\n") println("copyBufferToSkiko took ${ptr.duration}\n")
// Sanity check // Sanity check
...@@ -50,7 +51,7 @@ class WebExtTest { ...@@ -50,7 +51,7 @@ class WebExtTest {
} }
} }
val ba = createInt8ArrayToCopy(bytes.size) val ba = Int8Array(bytes.size)
for (i in bytes.indices) ba[i] = bytes[i] for (i in bytes.indices) ba[i] = bytes[i]
val info = ImageInfo(width, height, ColorType.RGBA_8888, ColorAlphaType.PREMUL) val info = ImageInfo(width, height, ColorType.RGBA_8888, ColorAlphaType.PREMUL)
...@@ -80,17 +81,10 @@ class WebExtTest { ...@@ -80,17 +81,10 @@ class WebExtTest {
} }
} }
private fun createInt8ArrayToCopy(length: Int): Int8ArrayInternal = private operator fun Int8Array.set(index: Int, value: Byte) {
js("new Int8Array(length)")
private external interface Int8ArrayInternal {
val buffer: WebArrayBufferExt
}
private operator fun Int8ArrayInternal.set(index: Int, value: Byte) {
int8ArraySet(this, index, value) int8ArraySet(this, index, value)
} }
private fun int8ArraySet(arrayInternal: Int8ArrayInternal, index: Int, value: Byte) { private fun int8ArraySet(arrayInternal: Int8Array, index: Int, value: Byte) {
js("arrayInternal[index] = value;") js("arrayInternal[index] = value;")
} }
\ No newline at end of file
...@@ -14,4 +14,6 @@ actual annotation class SkipNativeTarget ...@@ -14,4 +14,6 @@ actual annotation class SkipNativeTarget
actual fun makeFromFileName(path: String?): Data = Data(0) actual fun makeFromFileName(path: String?): Data = Data(0)
actual val isDebugModeOnJvm: Boolean = false actual val isDebugModeOnJvm: Boolean = false
\ No newline at end of file
actual typealias TestReturnType = Any
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment