Puede que el verano ya esté terminando, pero nuestro equipo de Kotlin/Native se ha pegado una buena panzada preparando Kotlin/Native 0.9... ¡y aquí está! Que no os engañe la numeración: esta es una versión mayor (y rompe cosas) del compilador, el plugin de Gradle y el plugin del IDE.

Las cosas más destacadas de esta versión son las siguientes:

  • Migrado a Kotlin 1.3-M2 (el compilador y la librería estándar)
  • Soporte para tipos sin signo en la librería estándar de Kotlin
  • Soporte para tipos sin signo en la capa de interoperabilidad con C/Objective-C/Swift
  • Soporte para la versión estable de las corrutinas de kotlin
  • Rediseñado de las primitivas de concurrencia
  • El paquete kotlin.native

Soporte para Kotlin 1.3-M2

Para dar soporte a la próxima versión de Kotlin 1.3, Kotlin/Native v0.9 ya está basado en el compilador y la librería estándar de Kotlin 1.3-M2. Esto significa que podéis usar todas las cosas molonas del lenguaje, como las clases inline, los tipos sin signo, o el API de generación de números aleatorios comóncomún, que están ahora disponibles en todos los targets nativos.

Soporte para tipos sin signo

Kotlin 1.3 trae soporte para tipos sin signo tanto en el lenguaje como en la librería estándar, en todos los backends: JVM, Nativo y JS. Por ejemplo, el siguiente código:

fun main(args: Array<String>) {
    println(maxOf(1u, UInt.MAX_VALUE))
}

funciona de forma correcta en todas las versiones de Kotlin, imprimiendo por pantalla 4294967295.
Y hemos extendido muchas de las operaciones populares de la librería estándar para añadir soporte para los operandos sin signo, de forma que se pueden usar de base sin dependencias adicionales.

Tipos sin signo e interoperabilidad

Y resulta que no solo ofrecemos soporte para tipos sin signo en el lenguaje en sí mismo, sino que también soportamos los tipos sin signo correspondientes de C, Objective-C/Swift, lo que quiere decir que la capa de interoperabilidad es aún mejor en cuanto a que casan correctamente.

Por ejemplo, echemos un vistazo el siguiente código de red, que lee datos de un socket a un ByteArray de Kotlin.

buffer.usePinned { pinned ->
    while (true) {
        val length = recv(commFd, pinned.addressOf(0), buffer.size.convert(), 0).toInt()
        .ensureUnixCallResult("read") { it >= 0 }
        if (length == 0) break
        send(commFd, pinned.addressOf(0), length.convert(), 0)
            .ensureUnixCallResult("send") { it >= 0 }
    }
}

Nótese que en ese código hay conversiones potencialmente inseguras entre tipos con signo y sin signo (marcadas con el método de extensión intrínseco convert()), y el API POSIX se convierte de forma correcta a la firma de Kotlin:

typealias ssize_t = Long
typealias size_t = ULong
...
fun recv(arg0: Int, arg1: CValuesRef<*>?, arg2: size_t, arg3: Int): ssize_t

Soporte para corrutinas estables

Como las corrutinas ya no son experimentales y se ofrecen en Kotlin 1.3 como listas para producción, ahora soportamos el nuevo API de corrutinas en Kotlin/Native v0.9. Ya no soportamos las corrutinas experimentales del paquete kotlin.coroutines.experimental ni en el compilador ni en el tiempo de ejecución.

Primitivas de concurrencia rediseñadas

Kotlin/Native v0.8 introdujo la congelación (freezing) de objetos singleton, permitiendo un estado global compartido confiable a través de múltiples ejecutores concurrentes. Con la v0.9, hemos rediseñado todas las primitivas de concurrencia y las hemos movido al nuevo pataquete kotlin.native.concurrent. Entre los cambios más importantes:

  • Las clases AtomicInt, AtomicLong, AtomicNativePtr y AtomicReference han tenido un potente lavado de cara
  • El delegado lazy se ha mejorado para funcionar con objetos congelados (frozen)
  • Hay una nueva clase DetachedObjectGraph que representa el concepto de subgrafo separado

Por ejemplo, el siguiente código:

dispatch_async_f(asyncQueue, DetachedObjectGraph {
    Data(clock_gettime_nsec_np(CLOCK_REALTIME))
}.asCPointer(), staticCFunction {
    it ->
    initRuntimeIfNeeded()
    val data = DetachedObjectGraph<Data>(it).attach()
    println("in async: $data")
})

puede pasar la pertenencia del objeto mutable Data a otra cola asíncrona en macOS.

Otro aspecto importante del soporte de concurrencia es que anteriormente, las variables globales eran thread-local, y ahora únicamente se puede acceder a ellas desde el thread principal de la aplicación por defecto, mientras que tratar de acceder a ellas desde otro thread lleva a una excepción en el runtime. Para controlar este comportamiento, hemos introducido un par de nuevas anotaciones: @ThreadLocal y @SharedImmutable, de forma que las variables globales marcadas con dichas anotaciones son diferentes por thread, o congeladas al inicializarse respectivamente

El paquete kotlin.native

Para asegurar una mejor estructura de la librería estándar y soportar el escribir código multiplataforma, hemos movido todo el código desde el paquete konan al paquete kotlin.native, y las interfaces se han rediseñado y limpiado para alinearse mejor con el paradigma multiplataforma de Kotlin. Algunas operaciones específicas de Native que tienen sus contrapartidas en la librería estándar común se han hecho internas o privadas, para promover el uso portable de las operaciones estándar.

Para usarlo

Echad un vistazo a la página de releases de GitHub para más detalles y para descargar los binarios de macOS, Linux y Windows. También hay disponible un paquete de Linux Snap.

Y por supuesto el plugin de gradle: https://plugins.gradle.org/plugin/org.jetbrains.kotlin.konan