Skip to content

Commit b4123aa

Browse files
committed
Parcelable: Add Parcelable functionality to Android Extensions plugin
1 parent cd7e9b8 commit b4123aa

Some content is hidden

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

47 files changed

+2978
-3
lines changed

compiler/compiler.pro

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -186,9 +186,8 @@ messages/**)
186186
-keep class org.jetbrains.org.objectweb.asm.signature.SignatureReader { *; }
187187
-keep class org.jetbrains.org.objectweb.asm.signature.SignatureVisitor { *; }
188188

189-
-keepclassmembers class org.jetbrains.org.objectweb.asm.Type {
190-
*** ARRAY;
191-
*** OBJECT;
189+
-keep class org.jetbrains.org.objectweb.asm.Type {
190+
public protected *;
192191
}
193192

194193
-keepclassmembers class org.jetbrains.org.objectweb.asm.ClassReader {

compiler/frontend/src/org/jetbrains/kotlin/resolve/extensions/SyntheticResolveExtension.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ interface SyntheticResolveExtension {
4343
override fun addSyntheticSupertypes(thisDescriptor: ClassDescriptor, supertypes: MutableList<KotlinType>) =
4444
instances.forEach { it.addSyntheticSupertypes(thisDescriptor, supertypes) }
4545

46+
// todo revert
4647
override fun generateSyntheticMethods(thisDescriptor: ClassDescriptor, name: Name,
4748
fromSupertypes: List<SimpleFunctionDescriptor>,
4849
result: MutableCollection<SimpleFunctionDescriptor>) =

generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.android.folding.AbstractAndroidResourceFoldingTest
2828
import org.jetbrains.kotlin.android.intention.AbstractAndroidIntentionTest
2929
import org.jetbrains.kotlin.android.intention.AbstractAndroidResourceIntentionTest
3030
import org.jetbrains.kotlin.android.lint.AbstractKotlinLintTest
31+
import org.jetbrains.kotlin.android.parcel.AbstractParcelBytecodeListingTest
3132
import org.jetbrains.kotlin.android.quickfix.AbstractAndroidLintQuickfixTest
3233
import org.jetbrains.kotlin.android.quickfix.AbstractAndroidQuickFixMultiFileTest
3334
import org.jetbrains.kotlin.asJava.AbstractCompilerLightClassTest
@@ -1249,6 +1250,10 @@ fun main(args: Array<String>) {
12491250
testClass<AbstractAndroidBytecodeShapeTest> {
12501251
model("codegen/bytecodeShape", recursive = false, extension = null)
12511252
}
1253+
1254+
testClass<AbstractParcelBytecodeListingTest> {
1255+
model("parcel/codegen")
1256+
}
12521257
}
12531258

12541259
testGroup("plugins/plugins-tests/tests", "plugins/annotation-collector/testData") {

idea/src/META-INF/android.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@
8585
<platformGradleDetector implementation="org.jetbrains.kotlin.android.configure.PlatformAndroidGradleDetector"/>
8686
<completionInformationProvider implementation="org.jetbrains.kotlin.AndroidExtensionsCompletionInformationProvider" />
8787

88+
<expressionCodegenExtension implementation="org.jetbrains.kotlin.android.parcel.ParcelableCodegenExtension"/>
89+
<syntheticResolveExtension implementation="org.jetbrains.kotlin.android.parcel.ParcelableResolveExtension"/>
90+
<classBuilderFactoryInterceptorExtension implementation="org.jetbrains.kotlin.android.synthetic.codegen.ParcelableClinitClassBuilderInterceptorExtension"/>
91+
8892
<androidDexer implementation="org.jetbrains.kotlin.android.debugger.AndroidDexerImpl"/>
8993
</extensions>
9094

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
/*
2+
* Copyright 2010-2015 JetBrains s.r.o.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.jetbrains.kotlin.android.synthetic.codegen
18+
19+
import com.intellij.psi.PsiElement
20+
import org.jetbrains.kotlin.android.parcel.isMagicParcelable
21+
import org.jetbrains.kotlin.codegen.ClassBuilder
22+
import org.jetbrains.kotlin.codegen.ClassBuilderFactory
23+
import org.jetbrains.kotlin.codegen.DelegatingClassBuilder
24+
import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension
25+
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
26+
import org.jetbrains.kotlin.psi.KtClass
27+
import org.jetbrains.kotlin.resolve.BindingContext
28+
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
29+
import org.jetbrains.org.objectweb.asm.*
30+
import org.jetbrains.org.objectweb.asm.Opcodes.*
31+
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
32+
33+
class ParcelableClinitClassBuilderInterceptorExtension : ClassBuilderInterceptorExtension {
34+
override fun interceptClassBuilderFactory(
35+
interceptedFactory: ClassBuilderFactory,
36+
bindingContext: BindingContext,
37+
diagnostics: DiagnosticSink
38+
): ClassBuilderFactory {
39+
return ParcelableClinitClassBuilderFactory(interceptedFactory, bindingContext)
40+
}
41+
42+
private inner class ParcelableClinitClassBuilderFactory(
43+
private val delegateFactory: ClassBuilderFactory,
44+
val bindingContext: BindingContext
45+
) : ClassBuilderFactory {
46+
47+
override fun newClassBuilder(origin: JvmDeclarationOrigin): ClassBuilder {
48+
return AndroidOnDestroyCollectorClassBuilder(delegateFactory.newClassBuilder(origin), bindingContext)
49+
}
50+
51+
override fun getClassBuilderMode() = delegateFactory.classBuilderMode
52+
53+
override fun asText(builder: ClassBuilder?): String? {
54+
return delegateFactory.asText((builder as AndroidOnDestroyCollectorClassBuilder).delegateClassBuilder)
55+
}
56+
57+
override fun asBytes(builder: ClassBuilder?): ByteArray? {
58+
return delegateFactory.asBytes((builder as AndroidOnDestroyCollectorClassBuilder).delegateClassBuilder)
59+
}
60+
61+
override fun close() {
62+
delegateFactory.close()
63+
}
64+
}
65+
66+
private inner class AndroidOnDestroyCollectorClassBuilder(
67+
internal val delegateClassBuilder: ClassBuilder,
68+
val bindingContext: BindingContext
69+
) : DelegatingClassBuilder() {
70+
private var currentClass: KtClass? = null
71+
private var currentClassName: String? = null
72+
private var isClinitGenerated = false
73+
74+
override fun getDelegate() = delegateClassBuilder
75+
76+
override fun defineClass(
77+
origin: PsiElement?,
78+
version: Int,
79+
access: Int,
80+
name: String,
81+
signature: String?,
82+
superName: String,
83+
interfaces: Array<out String>
84+
) {
85+
if (origin is KtClass) {
86+
currentClass = origin
87+
}
88+
89+
currentClassName = name
90+
isClinitGenerated = false
91+
92+
super.defineClass(origin, version, access, name, signature, superName, interfaces)
93+
}
94+
95+
override fun done() {
96+
if (!isClinitGenerated && currentClass != null && currentClassName != null) {
97+
val descriptor = bindingContext[BindingContext.CLASS, currentClass]
98+
if (descriptor != null && descriptor.isMagicParcelable) {
99+
val baseVisitor = super.newMethod(JvmDeclarationOrigin.NO_ORIGIN, ACC_STATIC, "<clinit>", "()V", null, null)
100+
val visitor = ClinitAwareMethodVisitor(currentClassName!!, baseVisitor)
101+
102+
visitor.visitCode()
103+
visitor.visitInsn(Opcodes.RETURN)
104+
visitor.visitEnd()
105+
}
106+
}
107+
108+
super.done()
109+
}
110+
111+
override fun newMethod(
112+
origin: JvmDeclarationOrigin,
113+
access: Int,
114+
name: String,
115+
desc: String,
116+
signature: String?,
117+
exceptions: Array<out String>?
118+
): MethodVisitor {
119+
if (name == "<clinit>" && currentClass != null && currentClassName != null) {
120+
isClinitGenerated = true
121+
return ClinitAwareMethodVisitor(currentClassName!!, super.newMethod(origin, access, name, desc, signature, exceptions))
122+
}
123+
124+
return super.newMethod(origin, access, name, desc, signature, exceptions)
125+
}
126+
}
127+
128+
private class ClinitAwareMethodVisitor(val parcelableName: String, mv: MethodVisitor) : MethodVisitor(Opcodes.ASM5, mv) {
129+
override fun visitInsn(opcode: Int) {
130+
if (opcode == Opcodes.RETURN) {
131+
val iv = InstructionAdapter(this)
132+
val creatorName = "$parcelableName\$CREATOR"
133+
val creatorType = Type.getObjectType(creatorName)
134+
135+
iv.anew(creatorType)
136+
iv.dup()
137+
iv.invokespecial(creatorName, "<init>", "()V", false)
138+
iv.putstatic(parcelableName, "CREATOR", creatorType.descriptor)
139+
}
140+
141+
super.visitInsn(opcode)
142+
}
143+
}
144+
}

0 commit comments

Comments
 (0)