Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

TMPA-2015: Kotlin: From Null Dereference to Smart Casts

2,358 views

Published on

Kotlin: From Null Dereference to Smart Casts
Mihael Glukhikh, JetBrains

12 - 14 November 2015
Tools and Methods of Program Analysis in St. Petersburg

Published in: Science
  • Be the first to comment

  • Be the first to like this

TMPA-2015: Kotlin: From Null Dereference to Smart Casts

  1. 1. Mikhail Glukhikh mailto: Mikhail.Glukhikh@jetbrains.com JetBrains, Senior Software Developer
  2. 2.  Developed by JetBrains since 2011  Open-Source since 2012  Targets JVM and JavaScript ◦ Java 6 / 7 / 8 / Android  Statically Typed  Object-Oriented ◦ + Functional features ◦ + Procedural features  Java- and Scala-Compatible  Plugins for IDEA and Eclipse Kotlin: from null dereference to smart casts 2
  3. 3.  14 minor releases passed in 2012 – 2015  1.0 beta released (Nov 2015)  1.0 coming soon  Lines of Code ◦ ~250 KLOC in Kotlin project itself ◦ ~250 KLOC in other JetBrains projects ◦ ~1000 KLOC in non-JetBrains projects on GitHub Kotlin: from null dereference to smart casts 3
  4. 4.  Working in Kotlin project since March 2015  Before: author of various research static analysis tools  Also: associate professor in SPbPU Kotlin: from null dereference to smart casts 4
  5. 5.  Full Java interoperability  Safer than Java ◦ Null safety ◦ No raw types ◦ Invariant arrays ◦ Read-only collections  More concise and expressive than Java ◦ Type inference ◦ Higher-order functions (closures) ◦ Extension functions ◦ Class delegation  Compiler is at least as fast as Java  Simpler than Scala Kotlin: from null dereference to smart casts 5
  6. 6. fun main(args: Array<String>) { val name = if (args.isNotEmpty()) args[0] else "Kotlin" println("Hello, $name") }  val / var name : Type or = …  fun name(a: TypeA, b: TypeB): Type { … } or = …  Array<>, String  if … else … expressions  "Hello, $name" or even "Hello, ${my.name}" Kotlin: from null dereference to smart casts 6
  7. 7.  No primitive types, everything is an object ◦ Standard classes Int, Long, Char, Boolean, String, etc. ◦ Unit as the single-value type  fun main(args: Array<String>): Unit { … } ◦ Nothing as the type that never exists  fun todo(): Nothing = throw AssertionError() ◦ null has the type of Nothing? ◦ Any as the very base type for everything (not-null)  fun equals(other: Any?): Boolean = …  Interfaces and classes  Nullable and not-null types ◦ E.g. String? and String, Any? and Any, etc. Kotlin: from null dereference to smart casts 7
  8. 8. interface Food interface Animal : Food { enum class Kind { HERBIVORE,OMNIVORE,CARNIVORE } val kind: Kind fun eat(food: Food): Boolean fun die() = println("It dies") } class Lion : Animal { override val kind = Animal.Kind.CARNIVORE override fun eat(food: Food) = if (food is Animal) { food.die() true } else false } } Kotlin: from null dereference to smart casts 8
  9. 9. fun String.greet() = println("Hello, $this") fun greet(args: List<String>) = args.filter { it.isNotEmpty() } .sortBy { it } .forEach { it.greet() } Kotlin: from null dereference to smart casts 9
  10. 10. sealed class Tree { object Empty: Tree() class Leaf(val x: Int): Tree() class Node(val left: Tree, val right: Tree): Tree() fun max(): Int = when (this) { Empty -> Int.MIN_VALUE is Leaf -> this.x is Node -> Math.max(this.left.max(), this.right.max()) } } Kotlin: from null dereference to smart casts 10
  11. 11.  Distinct nullable types Type? and not-null types Type  Null checks and smart casts fun foo(s: String?) { // Error: Only safe or not-null asserted // calls allowed println(s.length) // Ok, smart cast to String if (s != null) println(s.length) if (s == null) return println(s.length) // Smart cast also here } Kotlin: from null dereference to smart casts 11
  12. 12.  Distinct nullable types Type? and not-null types Type  Safe calls, not-null asserted calls fun foo(s: String?) { // Error: Only safe or not-null asserted // calls allowed println(s.length) // Ok, safe call (s.length or null) println(s?.length) // Ok, not-null assertion (unsafe!) println(s!!.length) } Kotlin: from null dereference to smart casts 12
  13. 13.  Distinct nullable types Type? and not-null types Type  Safe call with Elvis operator fun foo(s: String?) { // Error: Only safe or // not-null asserted calls allowed println(s.length) // Ok, safe call + Elvis (s.length or NOTHING) println(s?.length ?: "NOTHING") } Kotlin: from null dereference to smart casts 13
  14. 14.  Flexible Types like Type! ◦ Type in Java  Type! in Kotlin ◦ Type! is not a syntax, just notation ◦ Assignable to both Type and Type? ◦ Dot is applicable to a variable of Type! ◦ Annotations are taken into account  @NotNull Type in Java  Type in Kotlin Kotlin: from null dereference to smart casts 14
  15. 15.  is or !is to check, as or as? to convert class StringHolder(val s: String) { override fun equals(o: Any?): Boolean { // Error: unresolved reference (o.s) return s == o.s if (o !is StringHolder) return false // Ok, smart cast to StringHolder return s == o.s } } Kotlin: from null dereference to smart casts 15
  16. 16.  is or !is to check, as or as? to convert class StringHolder(val s: String) { override fun equals(o: Any?): Boolean { // Ok, unsafe (ClassCastException) return s == (o as StringHolder).s // Ok, safe return s == (o as? StringHolder)?.s } } Kotlin: from null dereference to smart casts 16
  17. 17.  Top-down analysis on AST  For each relevant expression E, possible types of E: T(E) are determined at each AST node  T(null) = Nothing?  Example fun foo(s: String?) { // T(s) = {String?} = {String,Nothing?} if (s != null) { // T(s) = {Nothing?} println(s.length) } } Kotlin: from null dereference to smart casts 17
  18. 18.  For local values, function parameters, special this variable  For member or top-level values if: ◦ They are not overridable and have no custom getter AND  They are private or internal (not a part of public API) OR  They are protected or public, and used in the same compilation module when declared class My { private val a: String? // safe public val b: String? // same module only internal open val c: String? // unsafe private val d: String? // unsafe get() = null } Kotlin: from null dereference to smart casts 18
  19. 19.  For local variables if ◦ a smart cast is performed not in the loop which changes the variable after the smart cast fun foo() { var s: String? // T(s) = String U Nothing? s = "abc" // T(s) = String s.length // smart cast while (s != "x") { // Unsafe: changed later in the loop if (s.length > 0) s = s.substring(1) else s = null } } Kotlin: from null dereference to smart casts 19
  20. 20.  For local variables if ◦ a smart cast is performed in the same function when the variable is declared, not inside some closure ◦ no closure that changes the variable exists before the location of the smart cast fun indexOfMax(a: IntArray): Int? { var maxI: Int? = null a.forEachIndexed { i, value -> if (maxI == null || value >= a[maxI]) { maxI = i } } return maxI } Kotlin: from null dereference to smart casts 20
  21. 21.  val / var x: Type : T(x) = Type  val / var x: Type? : T(x) = Type U Nothing?  Statement: Tin(x)  Tout(x)  If: Tin(x)  Ttrue(x), Tfalse(x)  Merge: Tin1(x), Tin2(x)  Tout(x) Kotlin: from null dereference to smart casts 21
  22. 22.  if (x != null) or while (x != null) Ttrue (x) = Tin(x) {Nothing?} Tfalse(x) = Tin(x) & {Nothing?}  if (x is Something) Ttrue (x) = Tin(x) & {Something} Tfalse(x) = Tin(x) {Something}  if (x == y) Ttrue (x,y) = Tin(x) & Tin(y) Tfalse(x) = Tin(x) Tin(y) Tfalse(y) = Tin(y) Tin(x) Kotlin: from null dereference to smart casts 22
  23. 23.  if (A && B) True: TrueA & TrueB False: FalseA U FalseB  if (A || B) True: TrueA U TrueB False: FalseA & FalseB  Example: if (x == null && y != null) Ttrue (x) = Tin(x) & {Nothing?} Ttrue (y) = Tin(y) {Nothing?} Tfalse(x) = Tin(x) Tfalse(y) = Tin(y) Kotlin: from null dereference to smart casts 23
  24. 24.  x!! Tout(x) = Tin(x) {Nothing?}  x as Something Tout(x) = Tin(x) & {Something}  x?.foo(...) Tfoo(x) = Tin(x) {Nothing?} Tout(x) = Tin(x) Kotlin: from null dereference to smart casts 24
  25. 25.  if (…) { … } else { … } Out = In1 U In2  when (…) { … -> …; … -> …; else -> …;} Out = In1 U In2 U … U In(else) Kotlin: from null dereference to smart casts 25
  26. 26.  Assignment: x = y or initialization: var x = y ◦ Tout(x) = Tin(y)  Initialization with given type: var x: Type = y ◦ Tout(x) = { Type }  Before entering a loop ◦ For all X changed inside: Tout(X) = Type(X) data class Node(val s: String, val next: Node?) fun foo(node: Node) { var current: Node? // T = {Node,Nothing?} current = node // T = {Node} while (true) { // T = {Node,Nothing?} println(current.s) // Error if (current == null) break println(current.s) // Ok: T = {Node} current = current.next // T = {Node,Nothing?} } // T = {Nothing?} } Kotlin: from null dereference to smart casts 26
  27. 27.  Classic data flow analysis on CFG ◦ Variable initialization analysis ◦ Variable usage analysis ◦ Code reachability analysis ◦ … Kotlin: from null dereference to smart casts 27
  28. 28.  Total smart casts: 8500  Unsafe operations ◦ !! : 1500 (mostly provoked by implicit conventions) ◦ as : 1500  Checks: ◦ != null : 2000 ◦ == null : 1000  Safe operations: ◦ ?. : 2500 ◦ as? : 1000 ◦ ?: : 2500 Kotlin: from null dereference to smart casts 28
  29. 29.  More precise loop analysis  More precise closure analysis  Extra helper annotations like @Pure or @Consistent for mutable properties  … Kotlin: from null dereference to smart casts 29
  30. 30.  http://kotlinlang.org – language information, API reference, tutorials, koans, installation instructions, community, etc.  http://github.com/JetBrains/Kotlin – main compiler & plugin repository  http://blog.jetbrains.com/kotlin/ – Kotlin blog  Questions? Kotlin: from null dereference to smart casts 30
  31. 31.  Java Kotlin ◦ Checked exceptions ◦ Primitive types that are not classes ◦ Static members ◦ Non-private fields  Kotlin Java ◦ Extension functions ◦ Null-safety, smart casts ◦ String templates ◦ Properties ◦ Primary constructors ◦ Class delegation ◦ Type inference ◦ Singletons ◦ Operator overloading, infix functions ◦ … Kotlin: from null dereference to smart casts 31
  32. 32.  Scala Kotlin ◦ Implicit conversions ◦ Overridable type members ◦ Existential types ◦ Structural types ◦ Value types ◦ Yield operator ◦ Actors ◦ …  Kotlin Scala ◦ Zero overhead null safety ◦ Smart casts ◦ Class delegation Kotlin: from null dereference to smart casts 32

×