Skip to content

Commit 2d0d198

Browse files
authored
Add initial Kotlin support (#1457)
* Remove copying base class metadata to child * Add initial support for Kotlin * Make Kotlin support enabled via gradle property
1 parent 5ab02e8 commit 2d0d198

49 files changed

Lines changed: 1352 additions & 4309 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

test-app/app/build.gradle

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ import java.security.MessageDigest
3030

3131
apply plugin: "com.android.application"
3232

33+
def useKotlin = project.hasProperty("useKotlin") && project.ext.useKotlin=="true"
34+
if (useKotlin) {
35+
apply plugin: 'kotlin-android'
36+
apply plugin: 'kotlin-android-extensions'
37+
}
38+
3339
def onlyX86 = project.hasProperty("onlyX86")
3440
if (onlyX86) {
3541
println "OnlyX86 build triggered."
@@ -64,6 +70,7 @@ def METADATA_OUT_PATH = "$projectDir/src/main/assets/metadata"
6470
def pluginsJarLibraries = new LinkedList<String>()
6571
def allJarLibraries = new LinkedList<String>()
6672

73+
def computeKotlinVersion = { -> project.hasProperty("kotlinVersion") ? kotlinVersion : "1.3.41" }
6774
def computeCompileSdkVersion = { -> project.hasProperty("compileSdk") ? compileSdk : 29 }
6875
def computeTargetSdkVersion = { -> project.hasProperty("targetSdk") ? targetSdk : 29 }
6976
def computeBuildToolsVersion = { ->
@@ -220,6 +227,13 @@ def setAppIdentifier = { ->
220227
}
221228

222229
android {
230+
231+
if (useKotlin) {
232+
kotlinOptions {
233+
jvmTarget = '1.8'
234+
}
235+
}
236+
223237
compileSdkVersion computeCompileSdkVersion()
224238
buildToolsVersion computeBuildToolsVersion()
225239

@@ -305,6 +319,7 @@ repositories {
305319
dirs pluginDependencies
306320
}
307321
}
322+
mavenCentral()
308323
}
309324

310325
dependencies {
@@ -325,7 +340,7 @@ dependencies {
325340

326341
println "\t + using android X library androidx.legacy:legacy-support-v4:$androidXLegacyVersion"
327342

328-
implementation "androidx.multidex:multidex:2.0.0"
343+
implementation "androidx.multidex:multidex:2.0.1"
329344
implementation "androidx.legacy:legacy-support-v4:$androidXLegacyVersion"
330345
implementation "androidx.appcompat:appcompat:$androidXAppCompatVersion"
331346
implementation "com.google.android.material:material:$androidXMaterialVersion"
@@ -364,6 +379,11 @@ dependencies {
364379
implementation project(':runtime')
365380
}
366381

382+
def kotlinVersion = computeKotlinVersion()
383+
if (useKotlin) {
384+
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion"
385+
}
386+
367387
}
368388

369389
////////////////////////////////////////////////////////////////////////////////////
@@ -686,6 +706,11 @@ task buildMetadata(type: JavaExec) {
686706
def classesDir = "$buildDir/intermediates/javac"
687707
inputs.dir(classesDir)
688708

709+
def kotlinClassesDir = "$buildDir/tmp/kotlin-classes"
710+
if (file(kotlinClassesDir).exists()) {
711+
inputs.dir(kotlinClassesDir)
712+
}
713+
689714
outputs.files("$METADATA_OUT_PATH/treeNodeStream.dat", "$METADATA_OUT_PATH/treeStringsStream.dat", "$METADATA_OUT_PATH/treeValueStream.dat")
690715

691716
doFirst {
@@ -703,6 +728,15 @@ task buildMetadata(type: JavaExec) {
703728
}
704729
}
705730

731+
if (file(kotlinClassesDir).exists()) {
732+
def kotlinClassesSubDirs = new File(kotlinClassesDir).listFiles()
733+
for (File subDir : kotlinClassesSubDirs) {
734+
if (subDir.getName() == selectedBuildType) {
735+
generatedClasses.add(subDir.getAbsolutePath())
736+
}
737+
}
738+
}
739+
706740
new File("$BUILD_TOOLS_PATH/$MDG_OUTPUT_DIR").withWriter { out ->
707741
out.println "$METADATA_OUT_PATH"
708742
}

test-app/app/src/main/assets/app/mainpage.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,9 @@ require("./tests/dex-interface-implementation");
5353
require("./tests/testInterfaceImplementation");
5454
require("./tests/testRuntimeImplementedAPIs");
5555
require("./tests/testsInstanceOfOperator");
56-
require("./tests/testReleaseNativeCounterpart");
56+
require("./tests/testReleaseNativeCounterpart");
57+
require("./tests/kotlin/companions/testCompanionObjectsSupport");
58+
require("./tests/kotlin/properties/testPropertiesSupport");
59+
require("./tests/kotlin/delegation/testDelegationSupport");
60+
require("./tests/kotlin/objects/testObjectsSupport");
61+
require("./tests/kotlin/functions/testTopLevelFunctionsSupport");
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
describe("Tests Kotlin companion objects support", function () {
2+
it("Test Kotlin companion object without a name should be supported", function () {
3+
var stringFromCompanion = com.tns.tests.kotlin.companions.KotlinClassWithCompanion.Companion.getStringFromCompanion();
4+
expect(stringFromCompanion).toBe("testCompanion");
5+
6+
var providedStringFromCompanion = com.tns.tests.kotlin.companions.KotlinClassWithCompanion.Companion.getProvidedStringFromCompanion("providedString");
7+
expect(providedStringFromCompanion).toBe("providedString");
8+
9+
var simpleObjectFromCompanion = com.tns.tests.kotlin.companions.KotlinClassWithCompanion.Companion.getSimpleObjectFromCompanion();
10+
expect(simpleObjectFromCompanion.getSomeString()).toBe("test");
11+
12+
var simpleKotlinObject = new com.tns.tests.kotlin.SimpleKotlinObject();
13+
var providedSimpleObjectFromCompanion = com.tns.tests.kotlin.companions.KotlinClassWithCompanion.Companion.getProvidedSimpleObjectFromCompanion(simpleKotlinObject);
14+
expect(simpleKotlinObject.equals(providedSimpleObjectFromCompanion)).toBe(true);
15+
16+
var stringJvmStaticFromCompanion = com.tns.tests.kotlin.companions.KotlinClassWithCompanion.getStringJvmStaticFromCompanion();
17+
expect(stringJvmStaticFromCompanion).toBe("testCompanion");
18+
19+
var providedStringJvmStaticFromCompanion = com.tns.tests.kotlin.companions.KotlinClassWithCompanion.getProvidedStringJvmStaticFromCompanion("providedString");
20+
expect(providedStringJvmStaticFromCompanion).toBe("providedString");
21+
});
22+
23+
it("Test Kotlin named companion object without a name should be supported", function () {
24+
var stringFromCompanion = com.tns.tests.kotlin.companions.KotlinClassWithNamedCompanion.NamedCompanion.getStringFromNamedCompanion();
25+
expect(stringFromCompanion).toBe("testCompanion");
26+
27+
var providedStringFromCompanion = com.tns.tests.kotlin.companions.KotlinClassWithNamedCompanion.NamedCompanion.getProvidedStringFromNamedCompanion("providedString");
28+
expect(providedStringFromCompanion).toBe("providedString");
29+
30+
var simpleObjectFromCompanion = com.tns.tests.kotlin.companions.KotlinClassWithNamedCompanion.NamedCompanion.getSimpleObjectFromNamedCompanion();
31+
expect(simpleObjectFromCompanion.getSomeString()).toBe("test");
32+
33+
var simpleKotlinObject = new com.tns.tests.kotlin.SimpleKotlinObject();
34+
var providedSimpleObjectFromCompanion = com.tns.tests.kotlin.companions.KotlinClassWithNamedCompanion.NamedCompanion.getProvidedSimpleObjectFromNamedCompanion(simpleKotlinObject);
35+
expect(simpleKotlinObject.equals(providedSimpleObjectFromCompanion)).toBe(true);
36+
37+
var stringJvmStaticFromCompanion = com.tns.tests.kotlin.companions.KotlinClassWithNamedCompanion.getStringJvmStaticFromNamedCompanion();
38+
expect(stringJvmStaticFromCompanion).toBe("testCompanion");
39+
40+
var providedStringJvmStaticFromCompanion = com.tns.tests.kotlin.companions.KotlinClassWithNamedCompanion.getProvidedStringJvmStaticFromNamedCompanion("providedString");
41+
expect(providedStringJvmStaticFromCompanion).toBe("providedString");
42+
});
43+
44+
45+
it("Test Kotlin interface companion object without a name should be supported", function () {
46+
var stringFromCompanion = com.tns.tests.kotlin.companions.KotlinInterfaceWithCompanion.Companion.getStringFromCompanion();
47+
expect(stringFromCompanion).toBe("testCompanion");
48+
49+
var providedStringFromCompanion = com.tns.tests.kotlin.companions.KotlinInterfaceWithCompanion.Companion.getProvidedStringFromCompanion("providedString");
50+
expect(providedStringFromCompanion).toBe("providedString");
51+
52+
var simpleObjectFromCompanion = com.tns.tests.kotlin.companions.KotlinInterfaceWithCompanion.Companion.getSimpleObjectFromCompanion();
53+
expect(simpleObjectFromCompanion.getSomeString()).toBe("test");
54+
55+
var simpleKotlinObject = new com.tns.tests.kotlin.SimpleKotlinObject();
56+
var providedSimpleObjectFromCompanion = com.tns.tests.kotlin.companions.KotlinInterfaceWithCompanion.Companion.getProvidedSimpleObjectFromCompanion(simpleKotlinObject);
57+
expect(simpleKotlinObject.equals(providedSimpleObjectFromCompanion)).toBe(true);
58+
59+
var stringJvmStaticFromCompanion = com.tns.tests.kotlin.companions.KotlinInterfaceWithCompanion.getStringJvmStaticFromCompanion();
60+
expect(stringJvmStaticFromCompanion).toBe("testCompanion");
61+
62+
var providedStringJvmStaticFromCompanion = com.tns.tests.kotlin.companions.KotlinInterfaceWithCompanion.getProvidedStringJvmStaticFromCompanion("providedString");
63+
expect(providedStringJvmStaticFromCompanion).toBe("providedString");
64+
});
65+
66+
it("Test Kotlin interface named companion object without a name should be supported", function () {
67+
var stringFromCompanion = com.tns.tests.kotlin.companions.KotlinInterfaceWithNamedCompanion.NamedCompanion.getStringFromNamedCompanion();
68+
expect(stringFromCompanion).toBe("testCompanion");
69+
70+
var providedStringFromCompanion = com.tns.tests.kotlin.companions.KotlinInterfaceWithNamedCompanion.NamedCompanion.getProvidedStringFromNamedCompanion("providedString");
71+
expect(providedStringFromCompanion).toBe("providedString");
72+
73+
var simpleObjectFromCompanion = com.tns.tests.kotlin.companions.KotlinInterfaceWithNamedCompanion.NamedCompanion.getSimpleObjectFromNamedCompanion();
74+
expect(simpleObjectFromCompanion.getSomeString()).toBe("test");
75+
76+
var simpleKotlinObject = new com.tns.tests.kotlin.SimpleKotlinObject();
77+
var providedSimpleObjectFromCompanion = com.tns.tests.kotlin.companions.KotlinInterfaceWithNamedCompanion.NamedCompanion.getProvidedSimpleObjectFromNamedCompanion(simpleKotlinObject);
78+
expect(simpleKotlinObject.equals(providedSimpleObjectFromCompanion)).toBe(true);
79+
80+
var stringJvmStaticFromCompanion = com.tns.tests.kotlin.companions.KotlinInterfaceWithNamedCompanion.getStringJvmStaticFromNamedCompanion();
81+
expect(stringJvmStaticFromCompanion).toBe("testCompanion");
82+
83+
var providedStringJvmStaticFromCompanion = com.tns.tests.kotlin.companions.KotlinInterfaceWithNamedCompanion.getProvidedStringJvmStaticFromNamedCompanion("providedString");
84+
expect(providedStringJvmStaticFromCompanion).toBe("providedString");
85+
});
86+
87+
it("Test Kotlin class whose parent class contains a companion should work", function () {
88+
var child = new com.tns.tests.kotlin.companions.ChildKotlinClass();
89+
var str = child.getStringFromCompanion();
90+
expect(str).toBe("someString");
91+
});
92+
93+
});
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
describe("Tests Kotlin delegation support", function () {
2+
it("Test Kotlin class implementation delegation should work", function () {
3+
var delegationObject = new com.tns.tests.kotlin.delegation.DelegationClass();
4+
expect(delegationObject.getString()).toBe("some string");
5+
});
6+
});
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
describe("Tests Kotlin top level functions support", function () {
2+
it("Test Kotlin top level functions in an unnamed class should work", function () {
3+
var res = com.tns.tests.kotlin.functions.TopLevelFunctionsKt.getRandomNumber();
4+
expect(res).toBe(42);
5+
});
6+
7+
it("Test Kotlin top level functions in a named class should work", function () {
8+
var res = com.tns.tests.kotlin.functions.DemoUtils.getRandomString();
9+
expect(res).toBe("42");
10+
});
11+
12+
it("Test Kotlin top level functions in a named multifile class should work", function () {
13+
var res1 = com.tns.tests.kotlin.multifiles.Utils.getNewString();
14+
expect(res1).toBe("new string");
15+
16+
var res2 = com.tns.tests.kotlin.multifiles.Utils.getOldString();
17+
expect(res2).toBe("old string");
18+
});
19+
});
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
describe("Tests Kotlin objects support", function () {
2+
it("Test Kotlin object instances should be the same reference", function () {
3+
var kotlinClass = com.tns.tests.kotlin.objects.KotlinSingleton.INSTANCE;
4+
var kotlinClass2 = com.tns.tests.kotlin.objects.KotlinSingleton.INSTANCE;
5+
expect(com.tns.EqualityComparator.areReferencesEqual(kotlinClass, kotlinClass2)).toBe(true);
6+
});
7+
});
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
describe("Tests Kotlin properties support", function () {
2+
it("Test Kotlin public properties should work", function () {
3+
var kotlinClass = new com.tns.tests.kotlin.properties.KotlinClassWithProperties();
4+
5+
expect(kotlinClass.immutableProperty).toBe("someImmutableProperty");
6+
try{
7+
kotlinClass.immutableProperty = "SHOULD NOT WORK";
8+
fail();
9+
} catch{}
10+
11+
expect(kotlinClass.mutableProperty).toBe("someMutableProperty");
12+
kotlinClass.mutableProperty = "someOtherMutableProperty";
13+
expect(kotlinClass.mutableProperty).toBe("someOtherMutableProperty");
14+
});
15+
16+
it("Test Kotlin private properties should not work", function () {
17+
var kotlinClass = new com.tns.tests.kotlin.properties.KotlinClassWithProperties();
18+
19+
expect(kotlinClass.privateMutableProperty).toBe(undefined);
20+
expect(kotlinClass.privateImmutableProperty).toBe(undefined);
21+
});
22+
23+
it("Test Kotlin internal properties should not work", function () {
24+
var kotlinClass = new com.tns.tests.kotlin.properties.KotlinClassWithProperties();
25+
26+
expect(kotlinClass.internalMutableProperty).toBe(undefined);
27+
expect(kotlinClass.internalImmutableProperty).toBe(undefined);
28+
});
29+
30+
it("Test Kotlin protected properties should work", function () {
31+
var kotlinClass = new (com.tns.tests.kotlin.properties.KotlinClassWithProperties.extend({
32+
getProtectedMutableProperty: function(){
33+
expect(this.super.protectedMutableProperty).toBe("someProtectedMutableProperty");
34+
this.super.protectedMutableProperty = "someOtherProtectedMutableProperty";
35+
expect(this.super.protectedMutableProperty).toBe("someOtherProtectedMutableProperty");
36+
},
37+
getProtectedImmutableProperty: function(){
38+
expect(this.super.protectedImmutableProperty).toBe("someProtectedImmutableProperty");
39+
try{
40+
this.super.protectedImmutableProperty = "SHOULD NOT WORK";
41+
fail();
42+
} catch {}
43+
}
44+
}))();
45+
46+
kotlinClass.getProtectedMutableProperty();
47+
kotlinClass.getProtectedImmutableProperty();
48+
});
49+
50+
51+
it("Test Kotlin property private should not work", function () {
52+
var kotlinClass = new com.tns.tests.kotlin.properties.KotlinClassWithProperties();
53+
try{
54+
kotlinClass.privateSetterProperty = "SHOULD NOT WORK";
55+
fail();
56+
} catch {}
57+
});
58+
59+
it("Test Kotlin boolean property should work", function () {
60+
var kotlinClass = new com.tns.tests.kotlin.properties.KotlinClassWithProperties();
61+
62+
expect(kotlinClass.isMutableBooleanProperty()).toBe(true);
63+
kotlinClass.setMutableBooleanProperty(false);
64+
expect(kotlinClass.isMutableBooleanProperty()).toBe(false);
65+
});
66+
67+
it("Test Kotlin property with complext type should work", function () {
68+
var kotlinClass = new com.tns.tests.kotlin.properties.KotlinClassWithProperties();
69+
70+
expect(kotlinClass.mutablePropertyWithComplexType.someString).toBe("test");
71+
72+
var simpleObject = new com.tns.tests.kotlin.SimpleKotlinObject();
73+
kotlinClass.mutablePropertyWithComplexType = simpleObject;
74+
expect(kotlinClass.mutablePropertyWithComplexType.equals(simpleObject)).toBe(true);
75+
});
76+
});
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.tns;
2+
3+
public final class EqualityComparator {
4+
public static boolean areReferencesEqual(Object a, Object b) {
5+
return a == b;
6+
}
7+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.tns.tests.kotlin
2+
3+
class SimpleKotlinObject {
4+
var someString: String = "test"
5+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.tns.tests.kotlin.companions
2+
3+
abstract class BaseKotlinClassWithCompanion {
4+
companion object {
5+
public fun getString() = "someString"
6+
}
7+
}

0 commit comments

Comments
 (0)