[Kotlin] Get rid of Kotlin generated check — “null cannot be cast to non-null type”
Use case 1:
The code below is to cast (as) a nullable variable:
data class Boss(val name: String)
data class Staff(val name: String)
fun getPerson(type: String, name: String): Any? {
return when (type) {
"boss" -> Boss(name)
"staff" -> Staff(name)
else -> null
}
}fun main() {
val boss = getPerson("boss", "Peter") as Boss
print(boss)
}
Highlighted points:
- getPerson() might return null
- Boss and Staff are supposed to be obfuscated and no clues in getPerson()
Let’s look at the decompiled code below:
/* This is Boss */
public static final class C0374a { /* renamed from: a */
public final String f8550a; public C0374a(String str) {
this.f8550a = str;
}
...}/* This is Staff */
public static final class C0379c { /* renamed from: a */
public final String f8556a; public C0379c(String str) {
this.f8556a = str;
}
...}/* This is getPerson(String, String) */
public final Object mo12141a(String str, String str2) {
int hashCode = str.hashCode();
if (hashCode != 3029869) {
if (hashCode == 109757152 && str.equals("staff")) {
return new C0379c(str2);
}
} else if (str.equals("boss")) {
return new C0374a(str2);
}
return null;
}/* This is main() */
public void mo12147a(boolean z) {
Object a = mo12141a("Boss", "Peter");
if (a != null) {
System.out.print((C0374a) a);
return;
}
throw new C3147k("null cannot be cast to non-null type Boss");
}
Highlighted points:
- In the obfuscated code, Boss is still visible
How to prevent null checkings in generated code ?
Solution A:
fun main() {
val temp = getPerson("boss", "Peter")
if (temp != null) {
val boss = temp as Boss
print(boss)
}
}
Solution B:
fun main() {
val boss = getPerson("boss", "Peter") as? Boss
print(boss?.name ?: "no boss")
}
Use case 2:
Slighly different from use case1:
open class A {
open fun print() {
}
}
class A1 : A() {
override fun print() {
print("A1")
}
}
class A2 : A() {
override fun print() {
print("B1")
}
}/**
* This function won't return null
*/
fun getA(isA1: Boolean): Any {
return if (isA1) {
A1()
} else {
A2()
}
}
Look at the code below:
Non-obfuscated:
(getA(true) as A1).print()Obfuscated:
Object c = mo12151c(true);
if (c != null) {
((C0374a) c).mo12159a();
}
// A1 is explored !!
throw new C3207k("null cannot be cast to non-null type A1");
Using same way to pretend:
val a1 = getA(true)
if (a1 != null) (a1 as A1).print()