[go: nahoru, domu]

blob: 9f7fdb1c28cc7184d224c3b214f33e18904c9f66 [file] [log] [blame]
Leland Richardsone49cef8f2020-06-27 11:33:36 -07001/*
2 * Copyright 2019 The Android Open Source Project
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
Louis Pullen-Freilich3ebca692020-07-24 01:10:07 +010017package androidx.compose.compiler.plugins.kotlin.lower
Leland Richardsone49cef8f2020-06-27 11:33:36 -070018
Louis Pullen-Freilich3ebca692020-07-24 01:10:07 +010019import androidx.compose.compiler.plugins.kotlin.ComposeFqNames
Leland Richardsone49cef8f2020-06-27 11:33:36 -070020import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
21import org.jetbrains.kotlin.backend.common.ir.addChild
Leland Richardson76b8acb2020-07-15 17:35:41 -070022import org.jetbrains.kotlin.backend.common.ir.copyTo
Leland Richardsone49cef8f2020-06-27 11:33:36 -070023import org.jetbrains.kotlin.backend.common.ir.createParameterDeclarations
24import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder
25import org.jetbrains.kotlin.descriptors.ClassKind
26import org.jetbrains.kotlin.descriptors.Visibilities
27import org.jetbrains.kotlin.ir.IrStatement
28import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
29import org.jetbrains.kotlin.ir.builders.declarations.addConstructor
Leland Richardsone49cef8f2020-06-27 11:33:36 -070030import org.jetbrains.kotlin.ir.builders.declarations.addFunction
Leland Richardson76b8acb2020-07-15 17:35:41 -070031import org.jetbrains.kotlin.ir.builders.declarations.addGetter
32import org.jetbrains.kotlin.ir.builders.declarations.addProperty
33import org.jetbrains.kotlin.ir.builders.declarations.addSetter
34import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
Leland Richardsone49cef8f2020-06-27 11:33:36 -070035import org.jetbrains.kotlin.ir.builders.declarations.buildClass
Leland Richardson76b8acb2020-07-15 17:35:41 -070036import org.jetbrains.kotlin.ir.builders.declarations.buildField
Leland Richardsone49cef8f2020-06-27 11:33:36 -070037import org.jetbrains.kotlin.ir.builders.irBlock
38import org.jetbrains.kotlin.ir.builders.irBlockBody
39import org.jetbrains.kotlin.ir.builders.irCall
40import org.jetbrains.kotlin.ir.builders.irDelegatingConstructorCall
41import org.jetbrains.kotlin.ir.builders.irGet
42import org.jetbrains.kotlin.ir.builders.irGetField
43import org.jetbrains.kotlin.ir.builders.irIfNull
44import org.jetbrains.kotlin.ir.builders.irReturn
Leland Richardson76b8acb2020-07-15 17:35:41 -070045import org.jetbrains.kotlin.ir.builders.irSet
Leland Richardsone49cef8f2020-06-27 11:33:36 -070046import org.jetbrains.kotlin.ir.builders.irSetField
47import org.jetbrains.kotlin.ir.builders.irString
48import org.jetbrains.kotlin.ir.builders.irTemporary
49import org.jetbrains.kotlin.ir.declarations.IrAnnotationContainer
50import org.jetbrains.kotlin.ir.declarations.IrClass
51import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
52import org.jetbrains.kotlin.ir.declarations.IrEnumEntry
53import org.jetbrains.kotlin.ir.declarations.IrFile
54import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
55import org.jetbrains.kotlin.ir.declarations.IrProperty
56import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
57import org.jetbrains.kotlin.ir.declarations.IrValueParameter
58import org.jetbrains.kotlin.ir.declarations.IrVariable
59import org.jetbrains.kotlin.ir.expressions.IrBlock
60import org.jetbrains.kotlin.ir.expressions.IrBlockBody
61import org.jetbrains.kotlin.ir.expressions.IrBody
62import org.jetbrains.kotlin.ir.expressions.IrBranch
63import org.jetbrains.kotlin.ir.expressions.IrCall
64import org.jetbrains.kotlin.ir.expressions.IrComposite
65import org.jetbrains.kotlin.ir.expressions.IrConst
66import org.jetbrains.kotlin.ir.expressions.IrConstKind
67import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
68import org.jetbrains.kotlin.ir.expressions.IrDelegatingConstructorCall
69import org.jetbrains.kotlin.ir.expressions.IrElseBranch
Leland Richardson793de652020-06-30 22:47:32 -070070import org.jetbrains.kotlin.ir.expressions.IrEnumConstructorCall
Leland Richardsone49cef8f2020-06-27 11:33:36 -070071import org.jetbrains.kotlin.ir.expressions.IrExpression
72import org.jetbrains.kotlin.ir.expressions.IrLoop
73import org.jetbrains.kotlin.ir.expressions.IrSetField
74import org.jetbrains.kotlin.ir.expressions.IrSetVariable
75import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
76import org.jetbrains.kotlin.ir.expressions.IrStringConcatenation
77import org.jetbrains.kotlin.ir.expressions.IrTry
78import org.jetbrains.kotlin.ir.expressions.IrVararg
79import org.jetbrains.kotlin.ir.expressions.IrVarargElement
80import org.jetbrains.kotlin.ir.expressions.IrWhen
81import org.jetbrains.kotlin.ir.expressions.impl.IrBranchImpl
82import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
83import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl
84import org.jetbrains.kotlin.ir.expressions.impl.IrElseBranchImpl
Leland Richardson9669e6a2020-07-10 16:26:10 -070085import org.jetbrains.kotlin.ir.expressions.impl.IrExpressionBodyImpl
Leland Richardsone49cef8f2020-06-27 11:33:36 -070086import org.jetbrains.kotlin.ir.expressions.impl.IrGetObjectValueImpl
87import org.jetbrains.kotlin.ir.expressions.impl.IrStringConcatenationImpl
88import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl
89import org.jetbrains.kotlin.ir.types.IrType
90import org.jetbrains.kotlin.ir.types.defaultType
91import org.jetbrains.kotlin.ir.types.makeNullable
92import org.jetbrains.kotlin.ir.types.typeWith
93import org.jetbrains.kotlin.ir.util.DeepCopySymbolRemapper
94import org.jetbrains.kotlin.ir.util.constructors
95import org.jetbrains.kotlin.ir.util.defaultType
96import org.jetbrains.kotlin.ir.util.getPropertyGetter
97import org.jetbrains.kotlin.ir.util.isAnnotationClass
98import org.jetbrains.kotlin.ir.util.parentAsClass
99import org.jetbrains.kotlin.ir.util.primaryConstructor
100import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
101import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils
102import org.jetbrains.kotlin.name.Name
103import org.jetbrains.kotlin.resolve.BindingTrace
104
105/**
106 * This transformer transforms constant literal expressions into expressions which read a
107 * MutableState instance so that changes to the source code of the constant literal can be
108 * communicated to the runtime without a recompile. This transformation is intended to improve
109 * developer experience and should never be enabled in a release build as it will significantly
110 * slow down performance-conscious code.
111 *
112 * The nontrivial piece of this transform is to create a stable "durable" unique key for every
113 * single constant in the module. It does this by creating a path-based key which roughly maps to
114 * the semantic structure of the code, and uses an incrementing index on sibling constants as a
115 * last resort. The constant expressions in the IR will be transformed into property getter calls
116 * to a synthetic "Live Literals" class that is generated per file. The class is an object
117 * singleton where each property is lazily backed by a MutableState instance which is accessed
118 * using the runtime's `liveLiteral(String,T)` top level API.
119 *
120 * Roughly speaking, the transform will turn the following:
121 *
122 * // file: Foo.kt
123 * fun Foo() {
124 * print("Hello World")
125 * }
126 *
127 * into the following equivalent representation:
128 *
129 * // file: Foo.kt
130 * fun Foo() {
131 * print(LiveLiterals$FooKt.`getString$arg-0$call-print$fun-Foo`())
132 * }
133 * object LiveLiterals$FooKt {
Leland Richardson9669e6a2020-07-10 16:26:10 -0700134 * var `String$arg-0$call-print$fun-Foo`: String = "Hello World"
135 * var `State$String$arg-0$call-print$fun-Foo`: MutableState<String>? = null
Leland Richardsone49cef8f2020-06-27 11:33:36 -0700136 * fun `getString$arg-0$call-print$fun-Foo`(): String {
137 * val field = this.`String$arg-0$call-print$fun-Foo`
138 * val state = if (field == null) {
Leland Richardson9669e6a2020-07-10 16:26:10 -0700139 * val tmp = liveLiteral(
140 * "String$arg-0$call-print$fun-Foo",
141 * this.`String$arg-0$call-print$fun-Foo`
142 * )
Leland Richardsone49cef8f2020-06-27 11:33:36 -0700143 * this.`String$arg-0$call-print$fun-Foo` = tmp
144 * tmp
145 * } else field
146 * return field.value
147 * }
148 * }
149 *
150 * @see DurableKeyVisitor
151 */
152open class LiveLiteralTransformer(
153 private val liveLiteralsEnabled: Boolean,
154 private val keyVisitor: DurableKeyVisitor,
155 context: IrPluginContext,
156 symbolRemapper: DeepCopySymbolRemapper,
157 bindingTrace: BindingTrace
158) :
159 AbstractComposeLowering(context, symbolRemapper, bindingTrace),
160 ModuleLoweringPass {
161
162 override fun lower(module: IrModuleFragment) {
163 module.transformChildrenVoid(this)
164 }
165
166 private val liveLiteral =
167 getInternalFunction("liveLiteral").bindIfNecessary()
168 private val liveLiteralInfoAnnotation =
169 getInternalClass("LiveLiteralInfo").bindIfNecessary()
170 private val liveLiteralFileInfoAnnotation =
171 getInternalClass("LiveLiteralFileInfo").bindIfNecessary()
172 private val stateInterface =
Leland Richardson3cc3c0a2020-07-23 11:50:55 -0700173 getTopLevelClass(ComposeFqNames.fqNameFor("State")).bindIfNecessary()
Leland Richardsone49cef8f2020-06-27 11:33:36 -0700174 private val NoLiveLiteralsAnnotation =
Leland Richardson3cc3c0a2020-07-23 11:50:55 -0700175 getTopLevelClass(ComposeFqNames.fqNameFor("NoLiveLiterals")).bindIfNecessary()
Leland Richardsone49cef8f2020-06-27 11:33:36 -0700176
177 private fun IrAnnotationContainer.hasNoLiveLiteralsAnnotation(): Boolean = annotations.any {
178 it.symbol.bindIfNecessary().owner == NoLiveLiteralsAnnotation.owner.primaryConstructor
179 }
180
181 private fun <T> enter(key: String, block: () -> T) = keyVisitor.enter(key, block)
182 private fun <T> siblings(key: String, block: () -> T) = keyVisitor.siblings(key, block)
183 private fun <T> siblings(block: () -> T) = keyVisitor.siblings(block)
184 private var liveLiteralsClass: IrClass? = null
185 private var currentFile: IrFile? = null
186
187 private fun irGetLiveLiteralsClass(): IrExpression {
188 return IrGetObjectValueImpl(
189 startOffset = UNDEFINED_OFFSET,
190 endOffset = UNDEFINED_OFFSET,
191 type = liveLiteralsClass!!.defaultType,
192 symbol = liveLiteralsClass!!.symbol
193 )
194 }
195
196 private fun Name.asJvmFriendlyString(): String {
197 return if (!isSpecial) identifier
198 else asString().replace('<', '$').replace('>', '$')
199 }
200
201 private fun irLiveLiteralInfoAnnotation(
202 key: String,
203 offset: Int
204 ): IrConstructorCall = IrConstructorCallImpl(
205 UNDEFINED_OFFSET,
206 UNDEFINED_OFFSET,
207 liveLiteralInfoAnnotation.defaultType,
208 liveLiteralInfoAnnotation.constructors.single(),
209 0,
210 0,
211 2
212 ).apply {
213 putValueArgument(0, irConst(key))
214 putValueArgument(1, irConst(offset))
215 }
216
217 private fun irLiveLiteralFileInfoAnnotation(
218 file: String
219 ): IrConstructorCall = IrConstructorCallImpl(
220 UNDEFINED_OFFSET,
221 UNDEFINED_OFFSET,
222 liveLiteralFileInfoAnnotation.defaultType,
223 liveLiteralFileInfoAnnotation.constructors.single(),
224 0,
225 0,
226 1
227 ).apply {
228 putValueArgument(0, irConst(file))
229 }
230
231 private fun irLiveLiteralGetter(
232 key: String,
233 literalValue: IrExpression,
234 literalType: IrType
235 ): IrSimpleFunction {
236 val clazz = liveLiteralsClass!!
237 val stateType = stateInterface.owner.typeWith(literalType).makeNullable()
238 val stateGetValue = stateInterface.getPropertyGetter("value")!!
Leland Richardson76b8acb2020-07-15 17:35:41 -0700239 val defaultProp = clazz.addProperty {
240 name = Name.identifier(key)
241 visibility = Visibilities.PRIVATE
242 }.also { p ->
243 p.backingField = buildField {
244 name = Name.identifier(key)
245 isStatic = true
246 type = literalType
247 visibility = Visibilities.PRIVATE
248 }.also { f ->
249 f.correspondingPropertySymbol = p.symbol
250 f.parent = clazz
251 f.initializer = IrExpressionBodyImpl(
252 literalValue.startOffset,
253 literalValue.endOffset,
254 literalValue
255 )
256 }
257 p.addGetter {
258 returnType = literalType
259 visibility = Visibilities.PRIVATE
260 origin = IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR
261 }.also { fn ->
262 val thisParam = clazz.thisReceiver!!.copyTo(fn)
263 fn.dispatchReceiverParameter = thisParam
264 fn.body = DeclarationIrBuilder(context, fn.symbol).irBlockBody {
265 +irReturn(irGetField(irGet(thisParam), p.backingField!!))
266 }
267 }
Leland Richardson9669e6a2020-07-10 16:26:10 -0700268 }
Leland Richardson76b8acb2020-07-15 17:35:41 -0700269 val stateProp = clazz.addProperty {
270 name = Name.identifier("State\$$key")
271 visibility = Visibilities.PRIVATE
272 isVar = true
273 }.also { p ->
274 p.backingField = buildField {
275 name = Name.identifier("State\$$key")
276 type = stateType
277 visibility = Visibilities.PRIVATE
278 isStatic = true
279 }.also { f ->
280 f.correspondingPropertySymbol = p.symbol
281 f.parent = clazz
282 }
283 p.addGetter {
284 returnType = stateType
285 visibility = Visibilities.PRIVATE
286 origin = IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR
287 }.also { fn ->
288 val thisParam = clazz.thisReceiver!!.copyTo(fn)
289 fn.dispatchReceiverParameter = thisParam
290 fn.body = DeclarationIrBuilder(context, fn.symbol).irBlockBody {
291 +irReturn(irGetField(irGet(thisParam), p.backingField!!))
292 }
293 }
294 p.addSetter {
295 returnType = context.irBuiltIns.unitType
296 visibility = Visibilities.PRIVATE
297 origin = IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR
298 }.also { fn ->
299 val thisParam = clazz.thisReceiver!!.copyTo(fn)
300 fn.dispatchReceiverParameter = thisParam
301 val valueParam = fn.addValueParameter("value", stateType)
302 fn.body = DeclarationIrBuilder(context, fn.symbol).irBlockBody {
303 +irSetField(irGet(thisParam), p.backingField!!, irGet(valueParam))
304 }
305 }
306 }
Leland Richardsone49cef8f2020-06-27 11:33:36 -0700307 return clazz.addFunction(
308 name = key,
309 returnType = literalType
310 ).also { fn ->
311 val thisParam = fn.dispatchReceiverParameter!!
Jim Sprocha88c07a2020-06-25 13:00:03 -0700312 fn.annotations += irLiveLiteralInfoAnnotation(key, literalValue.startOffset)
Leland Richardsone49cef8f2020-06-27 11:33:36 -0700313 fn.body = DeclarationIrBuilder(context, fn.symbol).irBlockBody {
Leland Richardson9669e6a2020-07-10 16:26:10 -0700314 // val a = stateField
Leland Richardsone49cef8f2020-06-27 11:33:36 -0700315 // val b = if (a == null) {
Leland Richardson9669e6a2020-07-10 16:26:10 -0700316 // val c = liveLiteralState("key", defaultValueField)
317 // stateField = c
Leland Richardsone49cef8f2020-06-27 11:33:36 -0700318 // c
319 // } else a
320 // return b.value
Leland Richardson76b8acb2020-07-15 17:35:41 -0700321 val a = irTemporary(irGet(stateType, irGet(thisParam), stateProp.getter!!.symbol))
Leland Richardsone49cef8f2020-06-27 11:33:36 -0700322 val b = irIfNull(
323 type = stateType,
324 subject = irGet(a),
325 thenPart = irBlock(resultType = stateType) {
326 val liveLiteralCall = irCall(liveLiteral).apply {
327 putValueArgument(0, irString(key))
Leland Richardson76b8acb2020-07-15 17:35:41 -0700328 putValueArgument(1, irGet(
329 literalType,
330 irGet(thisParam),
331 defaultProp.getter!!.symbol
332 ))
Leland Richardsone49cef8f2020-06-27 11:33:36 -0700333 putTypeArgument(0, literalType)
334 }
335 val c = irTemporary(liveLiteralCall)
Leland Richardson76b8acb2020-07-15 17:35:41 -0700336 +irSet(
337 stateType,
338 irGet(thisParam),
339 stateProp.setter!!.symbol,
340 irGet(c)
341 )
Leland Richardsone49cef8f2020-06-27 11:33:36 -0700342 +irGet(c)
343 },
344 elsePart = irGet(a)
345 )
Jim Sprocha88c07a2020-06-25 13:00:03 -0700346 val call = IrCallImpl(
347 UNDEFINED_OFFSET,
348 UNDEFINED_OFFSET,
349 literalType,
350 stateGetValue,
351 IrStatementOrigin.FOR_LOOP_ITERATOR
352 ).apply {
Leland Richardsone49cef8f2020-06-27 11:33:36 -0700353 dispatchReceiver = b
Jim Sprocha88c07a2020-06-25 13:00:03 -0700354 }
355
356 +irReturn(call)
Leland Richardsone49cef8f2020-06-27 11:33:36 -0700357 }
358 }
359 }
360
361 override fun <T> visitConst(expression: IrConst<T>): IrExpression {
362 when (expression.kind) {
363 IrConstKind.Null -> return expression
364 }
365 val (key, success) = keyVisitor.buildPath(
366 prefix = expression.kind.asString,
367 pathSeparator = "\$",
368 siblingSeparator = "-"
369 )
370 // NOTE: Even if `liveLiteralsEnabled` is false, we are still going to throw an exception
371 // here because the presence of a duplicate key represents a bug in this transform since
372 // it should be impossible. By checking this always, we are making it so that bugs in
373 // this transform will get caught _early_ and that there will be implicitly high coverage
374 // of the key generation algorithm despite this transform only being used by tooling.
375 // Developers have the ability to "silence" this exception by marking the surrounding
376 // class/file/function with the `@NoLiveLiterals` annotation.
377 if (!success) {
378 val file = currentFile ?: return expression
379 val src = file.fileEntry.getSourceRangeInfo(
380 expression.startOffset,
381 expression.endOffset
382 )
383
384 error(
385 "Duplicate live literal key found: $key\n" +
386 "Caused by element at: " +
387 "${src.filePath}:${src.startLineNumber}:${src.startColumnNumber}\n" +
388 "If you encounter this error, please file a bug at " +
389 "https://issuetracker.google.com/issues?q=componentid:610764\n" +
390 "Try adding the `@NoLiveLiterals` annotation around the surrounding code to " +
391 "avoid this exception."
392 )
393 }
394 // If live literals are enabled, don't do anything
395 if (!liveLiteralsEnabled) return expression
396
397 // create the getter function on the live literals class
398 val getter = irLiveLiteralGetter(
399 key = key,
400 literalValue = expression.copy(),
401 literalType = expression.type
402 )
403
404 // return a call to the getter in place of the constant
405 return IrCallImpl(
406 expression.startOffset,
407 expression.endOffset,
408 expression.type,
409 getter.symbol
410 ).apply {
411 dispatchReceiver = irGetLiveLiteralsClass()
412 }
413 }
414
415 override fun visitClass(declaration: IrClass): IrStatement {
416 if (declaration.hasNoLiveLiteralsAnnotation()) return declaration
417 // constants in annotations need to be compile-time values, so we can never transform them
418 if (declaration.isAnnotationClass) return declaration
419 return siblings("class-${declaration.name.asJvmFriendlyString()}") {
420 super.visitClass(declaration)
421 }
422 }
423
424 open fun makeKeySet(): MutableSet<String> {
425 return mutableSetOf()
426 }
427
428 override fun visitFile(declaration: IrFile): IrFile {
429 if (declaration.hasNoLiveLiteralsAnnotation()) return declaration
430 val filePath = declaration.fileEntry.name
431 val fileName = filePath.split('/').last()
432 val keys = makeKeySet()
433 return keyVisitor.root(keys) {
434 val prevClass = liveLiteralsClass
435 val nextClass = buildClass {
436 kind = ClassKind.OBJECT
437 visibility = Visibilities.INTERNAL
438 val shortName = PackagePartClassUtils.getFilePartShortName(fileName)
439 // the name of the LiveLiterals class is per-file, so we use the same name that
440 // the kotlin file class lowering produces, prefixed with `LiveLiterals$`.
441 name = Name.identifier("LiveLiterals${"$"}$shortName")
442 }.also {
443 it.createParameterDeclarations()
444
445 // store the full file path to the file that this class is associated with in an
446 // annotation on the class. This will be used by tooling to associate the keys
447 // inside of this class with actual PSI in the editor.
Jim Sprocha88c07a2020-06-25 13:00:03 -0700448 it.annotations += irLiveLiteralFileInfoAnnotation(declaration.fileEntry.name)
Leland Richardsone49cef8f2020-06-27 11:33:36 -0700449 it.addConstructor {
450 isPrimary = true
451 }.also { ctor ->
452 ctor.body = DeclarationIrBuilder(context, it.symbol).irBlockBody {
453 +irDelegatingConstructorCall(
454 context
455 .irBuiltIns
456 .anyClass
457 .bindIfNecessary()
458 .owner
459 .primaryConstructor!!
460 )
461 }
462 }
463 }
464 try {
465 liveLiteralsClass = nextClass
466 currentFile = declaration
467 val file = super.visitFile(declaration)
468 // if there were no constants found in the entire file, then we don't need to
469 // create this class at all
470 if (liveLiteralsEnabled && keys.isNotEmpty()) {
471 file.addChild(nextClass)
472 }
473 file
474 } finally {
475 liveLiteralsClass = prevClass
476 }
477 }
478 }
479
480 override fun visitTry(aTry: IrTry): IrExpression {
481 aTry.tryResult = enter("try") {
482 aTry.tryResult.transform(this, null)
483 }
484 siblings {
485 aTry.catches.forEach {
486 it.result = enter("catch") { it.result.transform(this, null) }
487 }
488 }
489 aTry.finallyExpression = enter("finally") {
490 aTry.finallyExpression?.transform(this, null)
491 }
492 return aTry
493 }
494
495 override fun visitDelegatingConstructorCall(
496 expression: IrDelegatingConstructorCall
497 ): IrExpression {
498 val owner = expression.symbol.bindIfNecessary().owner
499
500 // annotations are represented as constructor calls in IR, but the parameters need to be
501 // compile-time values only, so we can't transform them at all.
502 if (owner.parentAsClass.isAnnotationClass) return expression
503
504 val name = owner.name.asJvmFriendlyString()
505
506 return enter("call-$name") {
507 expression.dispatchReceiver = enter("\$this") {
508 expression.dispatchReceiver?.transform(this, null)
509 }
510 expression.extensionReceiver = enter("\$\$this") {
511 expression.extensionReceiver?.transform(this, null)
512 }
513
514 for (i in 0 until expression.valueArgumentsCount) {
515 val arg = expression.getValueArgument(i)
516 if (arg != null) {
517 enter("arg-$i") {
518 expression.putValueArgument(i, arg.transform(this, null))
519 }
520 }
521 }
522 expression
523 }
524 }
525
Leland Richardson793de652020-06-30 22:47:32 -0700526 override fun visitEnumConstructorCall(expression: IrEnumConstructorCall): IrExpression {
527 val owner = expression.symbol.bindIfNecessary().owner
528 val name = owner.name.asJvmFriendlyString()
529
530 return enter("call-$name") {
531 expression.dispatchReceiver = enter("\$this") {
532 expression.dispatchReceiver?.transform(this, null)
533 }
534 expression.extensionReceiver = enter("\$\$this") {
535 expression.extensionReceiver?.transform(this, null)
536 }
537
538 for (i in 0 until expression.valueArgumentsCount) {
539 val arg = expression.getValueArgument(i)
540 if (arg != null) {
541 enter("arg-$i") {
542 expression.putValueArgument(i, arg.transform(this, null))
543 }
544 }
545 }
546 expression
547 }
548 }
549
Leland Richardsone49cef8f2020-06-27 11:33:36 -0700550 override fun visitConstructorCall(expression: IrConstructorCall): IrExpression {
551 val owner = expression.symbol.bindIfNecessary().owner
552
553 // annotations are represented as constructor calls in IR, but the parameters need to be
554 // compile-time values only, so we can't transform them at all.
555 if (owner.parentAsClass.isAnnotationClass) return expression
556
557 val name = owner.name.asJvmFriendlyString()
558
559 return enter("call-$name") {
560 expression.dispatchReceiver = enter("\$this") {
561 expression.dispatchReceiver?.transform(this, null)
562 }
563 expression.extensionReceiver = enter("\$\$this") {
564 expression.extensionReceiver?.transform(this, null)
565 }
566
567 for (i in 0 until expression.valueArgumentsCount) {
568 val arg = expression.getValueArgument(i)
569 if (arg != null) {
570 enter("arg-$i") {
571 expression.putValueArgument(i, arg.transform(this, null))
572 }
573 }
574 }
575 expression
576 }
577 }
578
579 override fun visitCall(expression: IrCall): IrExpression {
580 val owner = expression.symbol.bindIfNecessary().owner
581 val name = owner.name.asJvmFriendlyString()
582
583 return enter("call-$name") {
584 expression.dispatchReceiver = enter("\$this") {
585 expression.dispatchReceiver?.transform(this, null)
586 }
587 expression.extensionReceiver = enter("\$\$this") {
588 expression.extensionReceiver?.transform(this, null)
589 }
590
591 for (i in 0 until expression.valueArgumentsCount) {
592 val arg = expression.getValueArgument(i)
593 if (arg != null) {
594 enter("arg-$i") {
595 expression.putValueArgument(i, arg.transform(this, null))
596 }
597 }
598 }
599 expression
600 }
601 }
602
603 override fun visitEnumEntry(declaration: IrEnumEntry): IrStatement {
604 return enter("entry-${declaration.name.asJvmFriendlyString()}") {
605 super.visitEnumEntry(declaration)
606 }
607 }
608
609 override fun visitVararg(expression: IrVararg): IrExpression {
610 if (expression !is IrVarargImpl) return expression
611 return enter("vararg") {
612 expression.elements.forEachIndexed { i, arg ->
613 expression.elements[i] = enter("$i") {
614 arg.transform(this, null) as IrVarargElement
615 }
616 }
617 expression
618 }
619 }
620
621 override fun visitSimpleFunction(declaration: IrSimpleFunction): IrStatement {
622 if (declaration.hasNoLiveLiteralsAnnotation()) return declaration
623 val name = declaration.name.asJvmFriendlyString()
624 val path = if (name == "<anonymous>") "lambda" else "fun-$name"
625 return enter(path) { super.visitSimpleFunction(declaration) }
626 }
627
628 override fun visitLoop(loop: IrLoop): IrExpression {
629 return when (loop.origin) {
630 // in these cases, the compiler relies on a certain structure for the condition
631 // expression, so we only touch the body
632 IrStatementOrigin.WHILE_LOOP,
633 IrStatementOrigin.FOR_LOOP_INNER_WHILE -> enter("loop") {
634 loop.body = enter("body") { loop.body?.transform(this, null) }
635 loop
636 }
637 else -> enter("loop") {
638 loop.condition = enter("cond") { loop.condition.transform(this, null) }
639 loop.body = enter("body") { loop.body?.transform(this, null) }
640 loop
641 }
642 }
643 }
644
645 override fun visitStringConcatenation(expression: IrStringConcatenation): IrExpression {
646 if (expression !is IrStringConcatenationImpl) return expression
647 return enter("str") {
648 siblings {
649 expression.arguments.forEachIndexed { index, expr ->
650 expression.arguments[index] = enter("$index") {
651 expr.transform(this, null)
652 }
653 }
654 expression
655 }
656 }
657 }
658
659 override fun visitWhen(expression: IrWhen): IrExpression {
660 return when (expression.origin) {
661 // ANDAND needs to have an 'if true then false' body on its second branch, so only
662 // transform the first branch
663 IrStatementOrigin.ANDAND -> {
664 expression.branches[0] = expression.branches[0].transform(this, null)
665 expression
666 }
667
668 // OROR condition should have an 'if a then true' body on its first branch, so only
669 // transform the second branch
670 IrStatementOrigin.OROR -> {
671 expression.branches[1] = expression.branches[1].transform(this, null)
672 expression
673 }
674
675 IrStatementOrigin.IF -> siblings("if") {
676 super.visitWhen(expression)
677 }
678
679 else -> siblings("when") {
680 super.visitWhen(expression)
681 }
682 }
683 }
684
685 override fun visitValueParameter(declaration: IrValueParameter): IrStatement {
686 return enter("param-${declaration.name.asJvmFriendlyString()}") {
687 super.visitValueParameter(declaration)
688 }
689 }
690
691 override fun visitElseBranch(branch: IrElseBranch): IrElseBranch {
692 return IrElseBranchImpl(
693 startOffset = branch.startOffset,
694 endOffset = branch.endOffset,
695 // the condition of an else branch is a constant boolean but we don't want
696 // to convert it into a live literal, so we don't transform it
697 condition = branch.condition,
698 result = enter("else") {
699 branch.result.transform(this, null)
700 }
701 )
702 }
703
704 override fun visitBranch(branch: IrBranch): IrBranch {
705 return IrBranchImpl(
706 startOffset = branch.startOffset,
707 endOffset = branch.endOffset,
708 condition = enter("cond") {
709 branch.condition.transform(this, null)
710 },
711 // only translate the result, as the branch is a constant boolean but we don't want
712 // to convert it into a live literal
713 result = enter("branch") {
714 branch.result.transform(this, null)
715 }
716 )
717 }
718
719 override fun visitComposite(expression: IrComposite): IrExpression {
720 return siblings {
721 super.visitComposite(expression)
722 }
723 }
724
725 override fun visitBlock(expression: IrBlock): IrExpression {
726 return when (expression.origin) {
727 // The compiler relies on a certain structure for the "iterator" instantiation in For
728 // loops, so we avoid transforming the first statement in this case
729 IrStatementOrigin.FOR_LOOP,
730 IrStatementOrigin.FOR_LOOP_INNER_WHILE -> {
731 expression.statements[1] = expression.statements[1].transform(this, null)
732 expression
733 }
734// IrStatementOrigin.SAFE_CALL
735// IrStatementOrigin.WHEN
736// IrStatementOrigin.IF
737// IrStatementOrigin.ELVIS
738// IrStatementOrigin.ARGUMENTS_REORDERING_FOR_CALL
739 else -> siblings {
740 super.visitBlock(expression)
741 }
742 }
743 }
744
745 override fun visitSetVariable(expression: IrSetVariable): IrExpression {
746 val owner = expression.symbol.bindIfNecessary().owner
747 val name = owner.name
748 return when (owner.origin) {
749 // for these synthetic variable declarations we want to avoid transforming them since
750 // the compiler will rely on their compile time value in some cases.
751 IrDeclarationOrigin.FOR_LOOP_IMPLICIT_VARIABLE -> expression
752 IrDeclarationOrigin.IR_TEMPORARY_VARIABLE -> expression
753 IrDeclarationOrigin.FOR_LOOP_VARIABLE -> expression
754 else -> enter("set-$name") { super.visitSetVariable(expression) }
755 }
756 }
757
758 override fun visitSetField(expression: IrSetField): IrExpression {
759 val name = expression.symbol.bindIfNecessary().owner.name
760 return enter("set-$name") { super.visitSetField(expression) }
761 }
762
763 override fun visitBlockBody(body: IrBlockBody): IrBody {
764 return siblings {
765 super.visitBlockBody(body)
766 }
767 }
768
769 override fun visitVariable(declaration: IrVariable): IrStatement {
770 return enter("val-${declaration.name.asJvmFriendlyString()}") {
771 super.visitVariable(declaration)
772 }
773 }
774
775 override fun visitProperty(declaration: IrProperty): IrStatement {
776 if (declaration.hasNoLiveLiteralsAnnotation()) return declaration
777 val backingField = declaration.backingField
778 val getter = declaration.getter
779 val setter = declaration.setter
780 val name = declaration.name.asJvmFriendlyString()
781
782 return enter("val-$name") {
783 // turn them into live literals. We should consider transforming some simple cases like
784 // `val foo = 123`, but in general turning this initializer into a getter is not a
785 // safe operation. We should figure out a way to do this for "static" expressions
786 // though such as `val foo = 16.dp`.
787 declaration.backingField = backingField
788 declaration.getter = enter("get") {
789 getter?.transform(this, null) as? IrSimpleFunction
790 }
791 declaration.setter = enter("set") {
792 setter?.transform(this, null) as? IrSimpleFunction
793 }
794 declaration
795 }
796 }
797}