Kotlin logo

The Kotlin Blog

Kotlin Programming Language by JetBrains

Releases

Lanzamiento de Kotlin 1.4.20

Kotlin 1.4.20 ha llegado con nuevas funcionalidades experimentales que ya puede probar. Uno de los principios básicos del equipo de Kotlin es estar abiertos a los comentarios de la comunidad, y nos gusta contar con su opinión sobre los prototipos de las nuevas funcionalidades. Pruébelas y envíenos sus comentarios a Slack (obtenga una invitación aquí o en YouTrack).

Kotlin 1.4.20

Estos son algunos de los puntos más destacados:

  • Compatibilidad con nuevas funcionalidades de JVM, como concatenación de cadenas mediante invokedynamic.
  • Rendimiento mejorado y gestión de excepciones para proyectos KMM.
  • Extensiones para la ruta JDK: Path(“dir”) / “file.txt”.

También estamos incluyendo numerosas soluciones y mejoras para funcionalidades existentes, incluidas las añadidas en 1.4.0. Así que, si tuvo algún problema con alguna de esas funcionalidades, ahora es un buen momento para darles otra oportunidad.

Continúe leyendo para obtener más información acerca de las funcionalidades de Kotlin 1.4.20. También encontrará un breve resumen sobre esta versión en la página Novedades de Kotlin 1.4.20 en Kotlin docs. Puede encontrar la lista completa de cambios en el registro de cambios.

Como siempre, nos gustaría agradecer a nuestros colaboradores externos que nos han ayudado con este lanzamiento.

Ahora ¡entremos en materia!

Kotlin/JVM

En la JVM hemos añadido el nuevo destino JVM 15, y nos hemos centrado principalmente en mejorar la funcionalidad y el rendimiento existentes, así como en solucionar errores.

Concatenación de cadenas invokedynamic

Desde Java 9, la concatenación de cadenas en JVM se había realizado mediante la invocación de métodos dinámicos (la instrucción invokedynamic en el bytecode). Esto funciona más rápido y consume menos memoria que la implementación anterior, y deja espacio a futuras optimizaciones sin necesitar cambios en el bytecode.

Hemos comenzado a implementar este mecanismo en Kotlin para un mejor rendimiento, y ahora puede copilar concatenaciones de cadenas en invocaciones dinámicas en destinos JVM 9+.

Actualmente, esta funcionalidad es experimental, y cubre los casos siguientes:

  • String.plus en el operador (a + b), (a.plus(b)) explícito y referencias ((a::plus)(b)).
  • toString en clases de datos inline.
  • Plantillas de cadenas, excepto para aquellas con un argumento individual no constante (véase KT-42457).

Para permitir la concatenación de cadenas invokedynamic, añada la opción del compilador -Xstring-concat con uno de los valores siguientes:

  • indy-with-constants para realizar concatenaciones invokedynamic en cadenas con StringConcatFactory.makeConcatWithConstants() (se planea que esta función pase a ser predeterminada para destinos JVM 9 y posteriores a partir de 1.5).
  • indy para realizar concatenaciones invokedynamic en cadenas con StringConcatFactory.makeConcat().
  • inline para volver a la concatenación clásica vía StringBuilder.append().

Kotlin/JS

Kotlin/JS continúa evolucionando rápidamente, y esta versión incorpora diversas mejoras, incluyendo nuevas plantillas para su asistente de proyectos, DSL mejorado para un mejor control de la configuración del proyecto, y muchas más. El nuevo compilador de IR también cuenta ahora con un modo totalmente nuevo de compilar proyectos con errores.

Cambios en el DSL de Gradle

El Gradle DSL de Kotlin/JS ha experimentado numerosas actualizaciones que simplifican la configuración y personalización de los proyectos, entre las que se incluyen ajustes de configuración de webpack, modificaciones en el archivo autogenerado package.json, y un control mejorado de las dependencias transitivas.

Un punto único para la configuración de webpack

Kotlin 1.4.20 incorpora un nuevo bloque de configuración para el destino browser denominado commonWebpackConfig. Desde él puede ajustar configuraciones habituales desde un único punto, en lugar de duplicar configuraciones para webpackTask, runTask y testTask.

Para permitir la compatibilidad con CSS de forma predeterminada para las tres tareas, tiene que incluir el fragmento siguiente en el build.gradle(.kts) de su proyecto:

Personalización de package.json desde Gradle

El archivo package.json suele definir cómo debería comportarse un proyecto JavaScript, identificando scripts disponibles para su ejecución, dependencias, etc. Para los proyectos Kotlin/JS se genera automáticamente en el momento de su creación. Como el contenido de package.json varía de un caso a otro, nos han pedido mucho un modo fácil de personalizar este archivo.

A partir de Kotlin 1.4.20, puede añadir entradas al archivo de proyecto package.json a partir del script de creación de Gradle. Para añadir campos personalizados a su package.json, utilice la función customField en el bloque de compilaciones packageJson:

Cuando cree el proyecto, esto añadirá el siguiente bloque al archivo de configuración build/js/packages/projectName/package.json:

"hello": {
  "one": 1,
  "two": 2
}

Tanto si desea añadir un campo de scripts a la configuración para facilitar la ejecución de su proyecto desde la línea de comandos, como si quiere incluir información para otras herramientas de posprocesamiento, esperamos que este nuevo modo de especificar campos personalizados le resulte útil.

Resoluciones de dependencias de Yarn selectivas (experimental)

Al incluir dependencias desde npm, en ocasiones desea tener un control más preciso de sus dependencias (dependencias transitivas). Existen numerosos motivos por los que podría darse este caso. Quizá desee aplicar un mejora importante a una de las dependencias de una biblioteca que está utilizando. O puede que quiera retirar una actualización de una dependencia transitiva que actualmente estropea su aplicación. Las resoluciones de dependencias selectivas de Yarn le permiten saltarse las dependencias especificadas por el autor original, para que pueda continuar desarrollando.

Con Kotlin 1.4.20, ofrecemos un modo preliminar (experimental) de configurar esta funcionalidad a partir de un script de compilación de un proyecto de Gradle. Mientras seguimos trabajando en una perfecta integración API con el resto de las opciones de Kotlin/JS, ya puede utilizar la funcionalidad a través de la YarnRootExtension que encontrará en el YarnPlugin. Para incidir en la versión resuelta de un paquete para su proyecto, utilice la función resolution. En sus argumentos, especifique el selector de nombre de paquete (como indica Yarn) y la versión deseada.

Una configuración de ejemplo para la resolución de dependencias selectiva en su archivo build.gradle.kts sería así:

Aquí, todas sus dependencias de npm que requieren react recibirán la versión 16.0.0, y processor recibirá su dependencia decamelize como versión 3.0.0. Además, también puede pasar las invocaciones include y exclude al bloque resolution, lo que le permitirá especificar restricciones acerca de versiones aceptables.

Deshabilitar espacios de trabajo dispersos (experimental)

Para abreviar los tiempos de creación, el complemento de Gradle para Kotlin/JS solo instala dependencias necesarias para una tarea en particular de Gradle. Por ejemplo, el paquete webpack-dev-server solo se instala cuando ejecuta una de las tareas *Run, y no cuando ejecuta la tarea assemble. Aunque esto significa que se evitan descargas innecesarias, puede crear problemas al ejecutar varios procesos de Gradle en paralelo. Cuando los requisitos de dependencias chocan, las dos instalaciones de paquetes npm pueden causar errores.

Para resolver este problema, Kotlin 1.4.20 incluye una nueva opción (experimental) para deshabilitar los denominados espacios de trabajo dispersos. Como la compatibilidad experimental con resoluciones de dependencias selectivas, esta funcionalidad también está accesible actualmente a través de YarnRootExtension, pero probablemente se integrará mejor con el resto de DSL de Gradle para Kotlin/JS. Para utilizarla, añada el fragmento siguiente a su archivo build.gradle.kts:

Con esta configuración, el complemento de Gradle para Kotlin/JS instalará todas las dependencias de npm que podría utilizar su proyecto, incluyendo las utilizadas por tareas que no se estén ejecutando actualmente. Esto significa que el primer build de Gradle podría tardar un poco más, pero las dependencias descargadas estarían actualizadas para todas las tareas que ejecute. De este modo, puede evitar conflictos al ejecutar varios procesos de Gradle en paralelo.

Nuevas plantillas del asistente

Para que disfrute de modos más prácticos de personalizar su proyecto durante la creación, el asistente de proyectos de Kotlin incluye nuevas plantillas adaptables para aplicaciones Kotlin/JS. Cuenta con plantillas tanto para el navegador como para entornos de ejecución Node.js. Son un buen punto de partida para su proyecto, y permiten ajustar con precisión la configuración inicial. Esto incluye ajustes como habilitar el nuevo compilador de IR o configurar compatibilidad con bibliotecas adicionales.

Con Kotlin 1.4.20 dispone de tres plantillas:

  • Browser Application le permite configurar un proyecto básico de Gradle para Kotlin/JS que se ejecuta en el navegador.
  • React Application contiene todo lo que necesita para comenzar a crear una aplicación de React utilizando los kotlin-wrappers adecuados. Le ofrece opciones para permitir integraciones para hojas de estilo, componentes de navegación y contenedores de estados.
  • Node.js Application preconfigura su proyecto para ejecutarlo en una ejecución Node.js. Incorpora la opción de incluir directamente el paquete experimental kotlinx-nodejs, del que ya hablamos en un artículo anterior.

Ignorar errores de compilación (experimental)

Con Kotlin 1.4.20 también estamos encantados de presentar una funcionalidad totalmente nueva disponible en el compilador IR de Kotlin/JSignorar errores de compilación. Esta funcionalidad le permite probar su aplicación incluso cuando se encuentra en un estado en el que normalmente no se compilaría. Por ejemplo, cuando está realizando una refactorización compleja o cuando trabaja en una parte del sistema que no tiene nada que ver con un error de compilación. Con este nuevo modo, el compilador hace caso omiso de cualquier código erróneo y lo sustituye con excepciones de ejecución en lugar de impedir la compilación.

Kotlin 1.4.20 cuenta con dos políticas de tolerancia para ignorar los errores de compilación en su código:

  • En modo SEMANTIC, el compilador aceptará código sintácticamente correcto que no tenga sentido semánticamente. Un ejemplo de esto sería una instrucción que contuviese un desajuste de tipo (como val x: String = 3).
  • En modo SYNTAX, el compilador aceptará cualquier código, incluso aunque contenga errores de sintaxis. Independientemente de lo que escriba, el compilador tratará de generar un ejecutable que funcione.

Como funcionalidad experimental, ignorar errores de compilación requiere una elección a través de una opción del compilador. Solo está disponible en compilador de IR Kotlin/JS. Para habilitarla, añada el fragmento siguiente a su archivo build.gradle.kts:

Esperamos que la compilación con errores le ayude a reforzar bucles de feedback y aumente su velocidad de iteración al trabajar en proyectos Kotlin/JS. Estamos deseando recibir sus comentarios, así como cualquier incidencia que encuentre mientras prueba esta funcionalidad, en nuestro YouTrack.

Mientras continuamos refinando la implementación de esta funcionalidad, también ofrecemos una integración más profunda de ella con del DSL de Gradle para Kotlin/JS y sus tareas más adelante.

Kotlin/Native

El rendimiento continúa siendo una de las principales prioridades de Kotlin/Native en 1.4.20. Una funcionalidad clave en esta área es un prototipo del nuevo mecanismo de análisis de escape que planeamos pulir y mejorar en futuros lanzamientos. Y, por supuesto, también hay mejoras de rendimiento menores, como comprobaciones de rango más rápidas (in).

Otro aspecto de las mejoras en el desarrollo en Kotlin/Native en 1.4.20 es el perfeccionamiento y la reparación de errores. Hemos abordado una gran cantidad de antiguas incidencias, así como las encontradas en las funcionalidades del nuevo 1.4, por ejemplo el mecanismo para compartir código. Un conjunto de mejoras soluciona las incoherencias de comportamiento entre Kotlin/Native y Kotlin/JVM en casos especiales, como la inicialización de propiedades o el modo en que funcionan equals y hashCode en las referencias funcionales.

Por último, hemos ampliado las capacidades de interoperabilidad de Objective-C con una opción de empaquetar excepciones de Objective-C en excepciones de Kotlin, permitiendo gestionarlas en el código Kotlin.

Análisis de escape

Escape analysis o análisis de escape es una técnica que utiliza el compilador para decidir si un objeto puede asignarse a la pila o debería "escapar" al montón. La asignación en las pilas es mucho más rápida y no requiere recogida de basura en el futuro.

Aunque Kotlin/Native ya contaba con análisis de escape, ahora estamos introduciendo una implementación de prototipos de un nuevo análisis de escape global más eficiente. Se efectúa en una fase de compilación por separado para los builds de lanzamiento (con la opción de compilador -opt).

Este prototipo ya ha logrado algunos resultados prometedores, como un 10 % de aumento medio del rendimiento con respecto a nuestros puntos de referencia. Estamos buscando modos de optimizar el algoritmo de modo que encuentre más objetos para la asignación de pilas y acelere el programa todavía más.

Mientras seguimos trabajando en el prototipo, puede ayudarnos en gran medida probándolo y compartiendo los resultados que obtiene en sus proyectos.

Si desea deshabilitar la fase de análisis de escape, utilice la opción del compilador -Xdisable-phases=EscapeAnalysis.

Elección de empaquetar las excepciones de Objective-C

El objetivo de las excepciones en Objective-C es muy distinto del de las de Kotlin. Su uso normalmente se limita a buscar errores durante el desarrollo. Pero técnicamente, las bibliotecas de Objective-C pueden lanzar excepciones en ejecución. Anteriormente no existía la opción de gestionar esas excepciones en Kotlin/Native, y encontrarse con una NSException lanzada desde una biblioteca daba lugar a la finalización del programa de Kotlin/Native al completo.

En 1.4.20, hemos añadido una opción para gestionar esas excepciones en ejecución para evitar caídas del programa. Puede elegir empaquetar excepciones NSException en excepciones ForeignException de Kotlin para continuar gestionándolas en el código Kotlin. Una ForeignException así mantiene la referencia a la NSException original, lo que le permite obtener información acerca de la causa raíz.

Para habilitar el empaquetado de excepciones de Objective-C, especifique la opción -Xforeign-exception-mode objc-wrap en la llamada a cinterop o añada la propiedad foreignExceptionMode = objc-wrap al archivo .def. Si utiliza la integración de CocoaPods, especifique la opción en el bloque de script de compilación pod {} de una dependencia del modo siguiente:

El comportamiento predeterminado no varía: el programa finaliza cuando se lanza una excepción desde el código de Objetive-C.

Mejoras en el complemento CocoaPods

Ejecución de tareas mejorada

En esta versión, hemos mejorado significativamente el flujo de ejecución de tareas. Por ejemplo, si añade una nueva dependencia de CocoaPods, las dependencias existentes no vuelven a crearse. Añadir un destino extra tampoco afecta a la nueva creación de dependencias para destinos existentes.

DSL ampliado

En 1.4.20 hemos ampliado el DSL para añadir dependencias de CocoaPods a su proyecto de Kotlin.

Además de los Pods locales y los Pods del repositorio de Cocoa Pods, puede añadir dependencias en los siguientes tipos de bibliotecas:

  • Una biblioteca desde un repositorio de especificaciones personalizado.
  • Una biblioteca remota desde un repositorio de Git.
  • Una biblioteca desde un archivo (también disponible mediante dirección HTTP arbitraria).
  • Una biblioteca estática.
  • Una biblioteca con opciones cinterop personalizadas.

Todavía se admite la sintaxis del DSL anterior.

Echemos un vistazo a un par de cambios en el DSL en los ejemplos siguientes:

  • Una dependencia en una biblioteca remota desde un repositorio de Git. Puede especificar una etiqueta, confirmación o rama utilizando las palabras clave correspondientes, por ejemplo:

    También puede combinar estas palabras clave para obtener la versión necesaria de un Pod.

  • Una dependencia en una biblioteca desde un repositorio de especificaciones personalizado. Utilice para ello el parámetro especial specRepos:

Encontrará más ejemplos en la demostración de Kotlin con CocoaPods.

Integración con Xcode actualizada

Para trabajar correctamente con Xcode, Kotlin requiere algunos cambios en Podfile:

  • Si su Kotlin Pod tiene alguna dependencia de Git, HTTP o specRepo, debería especificarla en el Podfile. Por ejemplo, si añade una dependencia en AFNetworking desde el repositorio de CocoaPods, declárela también en el Podfile:

    pod 'AFNetworking'
  • Cuando añada una biblioteca desde la especificación personalizada, también debería indicar la ubicación de las especificaciones al principio de su Podfile:

    source 'https://github.com/Kotlin/kotlin-cocoapods-spec.git'
    
    target 'kotlin-cocoapods-xcproj' do
      // ... other Pods ...
      pod 'example'
    end

Los errores de integración ahora cuentan con descripciones detalladas en IntelliJ IDEA, de modo que si tiene algún problema con su Podfile, inmediatamente obtendrá información sobre cómo resolverlo.

Eche un vistazo a la rama withXcproject de la demostración de Kotlin con CocoaPods. Contiene un ejemplo de integración de Xcode integration con el proyecto de Xcode existente denominado kotlin-cocoapods-xcproj.

Compatibilidad con bibliotecas de Xcode 12

Hemos añadido compatibilidad con nuevas bibliotecas incluidas en Xcode 12. ¡No dude en usarlas en su código Kotlin!

Estructura actualizada de publicaciones de bibliotecas multiplataforma

Antes de Kotlin 1.4.20, las publicaciones de bibliotecas multiplataforma incluían publicaciones específicas de una plataforma y una publicación de metadatos. Sin embargo, no era necesario depender únicamente de la publicación de metadatos, de modo que este artefacto nunca se utilizaba explícitamente.

A partir de Kotlin 1.4.20 ya no existe una publicación de metadatos por separado. Los artefactos de metadatos ahora se incluyen en la publicación raíz, que sirve para toda la biblioteca y se resuelve automáticamente en los artefactos específicos de cada plataforma al añadirse como dependencia al conjunto de fuentes común.

Tenga en cuenta que no tiene que añadir un artefacto vacío sin un clasificador al módulo raíz de su biblioteca para cumplir los requisitos de repositorios como Maven Central, puesto que esto daría lugar a un conflicto con los artefactos de metadatos que ahora se incluyen en este módulo.

Compatibilidad con bibliotecas publicadas en 1.4.20

Si ha habilitado la compatibilidad con estructura de proyecto jerárquica y desea utilizar una biblioteca multiplataforma que se publicó con dicha compatibilidad en Kotlin 1.4.20 o posterior, también tendrá que actualizar Kotlin en su proyecto a la versión 1.4.20 o posterior.

Si es el autor de una biblioteca y publica su biblioteca multiplataforma en Kotlin 1.4.20 o posterior con compatibilidad con estructura de proyecto jerárquica, recuerde que los usuarios con versiones anteriores de Kotlin que también tengan habilitada la compatibilidad con estructura de proyecto jerárquica no podrán utilizar su biblioteca. Tendrán que actualizar Kotlin a 1.4.20 o posterior.

No obstante, si usted o los usuarios de su biblioteca no habilitan la compatibilidad con estructura de proyecto jerárquica, quienes utilicen versiones anteriores de Kotlin todavía podrán usar su biblioteca.

Obtenga más información sobre cómo publicar una biblioteca multiplataforma.

Cambios en la biblioteca estándar

Extensiones para java.nio.file.Path

A partir de 1.4.20, la biblioteca estándar ofrece extensiones experimentales para java.nio.file.Path.

Trabajar con la API de archivo de JVM de un modo idiomático con Kotlin es ahora similar a trabajar con extensiones java.io.File desde el paquete kotlin.io. Ya no es necesario llamar a métodos estáticos de Files, puesto que la mayoría de ellos están ahora disponibles como extensiones en el tipo Path.

Las extensiones se encuentran en el paquete kotlin.io.path. Como el propio Path está disponible en JDK 7 y posteriores, las extensiones se sitúan en el módulo kotlin-stdlib-jdk7. Para utilizarlas, tiene que elegir la anotación experimental ExperimentalPathApi.

Deseamos agradecer especialmente a nuestro colaborador AJ Alt por nos enviar la solicitud de incorporación de cambios inicial con estas extensiones.

Rendimiento mejorado de la función String.replace

Siempre nos encanta que la comunidad de Kotlin nos sugiera mejoras, y el siguiente es uno de esos casos. En esta versión, hemos cambiado la implementación de la función String.replace().

La variante que distingue entre mayúsculas y minúsculas utiliza un bucle de sustitución manual basado en indexOf , mientras que la que no distingue entre mayúsculas y minúsculas utiliza la combinación de expresiones regulares.

Esta mejora acelera la ejecución de la función en ciertos casos.

Las Kotlin Android Extensions han quedado obsoletas

Desde que creamos las Kotlin Android Extensiones, han desempeñado un papel increíble en el aumento de la popularidad de Kotlin en el ecosistema Android. Con estas extensiones, dimos a los desarrolladores herramientas prácticas y eficientes para reducir el código reutilizable:

  • Vistas sintéticas (kotlinx.android.synthetics) para la interacción con la IU.
  • Generador de implementaciones Parcelable (@Parcelize) para pasar objetos como Parcel.

Al principio pensamos en añadir más componentes a kotlin-android-extensions. Pero no lo hicimos, y hemos recibido incluso solicitudes de usuarios para que dividamos el complemento en partes independientes.

Por otro lado, el ecosistema Android no deja de evolucionar, y los desarrolladores van obteniendo nuevas herramientas que les facilitan el trabajo. Algunas de las lagunas que abordaba Kotlin Android Extensions ahora están resueltas con mecanismos nativos de Google. Por ejemplo, con respecto a la sintaxis concisa para la interacción con la IU, ahora existe Android Jetpack, que dispone de vinculación de vistas, que sustituye a findViewById, como la sintetización de Kotlin.

Debido a estos dos motivos, hemos decidido retirar la sintetización en favor de la vinculación de vistas, y pasar el generador de implementaciones Parcelable a un complemento por separado.

En 1.4.20, hemos extraído el generador de implementaciones Parcelable de kotlin-android-extensions y hemos iniciado el ciclo de anulación del resto, que actualmente es solo sintetización. Por ahora, continuarán funcionando con un aviso de que van a quedar en desuso. Más adelante, tendrá que cambiar su proyecto a otra solución. Estas son las instrucciones para migrar proyectos de Android de la sintetización a la vinculación de vistas.

El generador de implementaciones Parcelable ahora está disponible en el nuevo complemento kotlin-parcelize. Use este complemento en lugar de kotlin-android-extensions, o combínelos si decide seguir utilizando la sintetización. La anotación @Parcelize se traslada al paquete kotlinx.parcelize.

Cómo actualizar

Antes de actualizar sus proyectos a la versión más reciente de Kotlin, puede probar el nuevo lenguaje y las funcionalidades de biblioteca estándar online en play.kotl.in.

En IntelliJ IDEA y Android Studio, puede actualizar el complemento de Kotlin a la versión 1.4.20; descubra cómo hacerlo aquí.

Si desea trabajar en proyectos existentes que se crearon con versiones anteriores de Kotlin, utilice la versión 1.4.20 de Kotlin en la configuración de su proyecto. Para obtener más información, vea los documentos para Gradle y para Maven.

Puede descargar el compilador de línea de comando desde la página de lanzamiento de GitHub.

Puede usar las siguientes versiones de las bibliotecas con esta versión:

Las versiones de las bibliotecas de kotlin-wrappers (kotlin-react etc.) se pueden encontrar en el repositorio correspondiente.

Los detalles del lanzamiento y la lista de bibliotecas compatibles también están disponibles aquí.

Si encuentra algún problema con la nueva versión, encontrará ayuda en Slack (obtenga una invitación aquí) e informe acerca de las incidencias en nuestro YouTrack.

Colaboradores externos

Nos gustaría agradecer a todos los contribuidores externos cuyas solicitudes de incorporación de cambios se han incluido en esta versión:

Jinseong Jeon Toshiaki Kameyama Steven Schäfer Mads Ager Mark Punzalan Ivan Gavrilovic pyos Jim Sproch Kristoffer Andersen Aleksandrina Streltsova cketti Konstantin Virolainen AJ Alt Henrik Tunedal Juan Chen KotlinIsland, Valeriy Vyrva Alex Chmyr Alexey Kudravtsev Andrey Matveev Aurimas Liutikas Dat Trieu Dereck Bridie Efeturi Money Elijah Verdoorn Enteerman fee1-dead Francesco Vasco Gia Thuan Lam Guillaume Darmont Jake Wharton Julian Kotrba Kevin Bierhoff Matthew Gharrity Matts966 Raluca Sauciuc Ryan Nett Sebastian Kaspari Vladimir Krivosheev n-p-s Pavlos-Petros Tournaris Robert Bares Yoshinori Isogai Kris Derek Bodin Dominik Wuttke Sam Wang Uzi Landsmann Yuya Urano Norbert Nogacki Alexandre Juca

Discover more