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

Disassembling Kotlin Features - mDevCamp 2025

Disassembling Kotlin Features - mDevCamp 2025

Avatar for Andrea Cioccarelli

Andrea Cioccarelli

June 03, 2025
Tweet

More Decks by Andrea Cioccarelli

Other Decks in Technology

Transcript

  1. Introduction • Talk idea from working with lower level languages,

    where much more direct representation exists between source code and executable • Exploring what lies between Kotlin source and Android execution • Developers model systems with <black|white> boxes • Throughline: adding to your mental model a general overview of those underlying systems we rely on when developing
  2. Heap Stack pc Constant Pool Method Area Specification Platform Language

    -> jvm spec, bytecode -> compulation, execution -> features & high-level Kotlin Android ART ELF when guards data classes conventions default arguments destructuring
  3. JVM Specification 1. Abstract JVM 2. Data Types 3. Class

    File 4. Runtime Data Areas 5. Comparison w/ Linux-like formats Heap Stack pc Constant Pool Method Area
  4. Abstract JVM From The JVM Specification: “[…], specifying the abstract

    Java Virtual Machine, serving as documentation for a concrete implementation only as blueprints document a house.” An implementation of the JVM must embody this specification, but is constrained only when necessary. • Specification define an abstract computing machine • Stack-based virtual machine, 256 instruction (28, 1-byte) [1]
  5. Abstract JVM • JVM knows nothing about Java • Acts

    as intermediary between language and platform • Has its own ISA (bytecode), hardware indepentent JVM Java Scala Clojure Linux Kotlin Windows MacOS Languages target JVM, Platforms implement JVM
  6. Abstract JVM Sometimes language restrictions aren’t because of constraints in

    the JVM, but because of the language itself • CPUs have their own machine code representation (assembly) • JVM bridges that gap with bytecode (+portability) • Virtual execution environment for languages to target
  7. Abstract JVM - > Android Android targets different processors, devices

    and architectures This enables deploying the same codebase over different devices • Android uses a runtime akin to the JVM to run apps • Heavily modified and optimized VM • Flexible: suited for mobile devices ecosystem iOS targets a homogeneous set of devices, with one streamlined CPU architecture; iOS applications are compiled native binaries
  8. Class File • Each Java/Kotlin class is compiled into one

    .class file • Simple binary format: stores all information about the class (methods, fields, visibility, superclass, ... ) • May be packaged together (.jar file) • Class files are then fed to the JVM
  9. Class File magic 0x00 0x03 minor_version 0x04 0x05 major_version 0x06

    0x07 Magic Magic number for .class files, 4 bytes, 0xcafebabe constant_pool_count 0x08 0x09 constant_pool
  10. Class File minor_version 0x04 0x05 major_version 0x06 0x07 Version(s) Contains

    version of the class file, specifying for which JVM specification it has been implemented e.g. Java 8 is 52.0, Java 11 is 55.0 constant_pool_count 0x08 0x09 constant_pool access_flags C + 0x01 C + 0x02 C 0x0A
  11. Class File Constant Pool Table of symbols, containing all constant

    values for the class i.e. strings, numbers, references, classes, internal structures constant_pool_count 0x08 0x09 constant_pool access_flags C + 0x01 C + 0x02 C 0x0A this_class super_class interfaces_count C + 0x03 C + 0x04 C + 0x05 C + 0x06 C + 0x07 C + 0x08
  12. Class File Access Flags Mask of flags, used to define

    properties about the class e.g. public, final, abstract super, interface, synthethic, annotation, enum access_flags C + 0x01 C + 0x02 this_class super_class interfaces_count C + 0x03 C + 0x04 C + 0x05 C + 0x06 C + 0x07 C + 0x08 interfaces C + 0x09 I
  13. Class File This & Super Offset in the constant_pool definining

    the name of the current and super class Can be zero if no superclass this_class super_class interfaces_count C + 0x03 C + 0x04 C + 0x05 C + 0x06 C + 0x07 C + 0x08 interfaces C + 0x09 I fields_count I + 0x01 I + 0x02
  14. Class File Interfaces Offsets in the constant_pool representing interface classes

    implemented by the current class interfaces_count C + 0x07 C + 0x08 interfaces C + 0x09 I fields_count I + 0x01 I + 0x02 fields I + 0x03 F
  15. Class File Fields Contains information for every field explicitly defined

    in the current class e.g. private/public, type fields_count I + 0x01 I + 0x02 fields I + 0x03 F methods_count methods F + 0x01 F + 0x02 F + 0x03 M
  16. Class File Methods Contains information for every method explicitly defined

    in the current class e.g. private/public, type methods_count methods F + 0x01 F + 0x02 F + 0x03 M attributes_count attributes M + 0x01 M + 0x02 M + 0x03 A
  17. Class File Attributes Contains all the meta- information about the

    whole class structure not specified in other fields e.g. annotations, debugging, exceptions, deprecation, line numbers attributes_count attributes M + 0x01 M + 0x02 M + 0x03 size-1 Fields are like class variables Attributes are meta-info Attributes only encode data defined by the JVM Spec
  18. Data Types Divied into either primitive or reference types Primitives

    References Basic data types, representing raw values No methods, references or any other special function Faster to work with Types referring to objects or arrays They hold a reference to an object on the heap @NotNull @Nullable
  19. Data Types / Primitives • Rationale: basic data types should

    have an explicit, value-based representation instead of being a refrence • Many bytecode instructions are tailored per type - > Addition: iadd, ladd, fadd, dadd - > Mutliply: imul, lmul, fmul, dmul - > Increment: iinc - > Conversion: l2i, f2i, d2f, …
  20. Runtime Data Areas •Load the .class file into memory, moving

    information from storage into an address space •Each class section stored in different memory area •Runtime relies on this memory division
  21. Method Area Method Area Contains in-memory fields, methods and constructors

    for the current class Loaded form the class file Created on JVM startup
  22. Method Area Runtime Constant Pool Runtime Constant Pool Runtime representation

    of the constant_pool for the class file Loaded form the class file Created on class creation
  23. Method Area Runtime Constant Pool Heap Heap Memory region used

    to store instances of objects Dynamically allocated as bytecode requests new object instantiations Created on JVM startup, GC’d The JVM bytecode instruction new is used to allocate heap memory and invoke the constructor for a certain class
  24. Method Area Call Stack “The stack in stacktrace” Contains the

    list of all the calls to past functions On function call -> stack pushed On return -> stack popped Runtime Constant Pool Heap Stack LIFO data structure, holds the activation record (aka frame) for the current method call
  25. Method Area Program Counter Per-thread register, containing address of current

    instruction being executed (either in method area or native) Runtime Constant Pool Heap Stack pc Only explicit register that the JVM specs mandate
  26. Method Area Multi-threaded model Many threads may concurrently access and

    execute a given class The only per-thread elements are the stack and the pc Runtime Constant Pool Heap Thread #1 Thread #2 Stack pc Stack pc
  27. Thread #2 Stack pc Local Variables Operand Stack One frame

    per function Frame on top of the stack is the one associated with the currently executing instruction Frame
  28. Thread #2 Stack pc Local Variables Operand Stack Initially contains

    list of parameters passed to the method currently positioned on the stack <stack+n> Frame fun pow(base: Int, exp: Int) Tstore instructions save values to local variables
  29. Thread #2 Stack pc Local Variables Operand Stack Initially empty

    Used by instructions to store operands for successive instructions taking them as argument <stack+n> Frame Tload instructions push values onto operand stack JVM is stack-based
  30. Bytecode analysis: Adder.kt Let’s now analyze a program performing a

    sum of two numbers in its decompiled form (JVM instructions) fun add() { val i = 15 val j = 10 return i + j } Adder.kt bipush 15 istore_0 bipush 10 istore_1 iload_0 iload_1 iadd ireturn kotlinc Adder.kt javap -c Adder.class Adder.class
  31. bipush 15 istore_0 bipush 10 istore_1 iload_0 iload_1 iadd ireturn

    Pre-first instruction Frame for add() method just pushed on stack Variables and operand stack empty Local Variables Operand Stack frame <add>
  32. bipush 15 istore_0 bipush 10 istore_1 iload_0 iload_1 iadd ireturn

    Store 15 on local variable Push 15 on operand stack via bipush Then pop it via istore_0, saving it in local variable, index 0 Local Variables Operand Stack frame <add> 15
  33. bipush 15 istore_0 bipush 10 istore_1 iload_0 iload_1 iadd ireturn

    Store 10 on local variable Push 10 on operand stack via bipush Then pop it via istore_1, saving it in local variable, index 1 Local Variables Operand Stack frame <add> 15 10
  34. bipush 15 istore_0 bipush 10 istore_1 iload_0 iload_1 iadd ireturn

    Load local variables back into operand stack Push variables at index 0 and 1 (respectively, 15 and 10) onto the operand stack Local Variables Operand Stack frame <add> 15 10 10 15
  35. bipush 15 istore_0 bipush 10 istore_1 iload_0 iload_1 iadd ireturn

    Perform addition The iadd instruction takes the top two operands from the stack, sums them, and pushes the result in the stack Local Variables Operand Stack frame <add> 15 10 10 15 25
  36. bipush 15 istore_0 bipush 10 istore_1 iload_0 iload_1 iadd ireturn

    Return from add() The ireturn instruction returns the value on top of the operand stack Local Variables Operand Stack frame <add> 15 10 10 15 25
  37. Calling Convention • Method parameters are copied in the callee

    function’s frame, inside the local variables - > Primitive types are just copied over The JVM always handles method calls as pass-by-value - > Reference types are copied as well, but for their nature, they provide a way to access and modify the original value Pass by value the reference’s memory address
  38. Platform 1. Kotlin Compilation Process 2. Android APK contents 3.

    ART Runtime structure 4. On-device optimization 5. OAT files & PGO Kotlin Android ART ELF+dex
  39. Frontend Backend *.kt *.class kotlinc • Platform independent • Takes

    source code (.kt), tokenizes, builds the PSI tree, checks for errors • Produces a form of IR* to pass to the backend • Responsible for checking syntax correctness • Can target different platforms • Takes the frontend* IR, generates IR, then turns it into platform- specific (lowered) IR • Produces desired platform binaries * w/ K2
  40. Android APKs Android • Android applications packaged as .apk file,

    subtype of .jar files, which is itself a .zip file • Contains all resources needed to install an application • On installation, application is placed in /data/data, / data/app, ... • On execution, application classes are loaded into memory and run
  41. Android AABs Android • Superset of APK files • Decouples

    base app, asset packs, dynamic features and a lot of metadata for putting it back together Android devices receive split APKs from the play store, they don’t process AABs directly
  42. - > Manifest Android • XML file providing a complete

    description and requirements for the Android application • Developers write AndroidManifest.xml, aapt converts it to a binary version (ABX from Android 12)
  43. - > /res / * Android • Processed resources (drawables,

    layouts, strings, icons) • Indexed in RESOURCES.ARSC (Android resource storage container)
  44. - > Signing Block Android • There are signature blocks

    in the APK file (versions JAR, v2, v3) • X.509 certificate (e.g. RSA / ECDSA) • Hashing (SHA2-256 / SHA-512)
  45. - > /lib / * Android • Contains native libraries

    (compiled binaries / JNI) • Most common: • arm64-v8a (64-bit ARM aarch64) • armeabi-v7a (32-bit ARM) • x86 (32-bit Intel 80386) • x86_64 (64-bit x86-64) • MIPS
  46. - > Android • Contains application bytecode in Dalvik-Executable Format

    (.dex) • Used by Android Runtime (ART) to execute application APK files don’t contain .class files • Android doesn’t run JVM, it has a custom bytecode and runtime classesN.dex
  47. Dexing Dexing • Converts *.class (kotlinc/javac output) to one .dex

    file • Packages multiple class files into a single dex, grouping constant pools
  48. .dex • Register-based VM (DVM) • Optimized, compact instructions •

    Slow evolution, fragmented delicate ecosystem .class • Stable bytecode • Stack-based VM (JVM) • General purpuse • Frequent-er releases • Flashy new language features Dexing Core Android component Android doesn’t run the JVM!
  49. Dexing Desugaring Desugaring ⇏ > destructures modern JVM bytecode into

    more primitive JVM bytecode “Undoing syntactic sugar” • Stable bytecode • Flashy new language features
  50. Dexing Desugaring Desugaring • Necessary for: - > Technical Compatibility:

    reduce bytecode for dexing, breaking down new JVM features - > API Compatibility: retrofit new API to android versions (e.g. time, Streams) - > Legal Reasons: allows to use one language to code, but change things before shipping (For years ART was stuck at Java 7 language features, Google v. Oracle)
  51. Dexing Desugaring Desugaring • Language features that may requiring desugaring:

    - > Lambda expressions - > Method references - > Interface default/static methods - > Repeating/type annotations - > Try with resources
  52. Dexing Desugaring Desugaring • If a JVM instruction ( <

    => language feature) is implemented in DEX instructions only in some versions, is it desugared or not? • e.g. JVM lambda introduced in Java 8 lambdas in JVM generates a invokedynamic instruction - > Prior to Android 7 (API 24), no equivalent exists in DEX instructions - > Needs to desugar lambdas into anonymous classes - > After to Android 7, DEX 038 implements invoke-polymorphic and invoke-custom instructions - > No desugar, dexer can convert lambdas with those specific calls
  53. JVM 6 JVM 7 JVM 8 JVM 9 JVM 10

    DEX 6 DEX 7 DEX 8 DEX 9 DEX 10 JVM Language Level Device Runtime Version ... ... minSdkVersion Native Desugared [symbolic, analog for language features]
  54. JVM 6 JVM 7 JVM 8 JVM 9 JVM 10

    DEX 6 DEX 7 DEX 8 DEX 9 DEX 10 ... ... minSdkVersion Native Desugared JVM Language Level Device Runtime Version [symbolic, analog for language features]
  55. Android Runtime (ART) • VM executing the classes[N].dex ART >

    Android’s VM • Dalvik optimized for memory, ART optimizes for performance
  56. Android Runtime (ART) • On KitKat and before, runtime was

    Dalvik VM A .dex file can hold at most 2^16 = 65,536 methods. Multidex applications have multiple .dex files. • Up until Lollipop, ART was introduced ART
  57. Android Runtime (ART) • ART keeps an uninitialized VM (zygote)

    • When an app launches, the zygote calls fork() and has an instant copy of an empty app • The process then starts executing app code ART
  58. ART Internals • Not exactly ELF+dex • Intuition: running a

    native binary is much faster than running a virtual machine (ART/Dalvik) executing bytecode - > We want to run native code where possible • So, Android executes our code in .dex files via ART runtime
  59. Comparison w/ C-like model • Native code is architecture-specific •

    C on Linux compiles programs in ELF format (Executable and Linkable Format), used throughout the Kernel • ELF is a container for native code • Maps easily its contents to memory GCC-x86 C x86 armv7 C ++ llvm-arm
  60. Comparison w/ C-like model Heap Stack pc Runtime Constant Pool

    Method Area Heap Stack .rodata .text .bss 0x00008000 high addresses
  61. Assembly analysis: adder.c int main() { int a = 15;

    int b = 10; return a + b; } adder.c gcc -S -masm=intel -O0 .section __ TEXT, __ text .globl _main _main: ## @main push rbp mov rbp, rsp mov dword ptr [rbp - 4], 0 mov dword ptr [rbp - 8], 15 mov dword ptr [rbp - 12], 10 mov eax, dword ptr [rbp - 8] add eax, dword ptr [rbp - 12] pop rbp ret ELF executable
  62. Compilation Types ELF+dex Techniques used to convert .dex classes into

    a lower level Ahead Of Time Just in Time Before application execution, portions of classes.dex is compiled into native code While the app is executing, the runtime profiles frquently used methods (hot), and compiles the to native code [5]
  63. dex2oat • Every Android release since Lollipop has /system/bin/dex2oat •

    Takes in .dex, produces .oat • OAT files are ELF binaries! • OAT format changes every android release, triggering recompilation of all installed apps ELF+dex “Optimizing app $X” means “executing dex2oat on app $X” [6]
  64. OAT files • Implemented in ELF container (used throughout android,

    enables mapping sections into memory: .so files are ELF) • OAT files “contains” a mix of compiled native code (optimized for the device architecture) and some dex (to be executed in the Dalvik virtual machine) OAT [7]
  65. OAT files • On app installation, installd runs dex2oat on

    the app classes.dex, generating a OAT file • OAT file contains a section (oatexec), containing some compiled DEX code, in the native device architecture • As the app is ran, reachable hot code is compiled JIT by a thread pool, and added to oatexec • Name of the game is to JIT the right things -> PGO • Execution flow switches from VM to native compiled methods OAT [7]
  66. OAT files > PGO • Compilation can be aided by

    profiles, instructing dex2oat on which code paths to optimize • Profiles distributed by google play with app install/updates App-version specific [8, 9]
  67. *.kt *.java kotlinc javac *.class Compilation Compile the source files

    with the java and kotlin compilers, generating standard *.class files
  68. *.dex d8/r8 kotlinc javac *.class D8 & R8 Convert class

    files into dex files. •D8 for debug •R8 for release [10]
  69. *.dex d8/r8 Packaging The *.dex classes are joined with resources

    and native libraries into an apk file Resources Native Libs aapt + .so .apk
  70. Zipalign - Aligns files inside the APK on a 4-byte

    boundary - Aligns files on page boundary as well (4KB) signer zipalign keystore COMPILE TIME INSTALL TIME Signing - Developer public key is added - Protected sections are hashed and signed [10] Aligning files makes loading via mmap(2) instead of read(2) .apk .apk
  71. *.dex d8/r8 *.kt *.java kotlinc javac *.class Resources Native Libs

    aapt + .so .apk signer zipalign keystore .apk
  72. *.so ELF .dex .oat dex2oat INSTALL TIME EXECUTION TIME ART

    Native /apex/com.android.art/bin/dalvikvm /system/bin/linker profile profman
  73. Re-compilation is tricky •Intuitively, compiling an application from dex to

    native code should make it run faster •In practice it’s tricky to choose how to implement this type of optimization - > When do I re-compile the app? Install-time or run-time? - > Do I recompile all the app? Or just some parts? - > Impact on battery, storage, CPU usage, device life TLDR: Difficult implementation; changes every Android update
  74. War story •Back in Froyo, early Dalvik VM, enum DEX

    implementation used a ton of memory ( >> int) on extremely constrained devices -> enums became an anti-pattern •This is an example of how architectural details have repercussions on developers “Premature optimization is the root of all evil”, Donald Knuth “80/20”, Pareto
  75. Language 1. @Metadata 2. Extensions 3. Conventions 4. Destricturing Declarations

    5. Default Arguments when guards @Metadata conventions extensions default arguments destructuring
  76. Preface • Language Features can be implemented by: -> Compiler

    (code generation / rearranging) -> Type system (compile-time errors) • Language design constrained by: - > JVM rules (or platform) -> Backward compatibility • Useful thinking: imagine how you would implement something, given the JVM constraints
  77. Preface • We’ll assume Kotlin 2.1 • Targeting JVM •

    Non-optimized bytecode • Cleaned-up decompilation
  78. class Counter { private var count: Int = 0 fun

    increment() { count += 1 } }
  79. @Metadata( mv = {2, 0, 0}, k = 1, xi

    = 48, d1 = {"\u0000\u0018\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u000 d2 = {"LK00_meta/Counter;", "", "<init>", "()V", "count", "", "inc ) public final class CounterKt { private int count; public final void increment() { ++ this.count; } }
  80. @Metadata • Kotlin needs to persist additional information to the

    .class Recall: JVM class files can’t store any Kotlin-specific information • It adds a @Metadata annotation to each class, containing those information -> Nullability, Declaration-site variance, Reflection, [ . .. ] • Also, kotlin class names are suffixed with “Kt”, so that it can avoid naming conflicts with Java classes
  81. Nullability > Intrinsics • Part of kotlin.jvm.internal • Injected by

    the compiler to enforce null-safety • Contains checker methods and assertions -> checkNotNullExpressionValue() -> checkNotNull() -> checkReturnedValueIsNotNull() -> throwNpe() -> checkFieldIsNotNull() -> checkParameterIsNotNull()
  82. Extension Functions • Simple mechanism: creates a static method accepting

    the receiver object as the first type • Allows to integrate classes you didn’t write: it’s enough to have access to the classfile • Can’t access private fields/methods of the object
  83. // Java (decompiled) @NotNull public static final String lastChar(@NotNull String

    $this$lastChar) { Intrinsics.checkNotNullParameter($this$lastChar, "<this>"); return String.valueOf($this$lastChar.charAt($this$lastChar.length() - 1)); }
  84. Conventions • Java integrates classes implementing certain interfaces with language

    features -> Iterable in for loops, AutoCloseable in try-with-resources • Kotlin formalizes this notion into conventions • Achieved by declaring functions with specific names -> Brings operator overloading cleanly into the syntax Conventions enable language features through certain functions, instead of relying on magical ad-hoc statements
  85. Conventions w/ Extension Functons By implementing conventions via function declaration,

    we can enrich existing classes with extension functions! • Self-contained: Kotlin extension functions work on top of already existing platform code • Most of the kotlin collections are implemented this way • Java can’t enrich already existing code, since its syntax requires interface implementation -> e.g. T implementing java.lang.Iterable -> e.g. defining T.iterator(), for any T
  86. Conventions for ((k,v) in map) { [ ... ] }

    val a = 0xCA and 0xFE val b = 0xBA xor 0xBE list += Element() V[x,y,z]
  87. Destructuring Declarations • Another convention! • Implemented by defining T.componentN()

    functions • Allows for extracting multiple data fields all at once -> (F1, …,FN ) = T • Destructuring declarations are positional • Can be ignored by supplying _ as the variable name
  88. / * Kotlin */ class Point(val x: Int, val y:

    Int) { operator fun component1(): Int = x operator fun component2(): Int = y }
  89. // Java (decompiled) public final class Point { private final

    int x; private final int y; public Point(int x, int y) { this.x = x; this.y = y; } public final int getX() { return this.x; } public final int getY() { return this.y; }
  90. } public final int getX() { return this.x; } public

    final int getY() { return this.y; } public final int component1() { return this.x; } public final int component2() { return this.y; } }
  91. // Java (decompiled) Point p = new Point(10, 20); int

    px = p.component1(); int py = p.component2(); /* Kotlin */ val p = Point(10, 20) val (px, py) = p
  92. // Java (decompiled) Point p = new Point(10, 20); int

    px = p.component1(); int py = p.component2(): /* Kotlin */ val p = Point(10, 20) val (px, _) = p
  93. Equality Checks • Kotlin: -> Equality operator always converted in

    .equals() -> If implemented, provides value-based comparison • Java: -> “ == “ on primitive types compares value -> “ == “ on reference types compares reference value (same object) -> Informal “convention” of using .equals() for value comparison == in kotlin ! = = = in java
  94. Default Arguments • The JVM doesn’t support default arguments for

    methods • Kotlin implements default parameters on top of that DefaultArgs.kt fun padding(top: Int = 10, bottom: Int = 10, left: Int = 24, right: Int = 24) { println("$top $bottom $left $right") } • Design challenge: think about how you would implement this
  95. Default Arguments • Key points: -> Should replace missing values

    with their default -> Should preserve introperability with java • Challenges: -> Any value combination can be missing -> Can’t generate as many functions as possible inputs
  96. /* Kotlin */ fun padding(top: Int = 10, bottom: Int

    = 10, left: Int = 24, right: Int = 24) { println("$top $bottom $left $right") }
  97. // Java (decompiled) public final class DefaultKt { public static

    final void padding(int top, int bottom, int left, int right) { String var4 = "" + top + ' ' + bottom + ' ' + left + ' ' + right; System.out.println(var4); } // /* Kotlin */ fun padding(top: Int = 10, bottom: Int = 10, left: Int = 24, right: Int = 24) { println("$top $bottom $left $right") }
  98. // $FF: synthetic method public static void padding$default(int var0, int

    var1, int var2, int var3, int var4, Object var5) { if ((var4 & 1) != 0) { var0 = 10; } if ((var4 & 2) != 0) { var1 = 10; } if ((var4 & 4) != 0) { var2 = 24; } if ((var4 & 8) != 0) { var3 = 24; } padding(var0, var1, var2, var3); }
  99. Default Arguments • Two methods are generated: -> Normal method

    and body (same as declared, no defaults) -> Helper synthetic method (method name appended with “$default”) • Any call f with any default argument will result in the compiler emitting a method call to the method’s f$default helper Basically, a call without all arguments isn’t considered complete and will need to go through the default-enhancing method Java code will be able to call the standard method, no defaults
  100. Default Arguments • Two+ parameters are added to the default-enhanching

    method: -> Bitmask, flagging which parameters haven’t been passed -> DefaultConstructorMarker for signature collision • If parameter 0 is absent, bitmask += 20=1 • If parameter 1 is absent, bitmask += 21=2 • If parameter 2 is absent, bitmask += 22=4 var0 absent var1 present 0 0 0 0 1 1 0 1
  101. fun fun_max_arg_nodfu( arg000: Int=0, arg001: Int=0, arg002: Int=0, arg003: Int=0,

    arg004: Int=0, arg005: Int=0, arg006: Int=0, arg007: I arg010: Int=0, arg011: Int=0, arg012: Int=0, arg013: Int=0, arg014: Int=0, arg015: Int=0, arg016: Int=0, arg017: I arg020: Int=0, arg021: Int=0, arg022: Int=0, arg023: Int=0, arg024: Int=0, arg025: Int=0, arg026: Int=0, arg027: I arg030: Int=2, arg031: Int=4, arg032: Int=8, arg033: Int=0, arg034: Int=0, arg035: Int=0, arg036: Int=0, arg037: I arg040: Int=0, arg041: Int=0, arg042: Int=0, arg043: Int=0, arg044: Int=0, arg045: Int=0, arg046: Int=0, arg047: I arg050: Int=0, arg051: Int=0, arg052: Int=0, arg053: Int=0, arg054: Int=0, arg055: Int=0, arg056: Int=0, arg057: I arg060: Int=0, arg061: Int=0, arg062: Int=0, arg063: Int=0, arg064: Int=0, arg065: Int=0, arg066: Int=0, arg067: I arg070: Int=0, arg071: Int=0, arg072: Int=0, arg073: Int=0, arg074: Int=0, arg075: Int=0, arg076: Int=0, arg077: I arg080: Int=0, arg081: Int=0, arg082: Int=0, arg083: Int=0, arg084: Int=0, arg085: Int=0, arg086: Int=0, arg087: I arg090: Int=0, arg091: Int=0, arg092: Int=0, arg093: Int=0, arg094: Int=0, arg095: Int=0, arg096: Int=0, arg097: I arg100: Int=0, arg101: Int=0, arg102: Int=0, arg103: Int=0, arg104: Int=0, arg105: Int=0, arg106: Int=0, arg107: I arg110: Int=0, arg111: Int=0, arg112: Int=0, arg113: Int=0, arg114: Int=0, arg115: Int=0, arg116: Int=0, arg117: I arg120: Int=0, arg121: Int=0, arg122: Int=0, arg123: Int=0, arg124: Int=0, arg125: Int=0, arg126: Int=0, arg127: I arg130: Int=0, arg131: Int=0, arg132: Int=0, arg133: Int=0, arg134: Int=0, arg135: Int=0, arg136: Int=0, arg137: I arg140: Int=0, arg141: Int=0, arg142: Int=0, arg143: Int=0, arg144: Int=0, arg145: Int=0, arg146: Int=0, arg147: I arg150: Int=0, arg151: Int=0, arg152: Int=0, arg153: Int=0, arg154: Int=0, arg155: Int=0, arg156: Int=0, arg157: I arg160: Int=0, arg161: Int=0, arg162: Int=0, arg163: Int=0, arg164: Int=0, arg165: Int=0, arg166: Int=0, arg167: I arg170: Int=0, arg171: Int=0, arg172: Int=0, arg173: Int=0, arg174: Int=0, arg175: Int=0, arg176: Int=0, arg177: I arg180: Int=0, arg181: Int=0, arg182: Int=0, arg183: Int=0, arg184: Int=0, arg185: Int=0, arg186: Int=0, arg187: I arg190: Int=0, arg191: Int=0, arg192: Int=0, arg193: Int=0, arg194: Int=0, arg195: Int=0, arg196: Int=0, arg197: I arg210: Int=0, arg211: Int=0, arg212: Int=0, arg213: Int=0, arg214: Int=0, arg215: Int=0, arg216: Int=0, arg217: I arg220: Int=0, arg221: Int=0, arg222: Int=0, arg223: Int=0, arg224: Int=0, arg225: Int=0, arg226: Int=0, arg227: I arg230: Int=0, arg231: Int=0, arg232: Int=0, arg233: Int=0, arg234: Int=0, arg235: Int=0, arg236: Int=0, arg237: I arg240: Int=0, arg241: Int=0, arg242: Int=0, arg243: Int=0, arg244: Int=0, arg245: Int=0, arg246: Int=0, arg247: I arg250: Int=0, arg251: Int=0, arg252: Int=0, arg253: Int=0, arg254: Int=0, arg255: Int=0, arg256: Int=0, arg257: I arg260: Int=0, arg261: Int=0, arg262: Int=0, arg263: Int=0, arg264: Int=0, arg265: Int=0, arg266: Int=0, arg267: I arg270: Int=0, arg271: Int=0, arg272: Int=0, arg273: Int=0, arg274: Int=0, arg275: Int=0, arg276: Int=0, arg277: I arg280: Int=0, arg281: Int=0, arg282: Int=0, arg283: Int=0, arg284: Int=0, arg285: Int=0, arg286: Int=0, arg287: I
  102. @Jvm* • Annotations for controlling bytecode compilation -> @JvmStatic: converts

    function to static java method -> @JvmInline: marks an inline class -> @JvmRecord: makes a data class a record -> @JvmName(“f”): sets the name of the method/field
  103. Thank you! • Slides on my Twitter @cioccarellia / Bluesky

    @cioccarelli • For playing around with disassembly/DEX/OAT, check out kotlin- explorer