battlecode / battlecode-server-2017

Official Battlecode game engine.
http://www.battlecode.org/
GNU Affero General Public License v3.0
23 stars 10 forks source link

Kotlin support #402

Open Pear0 opened 7 years ago

Pear0 commented 7 years ago

Kotlin support would be great and nearly works but Kotlin relies on kotlin/Intrinsics and other stdlib classes for many operations that it performs.

Even the most basic RobotPlayer will be rejected by the instrumenter because Kotlin inserts non-null checks using Intrinsics.requireNonNull.

@file:JvmName("RobotPlayer")

package kotlin_test

import battlecode.common.RobotController

fun run(rc: RobotController) {
    println("Hello World")
}

These particular checks can be disabled with -Xno-param-assertions and -Xno-call-assertions, but Kotlin relies on its stdlib for all kinds of things which can't be disabled.

fun check(x: String?) {
    val y = x!! // inserts call to Intrinsics.throwNpe()
    ...
}
fun listOperations() {
    val a = 1..6 // references kotlin/ranges/IntRange
    val b = a.map { it * 2 } // references kotlin/collections/CollectionsKt
}

I'm using Kotlin 1.0.6 if that matters.

HalfVoxel commented 7 years ago

See also http://battlecodeforum.org/t/anyone-interested-in-kotlin/134

Regarding the nonNull checks and similar things, it would be great if the instrumenter specifically made such assertions have a 0 cost to avoid penalizing bots just based on the language used even if the algorithms are exactly the same.

Pear0 commented 7 years ago

Because of Kotlin's pretty extensive interop features, the only thing needed to get it working should be allowing kotlin's stdlib packages.

I agree with @HalfVoxel though, Intrinsics should probably be ignored bytecode-wise, and possibly Typeintrinsics too.

I'll make a pull request for this if one of the project members weighs in on whether to count Intrinsics.

kazimuth commented 7 years ago

@Pear0 That would be great. Intrinsics should definitely be free. kotlin.reflect should be disabled, unfortunately; this prevents doing things like ::method, but is the best way to do things safely without exposing too many new attack surfaces on the battlecode infrastructure. We can loosen the restrictions once we've audited kotlin.reflect, but I at least don't have enough time to do that right now :/

I'm sorry this feature has taken so long, we've been busy putting out other fires.

Pear0 commented 7 years ago

Some things I've found:

As it turns out, because Kotlin targets 1.6, it doesn't use invokedynamic. It generates inner class wrappers for method and constructor references so ::Random and String::equals. (they still don't work because they reference interfaces in kotlin/reflect)

Disallowing kotlin/reflect also has the side-effect of disabling delegators which rely on kotlin/reflect/KProperty.