Kotlin 1.3.50 released

Posted on by

We’re happy to announce the release of Kotlin 1.3.50 today. In addition to the quality and tooling improvements, the main focus for this version has been on:

Designing a new Duration and Time Measurement API (available for preview).

Working on an improved Java-to-Kotlin converter.

Experimental generation of external declarations for npm dependencies in Gradle Kotlin/JS projects (using Dukat).

A separate plugin for debugging Kotlin/Native code in IntelliJ IDEA Ultimate.

Java compilation support in multiplatform projects.

You can find the complete list of changes in the change log. As always, we’d like to thank our external contributors. Now let’s dive into the details!

Null-check optimizations planned for Kotlin 1.4

As you probably know, Kotlin decreases the possibility of NullPointerException s by providing support for nullable types. However, because of interoperability with Java code, it’s impossible to avoid NPEs completely. To help developers better understand the source of a nullability problem if it occurs, Kotlin compiler throws different types of runtime exceptions with clear error messages instead of pure NPEs. It turned out that this approach has its disadvantages: it reduces possible null check optimizations that can be performed either by the Kotlin compiler or by various kinds of bytecode processing tools, such as the Android R8 optimizer.

To solve this, starting from Kotlin 1.4, all runtime null checks will throw a java.lang.NullPointerException instead of a KotlinNullPointerException , IllegalStateException , IllegalArgumentException , and TypeCastException . This applies to: the !! operator, parameter null checks in the method preamble, platform-typed expression null checks, and the as operator with a non-null type. This doesn’t apply to lateinit null checks and explicit library function calls like checkNotNull or requireNotNull .

Note that from a developer’s perspective, things won’t change that much: the Kotlin code will throw exceptions with the same error messages as before. The type of exception changes, but the information passed stays the same. For instance, the following code currently throws an IllegalStateException with the error message “JavaCode.getNull() must not be null”:

fun main() { duplicate(JavaCode.getNull()) // 1 } fun duplicate(s: String) = s + s

public class JavaCode { public static String getNull() { return null; } }

Right before the duplicate function call at line 1 , a special check is generated which throws this exception if the expression JavaCode.getNull() stores null . Starting with Kotlin 1.4, this code will throw a NullPointerException instead with the same message, “JavaCode.getNull() must not be null”.

After this change of behavior, the optimizers will be able to decrease the total number of null-checks present in the bytecode by removing repetitive null-checks when possible: since all checks throw the same NPE exception, only one can remain. During such optimizations, the specific helpful messages of NPEs can be lost, but that’s the price to be paid for the gained performance benefits. Note that these optimizations are still to be implemented in the corresponding tools, and when implemented, there will be more details about that, but the change of the exception type makes it possible in the future.

Standard library changes

Note that all new functions are added to the standard library in the “experimental” state.

Duration and time measurement API

A new duration and time measurement API is available for preview. Duration can be measured in a variety of units: seconds, milliseconds, nanoseconds, etc. The confusion between different units is a known source of bugs: if the API expects the duration stored as primitive value like Long , one can erroneously pass the value in the wrong unit, and unfortunately the type system doesn’t help prevent that. Creating a regular class to store duration solves this problem, but brings another one: additional allocations.

Inline classes provide a very elegant solution to that: they bring both type system guarantees and an allocation-free approach. Now the API can use the Duration type, and all the clients will need to specify the time in the desired units explicitly. Since Duration is declared as an inline class, no extra allocations are happening under the hood:

import kotlinx.coroutines.delay import kotlin.time.* @ExperimentalTime suspend fun greetAfterTimeout(duration: Duration) { delay(duration.toLongMilliseconds()) println("Hi!") } @UseExperimental(ExperimentalTime::class) suspend fun main() { greetAfterTimeout(100.milliseconds) greetAfterTimeout(1.seconds) }

This release brings support for MonoClock which represents the monotonic clock. The recommended approach to measuring time duration from a given point in your program is to use the monotonic clock, which doesn’t depend on the system time. System time might be changed outside, and that might lead to erroneous behavior. The monotonic clock can only measure time difference between given time points, but doesn’t know the “current time.”

The Clock interface provides a general API for measuring time intervals. MonoClock is an object implementing Clock ; it provides the default source of monotonic time on different platforms.

When using the Clock interface, you explicitly mark the time of action start, and later the time elapsed from the start point. It is especially convenient if you want to start and finish measuring time from different functions:

import kotlin.time.* @UseExperimental(ExperimentalTime::class) fun main() { val clock = MonoClock val mark = clock.markNow() // might be inside the first function Thread.sleep(10) // action println(mark.elapsedNow()) // might be inside the second function }

The measureTimedValue function allows you to measure the duration of a given action and get its result together with the duration of the elapsed time interval. It measures the elapsed time with MonoClock .

import kotlin.time.* @UseExperimental(ExperimentalTime::class) fun main() { val (value, duration) = measureTimedValue { Thread.sleep(100) 42 } println(value) // 42 println(duration) // e.g. 103 ms }

For more details about the implementation of the Duration class and the details of the Clock interface and MonoClock implementations for different platforms, please refer to the corresponding KEEP. Note that this API is experimental and is subject to change based on your feedback. You will need to explicitly give your consent to use it by applying the corresponding annotations.

We’re looking forward to your feedback!

Functions for bit manipulation

The standard library now contains an API for bit manipulation (as usual, in the experimental state initially):

@UseExperimental(ExperimentalStdlibApi::class) fun main() { val number = "1010000".toInt(radix = 2) println(number.countOneBits()) println(number.countTrailingZeroBits()) println(number.takeHighestOneBit().toString(2)) println(number.rotateRight(3).toString(2)) println(number.rotateLeft(3).toString(2)) }

Note that similar extension functions for Int, Long, Short, Byte, and their unsigned counterparts have been added.

IntelliJ IDEA support

Improved Java to Kotlin converter

We plan to improve the Java-to-Kotlin converter to minimize the amount of “red code” one has to fix manually after the conversion. As the current converter almost always generates non-nullable types, you need to fix the nullability issues by hand afterward. It can often lead to runtime errors from nullability mismatch later.

The new, improved version of the Java-to-Kotlin converter tries to infer nullability more correctly based on the Java type usages in the code. Note that there’s no goal to produce 100% error-free code. The goal is to decrease the number of compilation errors and to make the produced Kotlin code more convenient to work with. The new converter fixes many other known bugs, too; for instance, it now correctly handles implicit Java type casts.

In the future, the new converter is going to become the default one. In this release, it’s available for preview. To turn it on, specify the Use New J2K (experimental) flag in settings.

Please give it a try and share your feedback with us!

Debugging improvements

We’ve improved how the Kotlin “Variables” view chooses the variables to display. As there’s a lot of additional technical information in the bytecode, the Kotlin “Variables” view highlights only the relevant variables. Now it works better when you set a breakpoint inside a lambda (either inlined or non-inlined). Local variables inside the lambda, as well as captured variables from the outer context and parameters of the outer function, are correctly displayed:

You can now set a breakpoint at the end of the function, if needed:

Support for the “Evaluate expression” functionality in the debugger was improved for many non-trivial language features, such as local extension functions or accessors of member extension properties. Also, you can now modify variables via “Evaluate expression”:

Note that, alternatively, you can modify variables in the “Variables” view directly.

New intentions and inspections

New intentions and inspections have been added. One of the goals of intentions is to help you learn how to write idiomatic Kotlin code. The following intention, for instance, suggests using the indices property rather than building a range of indices manually:

If the index isn’t used, the loop can be automatically replaced with a for loop over elements.

IntelliJ IDEA now:

Can automatically replace the lateinit property of a primitive type with the by Delegates.notNull() syntax.

property of a primitive type with the syntax. Can convert a regular property to a lazy one and back.

Detects usages of Java methods for array comparison (like Arrays.equals() or Array.deepEquals() ) and suggest replacing them with their Kotlin counterparts (like contentEquals and contentDeepEquals ).

or ) and suggest replacing them with their Kotlin counterparts (like and ). Highlights the deprecated import in the completion list.

The general performance of IDE actions has been improved, and several known situations that were causing the UI to freeze (such as calling the “Move” refactoring on a file with a huge number of usages) have been fixed.

Kotlin/JS

This update adds support for building and running Kotlin/JS Gradle projects using the org.jetbrains.kotlin.js plugin on Windows. Just like on other platforms, you can build and run your projects using Gradle tasks, dependencies from NPM required in your Gradle configuration are resolved and included, you can try out your applications using webpack-dev-server (e.g. by invoking the browserRun Gradle task), and more. As with the other platforms, all of this can be used without having to manually install and manage a node, npm, or yarn distribution.

Under the hood, there have also been a series of performance improvements for Kotlin/JS, improving the incremental compilation time for projects. This means that you can expect speedups of up to 30% when compared to 1.3.41.

Our improved integration with NPM means that projects are now resolved lazily and in parallel, and support for projects with transitive dependencies between compilations in the same project has been added.

The new version also brings with it changes in the structure and naming of generated artifacts. Generated artifacts are now bundled in the distributions folder, and they include the version number of the project and archiveBaseName (which defaults to the project name), e.g. projectName-1.0-SNAPSHOT.js .

Dukat

Dukat allows the automatic conversion of TypeScript declaration files ( .d.ts ) into Kotlin external declarations (and thus replaces the ts2kt command-line tool). This makes it more comfortable to use libraries from the JavaScript ecosystem in a type-safe manner in Kotlin, by drastically reducing the need for manually writing wrappers for JS libraries.

Kotlin/JS now ships with experimental support for dukat integration for Gradle projects. With this integration, by running the build task in Gradle, typesafe wrappers are automatically generated for npm dependencies and can be used from Kotlin.

Because dukat is still in a very early stage, its integration is disabled by default. Add the kotlin.js.experimental.generateKotlinExternals=true line into the gradle.properties file in a project root directory to turn on dukat for your project. We’ve prepared an example project for it too, which demonstrates the use of dukat in Kotlin/JS projects. Try it out and share your feedback with us.

Kotlin/Native

It was not easy but possible to notice that the version of Kotlin/Native differed from the version of Kotlin. Not anymore! The version schemes for Kotlin and Kotlin/Native are now aligned. This release uses version 1.3.50 for both Kotlin and Kotlin/Native binaries, reducing the complexity.

This release brings more pre-imported Apple frameworks for all platforms, including macOS and iOS. The Kotlin/Native compiler now includes actual bitcode in produced frameworks.

Several improvements have been made for interop. We now support the kotlin.Deprecated annotation when producing a framework, and the generated Objective-C headers code will not have warnings. The getOriginalKotlinClass() function is added into the standard library to get a KClass from an Objective-C class or protocols.

The standard library has been updated to support the kotlin.reflect.typeOf() function for Kotlin/Native types. The new function executeAfter() is added to the Worker type to execute actions after a delay. In addition, you can call the processQueue() function on the Worker to process the tasks queue explicitly.

The older functions ByteArray.stringFromUtf8() and ByteArray.stringFromUtf8OrThrow() are now deprecated. In the previous release, we added the ByteArray.decodeToString() function (in the kotlin.text package) to convert a UTF-8 string to a Kotlin string. That function does not support NULL-terminated strings, so to fix that, in 1.3.50 we’ve added a new function to deal with NULL-terminated UTF-8 strings easily. The ByteArray.toKString() function (from the kotlinx.cinterop package) can be used to turn a NULL-terminated UTF-8 string into a Kotlin string. You can pass start and end indices to the function if needed.

We’ve deprecated and removed the kotlin-platform-native Gradle plugin in favor of the kotlin-multiplatform Gradle plugin, so now you can easily benefit from all multiplatform project features. Check out the documentation for the migration guide and more info

Finally, we are happy to share that the Kotlin/Native compiler and the interop tool performance has been improved in this release.

Multiplatform Projects

Java compilation can now be included in Kotlin/JVM targets of a multiplatform project by calling the newly added withJava() function of the DSL. It will configure Java plugin to use the src/<targetName>Main/java and src/<targetName>Test/java paths by default. Here is a full example to create a Kotlin/JVM target with enabled Java compilation:

plugins { kotlin("multiplatform") version "1.3.50" } kotlin { jvm { withJava() } }

Kotlin’s New Project wizard now generates Gradle Kotlin DSL for new projects.

Debugging Kotlin/Native code in IntelliJ IDEA Ultimate is now supported too! Try installing the new Native Debug for IntelliJ IDEA Ultimate plugin. It should detect Kotlin/Native run configurations and the debug action is supported:

Scripting

This release adds multiple features and improvements in scripting and REPL support. Using Kotlin as a scripting language for your application is even easier! Now the scripting support works out of the box: we’ve published a default JSR-223 implementation library, so to add Kotlin scripting support for your application, you only need to add kotlin-scripting-jsr223 as a dependency and use the javax.script API with Kotlin.

Properties that are set via the JSR-223 API are now accessible from scripts as regular Kotlin properties (before, that you had to use the bindings map):

val engine = ScriptEngineManager().getEngineByExtension("kts")!! engine.put("z", 42) engine.eval("""println("answer = $z")""")

The kotlin-main-kts artifact, which was introduced in Kotlin 1.3 to simplify the creation and usage of the basic utility scripts, can now be used as a JSR-223 host as well. In addition to the annotations like Repository and DependsOn for resolving dependencies, it now supports the @Import annotation instructing the scripting compiler to “import” another script into the current one:

// common.main.kts: val foo = "common foo"

// script.main.kts: @file:Import("common.main.kts") val bar = "bar with $foo"

Read more about scripting in KEEP-75. You can find examples in the Kotlin repository. Please share your feedback in the #scripting channel on the Kotlin Slack!

How to update

As always, you can try Kotlin online at play.kotl.in.

In Maven, Gradle, and npm : Use 1.3.50 as the version for the compiler and the standard library. See the docs here.

: Use as the version for the compiler and the standard library. See the docs here. In IntelliJ IDEA and Android Studio : Update the Kotlin plugin to version 1.3.50. Use Tools | Kotlin | Configure Kotlin Plugin Updates and click the “Check for updates now” button.

and : Update the Kotlin plugin to version 1.3.50. Use Tools | Kotlin | Configure Kotlin Plugin Updates and click the “Check for updates now” button. In Eclipse : Install the plugin using the Marketplace.

: Install the plugin using the Marketplace. The command-line compiler can be downloaded from the Github release page.

If you run into any problems with the new release, you’re welcome to ask for help on the forums, on Slack (get an invite here), or to report issues in the issue tracker.

Let’s Kotlin!

External Contributions

We want to especially thank Toshiaki Kameyama for his ongoing work on providing many useful intentions and inspections for IntelliJ IDEA.

We’d like to thank all our external contributors whose pull requests were included in this release:

* Steven Schäfer

* pyos

* Ivan Gavrilovic

* Mads Ager

* Ting-Yuan Huang

* ilgonmic

* Jiaxiang Chen

* Mark Punzalan

* Jake Wharton

* Jeffrey van Gogh

* Peter Xu

* Amaury

* Benjamin Orsini

* Dereck Bridie

* Eduard Wolf

* George Gastaldi

* Juan Chen

* Kevin Peek

* KilianCallebaut

* Louis CAD

* Martin Petrov

* Matthew Runo

* AJ Alt

* Ty Smith

* ghedeon

* technoir

* Dat Trieu