Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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

Exactpro
November 30, 2015

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

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

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

Exactpro

November 30, 2015
Tweet

More Decks by Exactpro

Other Decks in Science

Transcript

  1.  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
  2.  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
  3.  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
  4.  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
  5. 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
  6.  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
  7. 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
  8. 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
  9. 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
  10.  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
  11.  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
  12.  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
  13.  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
  14.  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
  15.  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
  16.  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
  17.  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
  18.  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
  19.  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
  20.  val / var x: Type : T(x) = Type

     val / var x: Type? : T(x) = Type U Nothing?  Statement: T in (x)  T out (x)  If: T in (x)  T true (x), T false (x)  Merge: T in1 (x), T in2 (x)  T out (x) Kotlin: from null dereference to smart casts 21
  21.  if (x != null) or while (x != null)

    T true (x) = T in (x) \ {Nothing?} T false (x) = T in (x) & {Nothing?}  if (x is Something) T true (x) = T in (x) & {Something} T false (x) = T in (x) \ {Something}  if (x == y) T true (x,y) = T in (x) & T in (y) T false (x) = T in (x) \ T in (y) T false (y) = T in (y) \ T in (x) Kotlin: from null dereference to smart casts 22
  22.  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) T true (x) = T in (x) & {Nothing?} T true (y) = T in (y) \ {Nothing?} T false (x) = T in (x) T false (y) = T in (y) Kotlin: from null dereference to smart casts 23
  23.  x!! T out (x) = T in (x) \

    {Nothing?}  x as Something T out (x) = T in (x) & {Something}  x?.foo(...) T foo (x) = T in (x) \ {Nothing?} T out (x) = T in (x) Kotlin: from null dereference to smart casts 24
  24.  if (…) { … } else { … }

    Out = In1 U In2  when (…) { … -> …; … -> …; else -> …;} Out = In1 U In2 U … U In(else) Kotlin: from null dereference to smart casts 25
  25.  Assignment: x = y or initialization: var x =

    y ◦ T out (x) = T in (y)  Initialization with given type: var x: Type = y ◦ T out (x) = { Type }  Before entering a loop ◦ For all X changed inside: T out (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
  26.  Classic data flow analysis on CFG ◦ Variable initialization

    analysis ◦ Variable usage analysis ◦ Code reachability analysis ◦ … Kotlin: from null dereference to smart casts 27
  27.  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
  28.  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
  29.  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
  30.  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
  31.  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