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