Skip to content

Commit 0078ac7

Browse files
author
Andrei Shikov
committed
Migrate to kotlin 1.3.70 and add integration test
1 parent 3037690 commit 0078ac7

File tree

8 files changed

+132
-38
lines changed

8 files changed

+132
-38
lines changed

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
plugins {
22
id 'java'
3-
id 'org.jetbrains.kotlin.jvm' version '1.3.61'
3+
id 'org.jetbrains.kotlin.jvm' version '1.3.70'
44
}
55

66
sourceCompatibility = 1.8

integration-test/build.gradle

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
apply plugin: "org.jetbrains.kotlin.jvm"
2+
3+
dependencies {
4+
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
5+
testImplementation 'junit:junit:4.12'
6+
7+
kotlinCompilerPluginClasspath project(':kotlin-plugin')
8+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import org.junit.Assert.assertEquals
2+
import org.junit.Test
3+
import java.io.ByteArrayInputStream
4+
import java.io.ByteArrayOutputStream
5+
import java.io.ObjectInputStream
6+
import java.io.ObjectOutputStream
7+
import java.io.Serializable
8+
9+
class ObjectSerializationIntegrationTest {
10+
private object TestObject : Serializable
11+
12+
@Test
13+
fun `object instance is the same after deserialization`() {
14+
assertEquals(TestObject, serializeDeserialize(TestObject))
15+
}
16+
17+
private fun serializeDeserialize(instance: Serializable): Serializable {
18+
val outputStream = ByteArrayOutputStream()
19+
ObjectOutputStream(outputStream).use {
20+
it.writeObject(instance)
21+
}
22+
return ObjectInputStream(ByteArrayInputStream(outputStream.toByteArray())).use {
23+
it.readObject() as TestObject
24+
}
25+
}
26+
}

kotlin-plugin/src/main/kotlin/me/shika/ComponentRegistrar.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class ObjectSerializationComponentRegistrar @JvmOverloads constructor(
1313
private val enabled: Boolean = false
1414
): ComponentRegistrar {
1515
override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) {
16-
if (configuration[KEY_ENABLED] != true && !enabled) {
16+
if (configuration[KEY_ENABLED] == false && !enabled) {
1717
return
1818
}
1919

kotlin-plugin/src/main/kotlin/me/shika/generation/ObjectSerializationIRGeneration.kt

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package me.shika.generation
22

3-
import org.jetbrains.kotlin.backend.common.BackendContext
43
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
5-
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
4+
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
5+
import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder
66
import org.jetbrains.kotlin.descriptors.Visibilities
77
import org.jetbrains.kotlin.ir.IrElement
88
import org.jetbrains.kotlin.ir.builders.declarations.addFunction
@@ -11,36 +11,35 @@ import org.jetbrains.kotlin.ir.builders.irGetObject
1111
import org.jetbrains.kotlin.ir.builders.irReturn
1212
import org.jetbrains.kotlin.ir.declarations.IrClass
1313
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOriginImpl
14-
import org.jetbrains.kotlin.ir.declarations.IrFile
14+
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
1515
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
1616
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
1717
import org.jetbrains.kotlin.ir.visitors.acceptVoid
18-
import org.jetbrains.kotlin.name.Name
19-
import org.jetbrains.kotlin.resolve.BindingContext
2018

2119
private object ObjectSerializationOrigin : IrDeclarationOriginImpl("object-serialization-fix", isSynthetic = true)
2220

2321
class ObjectSerializationIrGeneration : IrGenerationExtension {
24-
override fun generate(file: IrFile, backendContext: BackendContext, bindingContext: BindingContext) {
25-
file.acceptVoid(
22+
override fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) {
23+
moduleFragment.acceptVoid(
2624
recursiveIrClassVisitor {
2725
if (it.descriptor.needSerializableFix()) {
28-
fixSerializable(it, backendContext)
26+
fixSerializable(it, pluginContext)
2927
}
3028
}
3129
)
3230
}
3331

34-
private fun fixSerializable(cls: IrClass, context: BackendContext) {
32+
private fun fixSerializable(cls: IrClass, context: IrPluginContext) {
3533
cls.addFunction {
36-
name = Name.identifier(SERIALIZABLE_READ)
34+
name = SERIALIZABLE_READ
3735
returnType = context.irBuiltIns.anyNType
3836
visibility = Visibilities.PUBLIC
3937
origin = ObjectSerializationOrigin
4038
}.also { function ->
41-
function.body = context.createIrBuilder(function.symbol).irBlockBody {
42-
+irReturn(irGetObject(cls.symbol))
43-
}
39+
function.body = DeclarationIrBuilder(context, function.symbol, function.startOffset, function.endOffset)
40+
.irBlockBody {
41+
+irReturn(irGetObject(cls.symbol))
42+
}
4443
}
4544
}
4645

kotlin-plugin/src/main/kotlin/me/shika/generation/ObjectSerializationJvmGeneration.kt

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,32 @@ class ObjectSerializationJvmGeneration : ExpressionCodegenExtension {
1414
if (codegen.descriptor.needSerializableFix()) {
1515
val selfType = codegen.typeMapper.mapType(codegen.descriptor)
1616

17-
val visitor = codegen.v.newMethod(
18-
NO_ORIGIN,
19-
ACC_PUBLIC or ACC_SYNTHETIC,
20-
SERIALIZABLE_READ,
21-
"()Ljava/lang/Object;",
22-
null,
23-
EMPTY_STRING_ARRAY
24-
)
25-
26-
visitor.visitCode()
27-
val iv = InstructionAdapter(visitor)
28-
iv.getstatic(codegen.className, "INSTANCE", selfType.descriptor)
29-
iv.areturn(selfType)
30-
FunctionCodegen.endVisit(iv, "JVM serialization bindings")
17+
codegen.addFunction(SERIALIZABLE_READ.identifier, "()Ljava/lang/Object;") {
18+
getstatic(codegen.className, "INSTANCE", selfType.descriptor)
19+
areturn(selfType)
20+
}
3121
}
3222

3323
}
24+
25+
private fun ImplementationBodyCodegen.addFunction(
26+
name: String,
27+
asmDescriptor: String,
28+
block: InstructionAdapter.() -> Unit
29+
) {
30+
val visitor = v.newMethod(
31+
NO_ORIGIN,
32+
ACC_PUBLIC or ACC_SYNTHETIC,
33+
name,
34+
asmDescriptor,
35+
null,
36+
EMPTY_STRING_ARRAY
37+
)
38+
39+
visitor.visitCode()
40+
val iv = InstructionAdapter(visitor)
41+
iv.apply(block)
42+
FunctionCodegen.endVisit(iv, "JVM serialization bindings")
43+
}
44+
3445
}

kotlin-plugin/src/main/kotlin/me/shika/generation/utils.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
1010
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
1111
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperInterfaces
1212
import org.jetbrains.kotlin.resolve.descriptorUtil.module
13+
import java.io.Serializable
1314

1415
fun ClassDescriptor.needSerializableFix() =
1516
module.platform.has<JvmPlatform>()
@@ -18,12 +19,11 @@ fun ClassDescriptor.needSerializableFix() =
1819
&& !hasReadMethod()
1920

2021
fun ClassDescriptor.hasReadMethod() =
21-
unsubstitutedMemberScope.getFunctionNames().contains(Name.identifier(SERIALIZABLE_READ))
22+
unsubstitutedMemberScope.getFunctionNames().contains(SERIALIZABLE_READ)
2223

2324
fun ClassDescriptor.isSerializable(): Boolean =
24-
getSuperInterfaces().any { it.fqNameSafe == SERIALIZABLE_FQ_NAME }
25+
getSuperInterfaces().any { it.fqNameSafe == SERIALIZABLE_FQ_NAME || it.isSerializable() }
2526
|| getSuperClassNotAny()?.isSerializable() == true
2627

27-
const val SERIALIZABLE_READ = "readResolve"
28-
val SERIALIZABLE_FQ_NAME = FqName("java.io.Serializable")
29-
28+
val SERIALIZABLE_READ = Name.identifier("readResolve")
29+
val SERIALIZABLE_FQ_NAME = FqName(Serializable::class.java.name)

kotlin-plugin/src/test/kotlin/me/shika/ObjectSerializationFixTest.kt

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class ObjectSerializationFixTest(enableIr: Boolean) {
3434
val result = compiler.compile()
3535

3636
val klass = result.classLoader.loadClass("Serial")
37-
assertTrue(klass.methods.any { it.isReadResolve()})
37+
assertTrue(klass.methods.any { it.addedReadResolve()})
3838
}
3939

4040
private val NOT_SERIALIZABLE_OBJECT = """
@@ -47,7 +47,7 @@ class ObjectSerializationFixTest(enableIr: Boolean) {
4747
val result = compiler.compile()
4848

4949
val klass = result.classLoader.loadClass("NotSerial")
50-
assertTrue(klass.methods.none { it.isReadResolve() })
50+
assertTrue(klass.methods.none { it.addedReadResolve() })
5151
}
5252

5353
private val SERIALIZABLE_PARENT = """
@@ -63,10 +63,60 @@ class ObjectSerializationFixTest(enableIr: Boolean) {
6363
val result = compiler.compile()
6464

6565
val klass = result.classLoader.loadClass("SerialChild")
66-
assertTrue(klass.methods.any { it.isReadResolve() })
66+
assertTrue(klass.methods.any { it.addedReadResolve() })
6767
}
6868

69-
private fun Method.isReadResolve() =
69+
private val SERIALIZABLE_PARENT_INTERFACE = """
70+
import java.io.Serializable
71+
72+
interface SerialParent : Serializable
73+
object SerialChild : SerialParent
74+
""".source()
75+
76+
@Test
77+
fun `adds readResolve to obj extending serializable interface`() {
78+
compiler.sources = listOf(SERIALIZABLE_PARENT_INTERFACE)
79+
val result = compiler.compile()
80+
81+
val klass = result.classLoader.loadClass("SerialChild")
82+
assertTrue(klass.methods.any { it.addedReadResolve() })
83+
}
84+
85+
private val SERIALIZABLE_READ_EXISTS = """
86+
import java.io.Serializable
87+
88+
object SerialChild : Serializable {
89+
fun readResolve() = SerialChild
90+
}
91+
""".source()
92+
93+
@Test
94+
fun `does not add readResolve to obj if read resolve already exits`() {
95+
compiler.sources = listOf(SERIALIZABLE_READ_EXISTS)
96+
val result = compiler.compile()
97+
98+
val klass = result.classLoader.loadClass("SerialChild")
99+
assertTrue(klass.methods.none { it.addedReadResolve() })
100+
}
101+
102+
private val SERIALIZABLE_READ_EXISTS_PARENT = """
103+
import java.io.Serializable
104+
105+
object SerialChild : Serializable {
106+
fun readResolve() = SerialChild
107+
}
108+
""".source()
109+
110+
@Test
111+
fun `does not add readResolve to obj if read resolve already exits in parent`() {
112+
compiler.sources = listOf(SERIALIZABLE_READ_EXISTS_PARENT)
113+
val result = compiler.compile()
114+
115+
val klass = result.classLoader.loadClass("SerialChild")
116+
assertTrue(klass.methods.none { it.addedReadResolve() })
117+
}
118+
119+
private fun Method.addedReadResolve() =
70120
name == "readResolve"
71121
&& parameterCount == 0
72122
&& returnType == Object::class.java

0 commit comments

Comments
 (0)