diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt index 67259e355f2..207c5838d05 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt @@ -15,6 +15,7 @@ import org.jetbrains.kotlin.config.languageVersionSettings import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl import org.jetbrains.kotlin.ir.backend.js.lower.* +import org.jetbrains.kotlin.ir.backend.js.lower.calls.CallsLowering import org.jetbrains.kotlin.ir.backend.js.lower.coroutines.CoroutineIntrinsicLowering import org.jetbrains.kotlin.ir.backend.js.lower.coroutines.SuspendFunctionsLowering import org.jetbrains.kotlin.ir.backend.js.lower.inline.FunctionInlining @@ -131,7 +132,8 @@ private fun JsIrBackendContext.lower(moduleFragment: IrModuleFragment, dependenc moduleFragment.files.forEach(clble.getReferenceReplacer()) moduleFragment.files.forEach(ClassReferenceLowering(this)::lower) moduleFragment.files.forEach(PrimitiveCompanionLowering(this)::lower) - moduleFragment.files.forEach(IntrinsicifyCallsLowering(this)::lower) + moduleFragment.files.forEach(ConstLowering(this)::lower) + moduleFragment.files.forEach(CallsLowering(this)::lower) } private fun FileLoweringPass.lower(files: List) = files.forEach { lower(it) } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ConstLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ConstLowering.kt new file mode 100644 index 00000000000..1d50cf08342 --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ConstLowering.kt @@ -0,0 +1,72 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.ir.backend.js.lower + +import org.jetbrains.kotlin.backend.common.FileLoweringPass +import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET +import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext +import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder +import org.jetbrains.kotlin.ir.declarations.IrFile +import org.jetbrains.kotlin.ir.expressions.IrConst +import org.jetbrains.kotlin.ir.expressions.IrConstKind +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl +import org.jetbrains.kotlin.ir.symbols.IrClassSymbol +import org.jetbrains.kotlin.ir.types.IrType +import org.jetbrains.kotlin.ir.types.classifierOrNull +import org.jetbrains.kotlin.ir.util.constructors +import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid +import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid + + +class ConstTransformer(private val context: JsIrBackendContext) : IrElementTransformerVoid() { + private fun lowerConst( + irClass: IrClassSymbol, + carrierFactory: (Int, Int, IrType, C) -> IrExpression, + vararg args: C + ): IrExpression { + val constructor = irClass.constructors.single() + val argType = constructor.owner.valueParameters.first().type + return JsIrBuilder.buildCall(constructor).apply { + for (i in args.indices) { + putValueArgument(i, carrierFactory(UNDEFINED_OFFSET, UNDEFINED_OFFSET, argType, args[i])) + } + } + } + + private fun createLong(v: Long): IrExpression = + lowerConst(context.intrinsics.longClassSymbol, IrConstImpl<*>::int, v.toInt(), (v shr 32).toInt()) + + override fun visitConst(expression: IrConst): IrExpression { + with(context.intrinsics) { + return when (expression.type.classifierOrNull) { + uByteClassSymbol -> lowerConst(uByteClassSymbol, IrConstImpl<*>::byte, IrConstKind.Byte.valueOf(expression)) + + uShortClassSymbol -> lowerConst(uShortClassSymbol, IrConstImpl<*>::short, IrConstKind.Short.valueOf(expression)) + + uIntClassSymbol -> lowerConst(uIntClassSymbol, IrConstImpl<*>::int, IrConstKind.Int.valueOf(expression)) + + uLongClassSymbol -> lowerConst(uLongClassSymbol, { _, _, _, v -> createLong(v) }, IrConstKind.Long.valueOf(expression)) + + else -> when { + expression.kind is IrConstKind.Char -> + lowerConst(charClassSymbol, IrConstImpl<*>::int, IrConstKind.Char.valueOf(expression).toInt()) + + expression.kind is IrConstKind.Long -> + createLong(IrConstKind.Long.valueOf(expression)) + + else -> super.visitConst(expression) + } + } + } + } +} + +class ConstLowering(private val context: JsIrBackendContext) : FileLoweringPass { + override fun lower(irFile: IrFile) { + irFile.transformChildrenVoid(ConstTransformer(context)) + } +} \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/IntrinsicifyCallsLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/IntrinsicifyCallsLowering.kt deleted file mode 100644 index fe4ef119667..00000000000 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/IntrinsicifyCallsLowering.kt +++ /dev/null @@ -1,711 +0,0 @@ -/* - * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license - * that can be found in the license/LICENSE.txt file. - */ - -package org.jetbrains.kotlin.ir.backend.js.lower - -import org.jetbrains.kotlin.backend.common.FileLoweringPass -import org.jetbrains.kotlin.backend.common.utils.isSubtypeOfClass -import org.jetbrains.kotlin.descriptors.PropertyAccessorDescriptor -import org.jetbrains.kotlin.ir.IrStatement -import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET -import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext -import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder -import org.jetbrains.kotlin.ir.backend.js.ir.irCall -import org.jetbrains.kotlin.ir.backend.js.utils.ConversionNames -import org.jetbrains.kotlin.ir.backend.js.utils.Namer -import org.jetbrains.kotlin.ir.backend.js.utils.OperatorNames -import org.jetbrains.kotlin.ir.declarations.* -import org.jetbrains.kotlin.ir.expressions.* -import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl -import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl -import org.jetbrains.kotlin.ir.symbols.IrClassSymbol -import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol -import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol -import org.jetbrains.kotlin.ir.types.* -import org.jetbrains.kotlin.ir.types.impl.originalKotlinType -import org.jetbrains.kotlin.ir.util.* -import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid -import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.types.SimpleType -import org.jetbrains.kotlin.types.isNullable - -private typealias MemberToTransformer = MutableMap IrExpression> -private typealias SymbolToTransformer = MutableMap IrExpression> - - -class IntrinsicifyCallsLowering(private val context: JsIrBackendContext) : FileLoweringPass { - private val intrinsics = context.intrinsics - private val irBuiltIns = context.irBuiltIns - - // TODO: should/can we unify these maps? - private val memberToTransformer: MemberToTransformer - private val symbolToTransformer: SymbolToTransformer - private val nameToTransformer: Map IrExpression> - private val dynamicCallOriginToIrFunction: Map - - init { - symbolToTransformer = mutableMapOf() - memberToTransformer = mutableMapOf() - nameToTransformer = mutableMapOf() - dynamicCallOriginToIrFunction = mutableMapOf() - - val primitiveNumbers = context.irBuiltIns.run { listOf(intType, shortType, byteType, floatType, doubleType) } - - memberToTransformer.run { - for (type in primitiveNumbers) { - op(type, OperatorNames.UNARY_PLUS, intrinsics.jsUnaryPlus) - op(type, OperatorNames.UNARY_MINUS, intrinsics.jsUnaryMinus) - } - - irBuiltIns.stringType.let { - op(it, OperatorNames.ADD, intrinsics.jsPlus) - } - - irBuiltIns.intType.let { - op(it, OperatorNames.SHL, intrinsics.jsBitShiftL) - op(it, OperatorNames.SHR, intrinsics.jsBitShiftR) - op(it, OperatorNames.SHRU, intrinsics.jsBitShiftRU) - op(it, OperatorNames.AND, intrinsics.jsBitAnd) - op(it, OperatorNames.OR, intrinsics.jsBitOr) - op(it, OperatorNames.XOR, intrinsics.jsBitXor) - op(it, OperatorNames.INV, intrinsics.jsBitNot) - } - - irBuiltIns.booleanType.let { - op(it, OperatorNames.AND, intrinsics.jsBitAnd) - op(it, OperatorNames.OR, intrinsics.jsBitOr) - op(it, OperatorNames.NOT, intrinsics.jsNot) - op(it, OperatorNames.XOR, intrinsics.jsBitXor) - } - - // Conversion rules are ported from NumberAndCharConversionFIF - // TODO: Add Char and Number conversions - - irBuiltIns.byteType.let { - op(it, ConversionNames.TO_BYTE, ::useDispatchReceiver) - op(it, ConversionNames.TO_DOUBLE, ::useDispatchReceiver) - op(it, ConversionNames.TO_FLOAT, ::useDispatchReceiver) - op(it, ConversionNames.TO_INT, ::useDispatchReceiver) - op(it, ConversionNames.TO_SHORT, ::useDispatchReceiver) - op(it, ConversionNames.TO_LONG, intrinsics.jsToLong) - } - - for (type in listOf(irBuiltIns.floatType, irBuiltIns.doubleType)) { - op(type, ConversionNames.TO_BYTE, intrinsics.jsNumberToByte) - op(type, ConversionNames.TO_DOUBLE, ::useDispatchReceiver) - op(type, ConversionNames.TO_FLOAT, ::useDispatchReceiver) - op(type, ConversionNames.TO_INT, intrinsics.jsNumberToInt) - op(type, ConversionNames.TO_SHORT, intrinsics.jsNumberToShort) - op(type, ConversionNames.TO_LONG, intrinsics.jsNumberToLong) - } - - irBuiltIns.intType.let { - op(it, ConversionNames.TO_BYTE, intrinsics.jsToByte) - op(it, ConversionNames.TO_DOUBLE, ::useDispatchReceiver) - op(it, ConversionNames.TO_FLOAT, ::useDispatchReceiver) - op(it, ConversionNames.TO_INT, ::useDispatchReceiver) - op(it, ConversionNames.TO_SHORT, intrinsics.jsToShort) - op(it, ConversionNames.TO_LONG, intrinsics.jsToLong) - } - - irBuiltIns.shortType.let { - op(it, ConversionNames.TO_BYTE, intrinsics.jsToByte) - op(it, ConversionNames.TO_DOUBLE, ::useDispatchReceiver) - op(it, ConversionNames.TO_FLOAT, ::useDispatchReceiver) - op(it, ConversionNames.TO_INT, ::useDispatchReceiver) - op(it, ConversionNames.TO_SHORT, ::useDispatchReceiver) - op(it, ConversionNames.TO_LONG, intrinsics.jsToLong) - } - - for (type in primitiveNumbers) { - op(type, Name.identifier("rangeTo"), ::transformRangeTo) - } - } - - symbolToTransformer.run { - add(irBuiltIns.eqeqeqSymbol, intrinsics.jsEqeqeq) - add(irBuiltIns.eqeqSymbol, ::transformEqeqOperator) - // ieee754equals can only be applied in between statically known Floats, Doubles, null or undefined - add(irBuiltIns.ieee754equalsFunByOperandType, ::chooseEqualityOperatorForPrimitiveTypes) - - add(irBuiltIns.booleanNotSymbol, intrinsics.jsNot) - - add(irBuiltIns.lessFunByOperandType, intrinsics.jsLt) - add(irBuiltIns.lessOrEqualFunByOperandType, intrinsics.jsLtEq) - add(irBuiltIns.greaterFunByOperandType, intrinsics.jsGt) - add(irBuiltIns.greaterOrEqualFunByOperandType, intrinsics.jsGtEq) - - // Arrays - add(context.intrinsics.array.sizeProperty, context.intrinsics.jsArrayLength, true) - add(context.intrinsics.array.getFunction, context.intrinsics.jsArrayGet, true) - add(context.intrinsics.array.setFunction, context.intrinsics.jsArraySet, true) - add(context.intrinsics.array.iterator, context.intrinsics.jsArrayIteratorFunction.owner, true) - for ((key, elementType) in context.intrinsics.primitiveArrays) { - add(key.sizeProperty, context.intrinsics.jsArrayLength, true) - add(key.getFunction, context.intrinsics.jsArrayGet, true) - add(key.setFunction, context.intrinsics.jsArraySet, true) - add(key.iterator, context.intrinsics.jsPrimitiveArrayIteratorFunctions[elementType]!!.owner, true) - - // TODO irCall? - add(key.sizeConstructor) { call -> - IrCallImpl(call.startOffset, call.endOffset, call.type, context.intrinsics.primitiveToSizeConstructor[elementType]!!).apply { - putValueArgument(0, call.getValueArgument(0)) - } - } - } - - add(context.irBuiltIns.stringClass.lengthProperty, context.intrinsics.jsArrayLength, true) - add(context.irBuiltIns.stringClass.getFunction, intrinsics.jsCharSequenceGet.owner, true) - add(context.irBuiltIns.stringClass.owner.declarations.filterIsInstance().single { it.name.asString() == "subSequence"}.symbol, - intrinsics.jsCharSequenceSubSequence.owner, true) - - add(intrinsics.charSequenceLengthPropertyGetterSymbol, intrinsics.jsCharSequenceLength.owner, true) - add(intrinsics.charSequenceGetFunctionSymbol, intrinsics.jsCharSequenceGet.owner, true) - add(intrinsics.charSequenceSubSequenceFunctionSymbol, intrinsics.jsCharSequenceSubSequence.owner, true) - } - - memberToTransformer.run { - for (type in primitiveNumbers) { - // TODO: use increment and decrement when it's possible - op(type, OperatorNames.INC) { - irCall(it, intrinsics.jsPlus.symbol, dispatchReceiverAsFirstArgument = true).apply { - putValueArgument(1, JsIrBuilder.buildInt(irBuiltIns.intType, 1)) - } - } - op(type, OperatorNames.DEC) { - irCall(it, intrinsics.jsMinus.symbol, dispatchReceiverAsFirstArgument = true).apply { - putValueArgument(1, JsIrBuilder.buildInt(irBuiltIns.intType, 1)) - } - } - } - - for (type in primitiveNumbers) { - op(type, OperatorNames.ADD, withLongCoercion(intrinsics.jsPlus)) - op(type, OperatorNames.SUB, withLongCoercion(intrinsics.jsMinus)) - op(type, OperatorNames.MUL, withLongCoercion(intrinsics.jsMult)) - op(type, OperatorNames.DIV, withLongCoercion(intrinsics.jsDiv)) - op(type, OperatorNames.MOD, withLongCoercion(intrinsics.jsMod)) - op(type, OperatorNames.REM, withLongCoercion(intrinsics.jsMod)) - } - - for (type in arrayOf(irBuiltIns.byteType, irBuiltIns.intType)) { - op(type, ConversionNames.TO_CHAR) { - irCall(it, intrinsics.charClassSymbol.constructors.single(), dispatchReceiverAsFirstArgument = true) - } - } - - for (type in arrayOf(irBuiltIns.floatType, irBuiltIns.doubleType)) { - op(type, ConversionNames.TO_CHAR) { - JsIrBuilder.buildCall(intrinsics.charClassSymbol.constructors.single()).apply { - putValueArgument(0, irCall(it, intrinsics.jsNumberToInt, dispatchReceiverAsFirstArgument = true)) - } - } - } - - op(irBuiltIns.charType, ConversionNames.TO_CHAR) { it.dispatchReceiver!! } - } - - nameToTransformer.run { - addWithPredicate( - Name.special(Namer.KCALLABLE_GET_NAME), - { call -> - call.symbol.owner.dispatchReceiverParameter?.run { type.isSubtypeOfClass(context.irBuiltIns.kCallableClass) } ?: false - }, - { call -> irCall(call, context.intrinsics.jsName.symbol, dispatchReceiverAsFirstArgument = true) }) - - addWithPredicate( - Name.identifier(Namer.KPROPERTY_GET), - { call -> - call.symbol.owner.dispatchReceiverParameter?.run { type.isSubtypeOfClass(context.irBuiltIns.kPropertyClass) } ?: false - }, - { call -> irCall(call, context.intrinsics.jsPropertyGet.symbol, dispatchReceiverAsFirstArgument = true) } - ) - - addWithPredicate( - Name.identifier(Namer.KPROPERTY_SET), - { call -> - call.symbol.owner.dispatchReceiverParameter?.run { type.isSubtypeOfClass(context.irBuiltIns.kPropertyClass) } ?: false - }, - { call -> irCall(call, context.intrinsics.jsPropertySet.symbol, dispatchReceiverAsFirstArgument = true) } - ) - - - put(Name.identifier("toString")) { call -> - if (shouldReplaceToStringWithRuntimeCall(call)) { - if (call.isSuperToAny()) { - irCall(call, intrinsics.jsAnyToString, dispatchReceiverAsFirstArgument = true) - } else { - irCall(call, intrinsics.jsToString, dispatchReceiverAsFirstArgument = true) - } - } else { - call - } - } - - put(Name.identifier("hashCode")) { call -> - if (call.symbol.owner.isFakeOverriddenFromAny()) { - if (call.isSuperToAny()) { - irCall(call, intrinsics.jsGetObjectHashCode, dispatchReceiverAsFirstArgument = true) - } else { - irCall(call, intrinsics.jsHashCode, dispatchReceiverAsFirstArgument = true) - } - } else { - call - } - } - - put(Name.identifier("compareTo"), ::transformCompareToMethodCall) - put(Name.identifier("equals"), ::transformEqualsMethodCall) - } - - dynamicCallOriginToIrFunction.apply { - put(IrStatementOrigin.EXCL, context.intrinsics.jsNot) - - put(IrStatementOrigin.LT, context.intrinsics.jsLt) - put(IrStatementOrigin.GT, context.intrinsics.jsGt) - put(IrStatementOrigin.LTEQ, context.intrinsics.jsLtEq) - put(IrStatementOrigin.GTEQ, context.intrinsics.jsGtEq) - - put(IrStatementOrigin.EQEQ, context.intrinsics.jsEqeq) - put(IrStatementOrigin.EQEQEQ, context.intrinsics.jsEqeqeq) - put(IrStatementOrigin.EXCLEQ, context.intrinsics.jsNotEq) - put(IrStatementOrigin.EXCLEQEQ, context.intrinsics.jsNotEqeq) - - put(IrStatementOrigin.ANDAND, context.intrinsics.jsAnd) - put(IrStatementOrigin.OROR, context.intrinsics.jsOr) - - put(IrStatementOrigin.UMINUS, context.intrinsics.jsUnaryMinus) - put(IrStatementOrigin.UPLUS, context.intrinsics.jsUnaryPlus) - - put(IrStatementOrigin.PLUS, context.intrinsics.jsPlus) - put(IrStatementOrigin.MINUS, context.intrinsics.jsMinus) - put(IrStatementOrigin.MUL, context.intrinsics.jsMult) - put(IrStatementOrigin.DIV, context.intrinsics.jsDiv) - put(IrStatementOrigin.PERC, context.intrinsics.jsMod) - - put(IrStatementOrigin.PLUSEQ, context.intrinsics.jsPlusAssign) - put(IrStatementOrigin.MINUSEQ, context.intrinsics.jsMinusAssign) - put(IrStatementOrigin.MULTEQ, context.intrinsics.jsMultAssign) - put(IrStatementOrigin.DIVEQ, context.intrinsics.jsDivAssign) - put(IrStatementOrigin.PERCEQ, context.intrinsics.jsModAssign) - - put(IrStatementOrigin.PREFIX_INCR, context.intrinsics.jsPrefixInc) - put(IrStatementOrigin.PREFIX_DECR, context.intrinsics.jsPrefixDec) - put(IrStatementOrigin.POSTFIX_INCR, context.intrinsics.jsPostfixInc) - put(IrStatementOrigin.POSTFIX_DECR, context.intrinsics.jsPostfixDec) - put(IrStatementOrigin.GET_ARRAY_ELEMENT, context.intrinsics.jsArrayGet) - // TODO add a special statement origin, e.g. SET_ARRAY_ELEMENT - put(IrStatementOrigin.EQ, context.intrinsics.jsArraySet) - } - } - - override fun lower(irFile: IrFile) { - irFile.transform(object : IrElementTransformerVoid() { - private fun lowerConst( - irClass: IrClassSymbol, - carrierFactory: (Int, Int, IrType, C) -> IrExpression, - vararg args: C - ): IrExpression { - val constructor = irClass.constructors.single() - val argType = constructor.owner.valueParameters.first().type - return JsIrBuilder.buildCall(constructor).apply { - for (i in args.indices) { - putValueArgument(i, carrierFactory(UNDEFINED_OFFSET, UNDEFINED_OFFSET, argType, args[i])) - } - } - } - - private fun createLong(v: Long): IrExpression = lowerConst(context.intrinsics.longClassSymbol, IrConstImpl<*>::int, v.toInt(), (v shr 32).toInt()) - - // TODO should this be a separate lowering? - override fun visitConst(expression: IrConst): IrExpression { - with(context.intrinsics) { - return when (expression.type.classifierOrNull) { - uByteClassSymbol -> lowerConst(uByteClassSymbol, IrConstImpl<*>::byte, IrConstKind.Byte.valueOf(expression)) - - uShortClassSymbol -> lowerConst(uShortClassSymbol, IrConstImpl<*>::short, IrConstKind.Short.valueOf(expression)) - - uIntClassSymbol -> lowerConst(uIntClassSymbol, IrConstImpl<*>::int, IrConstKind.Int.valueOf(expression)) - - uLongClassSymbol -> lowerConst(uLongClassSymbol, { _, _, _, v -> createLong(v) }, IrConstKind.Long.valueOf(expression)) - - else -> when { - expression.kind is IrConstKind.Char -> - lowerConst(charClassSymbol, IrConstImpl<*>::int, IrConstKind.Char.valueOf(expression).toInt()) - - expression.kind is IrConstKind.Long -> - createLong(IrConstKind.Long.valueOf(expression)) - - else -> super.visitConst(expression) - } - } - } - } - - override fun visitFunction(declaration: IrFunction): IrStatement { - if (declaration.hasAnnotation(intrinsics.doNotIntrinsifyAnnotationSymbol)) - return declaration - return super.visitFunction(declaration) - } - - override fun visitCall(expression: IrCall): IrExpression { - val call = super.visitCall(expression) - - if (call is IrCall) { - val symbol = call.symbol - val declaration = symbol.owner - - if (declaration.isDynamic() || declaration.isEffectivelyExternal()) { - when (call.origin) { - IrStatementOrigin.GET_PROPERTY -> { - val fieldSymbol = context.symbolTable.lazyWrapper.referenceField( - (symbol.descriptor as PropertyAccessorDescriptor).correspondingProperty - ) - return JsIrBuilder.buildGetField(fieldSymbol, call.dispatchReceiver, type = call.type) - } - - // assignment to a property - IrStatementOrigin.EQ -> { - if (symbol.descriptor is PropertyAccessorDescriptor) { - val fieldSymbol = context.symbolTable.lazyWrapper.referenceField( - (symbol.descriptor as PropertyAccessorDescriptor).correspondingProperty - ) - return call.run { - JsIrBuilder.buildSetField(fieldSymbol, dispatchReceiver, getValueArgument(0)!!, type) - } - } - } - } - } - - if (declaration.isDynamic()) { - dynamicCallOriginToIrFunction[call.origin]?.let { - return irCall(call, it.symbol, dispatchReceiverAsFirstArgument = true) - } - } - - symbolToTransformer[symbol]?.let { - return it(call) - } - - (symbol.owner as? IrFunction)?.dispatchReceiverParameter?.let { - val key = SimpleMemberKey(it.type, symbol.owner.name) - memberToTransformer[key]?.let { - return it(call) - } - } - - nameToTransformer[symbol.owner.name]?.let { - return it(call) - } - } - - return call - } - }, null) - } - - private fun useDispatchReceiver(call: IrCall): IrExpression { - return call.dispatchReceiver!! - } - - private fun transformRangeTo(call: IrCall): IrExpression { - if (call.valueArgumentsCount != 1) return call - return with(call.symbol.owner.valueParameters[0].type) { - when { - isByte() || isShort() || isInt() -> - irCall(call, intrinsics.jsNumberRangeToNumber, dispatchReceiverAsFirstArgument = true) - isLong() -> - irCall(call, intrinsics.jsNumberRangeToLong, dispatchReceiverAsFirstArgument = true) - else -> call - } - } - } - - - private fun withLongCoercion(intrinsic: IrSimpleFunction): (IrCall) -> IrExpression = { call -> - assert(call.valueArgumentsCount == 1) - val arg = call.getValueArgument(0)!! - - if (arg.type.isLong()) { - val receiverType = call.dispatchReceiver!!.type - - when { - // Double OP Long => Double OP Long.toDouble() - receiverType.isDouble() -> { - call.putValueArgument(0, IrCallImpl( - call.startOffset, - call.endOffset, - context.intrinsics.longToDouble.owner.returnType, - context.intrinsics.longToDouble - ).apply { - dispatchReceiver = arg - }) - } - // Float OP Long => Float OP Long.toFloat() - receiverType.isFloat() -> { - call.putValueArgument(0, IrCallImpl( - - call.startOffset, - call.endOffset, - context.intrinsics.longToFloat.owner.returnType, - context.intrinsics.longToFloat - ).apply { - dispatchReceiver = arg - }) - } - // {Byte, Short, Int} OP Long => {Byte, Sort, Int}.toLong() OP Long - !receiverType.isLong() -> { - call.dispatchReceiver = IrCallImpl( - call.startOffset, - call.endOffset, - intrinsics.jsNumberToLong.owner.returnType, - intrinsics.jsNumberToLong - ).apply { - putValueArgument(0, call.dispatchReceiver) - } - } - } - } - - if (call.dispatchReceiver!!.type.isLong()) { - // LHS is Long => use as is - call - } else { - irCall(call, intrinsic.symbol, dispatchReceiverAsFirstArgument = true) - } - } - - private fun transformEqeqOperator(call: IrCall): IrExpression { - val lhs = call.getValueArgument(0)!! - val rhs = call.getValueArgument(1)!! - - - val lhsJsType = lhs.type.getPrimitiveType() - val rhsJsType = rhs.type.getPrimitiveType() - - val equalsMethod = lhs.type.findEqualsMethod() - val isLhsPrimitive = lhsJsType != PrimitiveType.OTHER - - return when { - lhs.type is IrDynamicType -> - irCall(call, intrinsics.jsEqeq.symbol) - - // Special optimization for " == null" - lhs.isNullConst() || rhs.isNullConst() -> - irCall(call, intrinsics.jsEqeq.symbol) - - // For non-float primitives of the same type use JS `==` - isLhsPrimitive && lhsJsType == rhsJsType && lhsJsType != PrimitiveType.FLOATING_POINT_NUMBER -> - chooseEqualityOperatorForPrimitiveTypes(call) - - !isLhsPrimitive && !lhs.type.toKotlinType().isNullable() && equalsMethod != null -> - irCall(call, equalsMethod.symbol, firstArgumentAsDispatchReceiver = true) - - else -> - irCall(call, intrinsics.jsEquals) - } - } - - private fun chooseEqualityOperatorForPrimitiveTypes(call: IrCall): IrExpression = when { - call.allValueArgumentsAreNullable() -> - irCall(call, intrinsics.jsEqeq.symbol) - else -> - irCall(call, intrinsics.jsEqeqeq.symbol) - } - - private fun IrCall.allValueArgumentsAreNullable() = - (0 until valueArgumentsCount).all { getValueArgument(it)!!.type.isNullable() } - - private fun transformCompareToMethodCall(call: IrCall): IrExpression { - val function = call.symbol.owner as IrSimpleFunction - if (function.parent !is IrClass) return call - - fun IrSimpleFunction.isFakeOverriddenFromComparable(): Boolean = when { - origin != IrDeclarationOrigin.FAKE_OVERRIDE -> - parentAsClass.thisReceiver!!.type.isComparable() - - else -> overriddenSymbols.all { it.owner.isFakeOverriddenFromComparable() } - } - - return when { - // Use runtime function call in case when receiverType is a primitive JS type that doesn't have `compareTo` method, - // or has a potential to be primitive type (being fake overridden from `Comparable`) - function.isMethodOfPrimitiveJSType() || function.isFakeOverriddenFromComparable() -> - irCall(call, intrinsics.jsCompareTo, dispatchReceiverAsFirstArgument = true) - - // Valid `compareTo` method must be present at this point - else -> - call - } - } - - - private fun transformEqualsMethodCall(call: IrCall): IrExpression { - val function = call.symbol.owner - return when { - // Nothing special - !function.isEqualsInheritedFromAny() -> call - - // `Any.equals` works as identity operator - call.isSuperToAny() -> - irCall(call, intrinsics.jsEqeqeq.symbol, dispatchReceiverAsFirstArgument = true) - - // Use runtime function call in case when receiverType is a primitive JS type that doesn't have `equals` method, - // or has a potential to be primitive type (being fake overridden from `Any`) - function.isMethodOfPotentiallyPrimitiveJSType() -> - irCall(call, intrinsics.jsEquals, dispatchReceiverAsFirstArgument = true) - - // Valid `equals` method must be present at this point - else -> call - } - } - - private fun IrType.findEqualsMethod(): IrSimpleFunction? { - val klass = getClass() ?: return null - return klass.declarations - .filterIsInstance() - .filter { it.isEqualsInheritedFromAny() && !it.isFakeOverriddenFromAny() } - .also { assert(it.size <= 1) } - .singleOrNull() - } - - private fun IrFunction.isMethodOfPrimitiveJSType() = - dispatchReceiverParameter?.type?.getPrimitiveType() != PrimitiveType.OTHER - - private fun IrFunction.isMethodOfPotentiallyPrimitiveJSType() = - isMethodOfPrimitiveJSType() || isFakeOverriddenFromAny() - - private fun IrFunction.isEqualsInheritedFromAny() = - name == Name.identifier("equals") && - dispatchReceiverParameter != null && - valueParameters.size == 1 && - valueParameters[0].type.isNullableAny() - - private fun shouldReplaceToStringWithRuntimeCall(call: IrCall): Boolean { - // TODO: (KOTLIN-CR-2079) - // - User defined extension functions Any?.toString() call can be lost during lowering. - // - Use direct method call for dynamic types??? - // - Define Any?.toString() in runtime library and stop intrincifying extensions - - if (call.valueArgumentsCount > 0) - return false - - val receiverParameterType = with(call.symbol.owner) { - dispatchReceiverParameter ?: extensionReceiverParameter - }?.type ?: return false - - return receiverParameterType.run { - isArray() || isAny() || isNullable() || this is IrDynamicType || isString() - } - } -} - -enum class PrimitiveType { - FLOATING_POINT_NUMBER, - INTEGER_NUMBER, - STRING, - BOOLEAN, - OTHER -} - -fun IrType.getPrimitiveType() = makeNotNull().run { - when { - isBoolean() -> PrimitiveType.BOOLEAN - isByte() || isShort() || isInt() -> PrimitiveType.INTEGER_NUMBER - isFloat() || isDouble() -> PrimitiveType.FLOATING_POINT_NUMBER - isString() -> PrimitiveType.STRING - else -> PrimitiveType.OTHER - } -} - -private fun MemberToTransformer.op(type: IrType, name: Name, v: IrSimpleFunctionSymbol) { - op(type, name, v = { irCall(it, v, dispatchReceiverAsFirstArgument = true) }) -} - -private fun MemberToTransformer.op(type: IrType, name: Name, v: IrSimpleFunction) { - op(type, name, v.symbol) -} - -private fun MemberToTransformer.op(type: IrType, name: Name, v: (IrCall) -> IrExpression) { - put(SimpleMemberKey(type, name), v) -} - - -// TODO issue: marked as unused, but used; rename works wrongly. -private fun MutableMap.op(type: IrType, name: String, v: V) { - put(SimpleMemberKey(type, Name.identifier(name)), v) -} - -private fun SymbolToTransformer.add(from: Map, to: IrSimpleFunction) { - from.forEach { _, func -> - add(func.symbol, to) - } -} - -private fun SymbolToTransformer.add(from: Map, to: (IrCall) -> IrExpression) { - from.forEach { _, func -> - add(func.symbol, to) - } -} - -private fun SymbolToTransformer.add(from: IrFunctionSymbol, to: (IrCall) -> IrExpression) { - put(from, to) -} - -private fun SymbolToTransformer.add(from: IrFunctionSymbol, to: IrSimpleFunction, dispatchReceiverAsFirstArgument: Boolean = false) { - put(from, { call -> irCall(call, to.symbol, dispatchReceiverAsFirstArgument) }) -} - -private fun MutableMap IrExpression>.addWithPredicate( - from: K, - predicate: (IrCall) -> Boolean, - action: (IrCall) -> IrExpression -) { - put(from) { call: IrCall -> select({ predicate(call) }, { action(call) }, { call }) } -} - -private inline fun select(crossinline predicate: () -> Boolean, crossinline ifTrue: () -> T, crossinline ifFalse: () -> T): T = - if (predicate()) ifTrue() else ifFalse() - -private class SimpleMemberKey(val klass: IrType, val name: Name) { - // TODO drop custom equals and hashCode when IrTypes will have right equals - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as SimpleMemberKey - - if (name != other.name) return false - if (klass.originalKotlinType != other.klass.originalKotlinType) return false - - return true - } - - override fun hashCode(): Int { - var result = klass.originalKotlinType?.hashCode() ?: 0 - result = 31 * result + name.hashCode() - return result - } -} - - -private val IrClassSymbol.sizeProperty - get() = owner.declarations.filterIsInstance().first { it.name.asString() == "size" }.getter!!.symbol - -private val IrClassSymbol.getFunction - get() = owner.declarations.filterIsInstance().first { it.name.asString() == "get" }.symbol - -private val IrClassSymbol.setFunction - get() = owner.declarations.filterIsInstance().first { it.name.asString() == "set" }.symbol - -private val IrClassSymbol.iterator - get() = owner.declarations.filterIsInstance().first { it.name.asString() == "iterator" }.symbol - -private val IrClassSymbol.sizeConstructor - get() = owner.declarations.filterIsInstance().first { it.valueParameters.size == 1 }.symbol - -private val IrClassSymbol.lengthProperty - get() = owner.declarations.filterIsInstance().first { it.name.asString() == "length" }.getter!!.symbol diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/CallsLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/CallsLowering.kt new file mode 100644 index 00000000000..e00b30e4bcc --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/CallsLowering.kt @@ -0,0 +1,55 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.ir.backend.js.lower.calls + +import org.jetbrains.kotlin.backend.common.FileLoweringPass +import org.jetbrains.kotlin.ir.IrStatement +import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext +import org.jetbrains.kotlin.ir.declarations.IrFile +import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.expressions.IrCall +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.util.hasAnnotation +import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid +import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid + +class CallsLowering(val context: JsIrBackendContext) : FileLoweringPass { + private val transformers = listOf( + NumberOperatorCallsTransformer(context), + DynamicCallsTransformer(context), + EqualityAndComparisonCallsTransformer(context), + PrimitiveContainerMemberCallTransformer(context), + MethodsOfAnyCallsTransformer(context), + ReflectionCallsTransformer(context) + ) + + override fun lower(irFile: IrFile) { + irFile.transformChildrenVoid(object : IrElementTransformerVoid() { + override fun visitFunction(declaration: IrFunction): IrStatement { + if (declaration.hasAnnotation(context.intrinsics.doNotIntrinsifyAnnotationSymbol)) + return declaration + return super.visitFunction(declaration) + } + + override fun visitCall(expression: IrCall): IrExpression { + val call = super.visitCall(expression) + if (call is IrCall) { + for (transformer in transformers) { + val newCall = transformer.transformCall(call) + if (newCall !== call) { + return newCall + } + } + } + return call + } + }) + } +} + +interface CallsTransformer { + fun transformCall(call: IrCall): IrExpression +} diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/CallsLoweringUtils.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/CallsLoweringUtils.kt new file mode 100644 index 00000000000..13fd26f2915 --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/CallsLoweringUtils.kt @@ -0,0 +1,100 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.ir.backend.js.lower.calls + +import org.jetbrains.kotlin.ir.backend.js.ir.irCall +import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction +import org.jetbrains.kotlin.ir.expressions.IrCall +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol +import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol +import org.jetbrains.kotlin.ir.types.* +import org.jetbrains.kotlin.ir.types.impl.originalKotlinType +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.types.SimpleType + +typealias SymbolToTransformer = MutableMap IrExpression> + +internal fun SymbolToTransformer.add(from: Map, to: IrSimpleFunction) { + from.forEach { _, func -> + add(func.symbol, to) + } +} + +internal fun SymbolToTransformer.add(from: Map, to: (IrCall) -> IrExpression) { + from.forEach { _, func -> + add(func.symbol, to) + } +} + +internal fun SymbolToTransformer.add(from: IrFunctionSymbol, to: (IrCall) -> IrExpression) { + put(from, to) +} + +internal fun SymbolToTransformer.add(from: IrFunctionSymbol, to: IrSimpleFunction, dispatchReceiverAsFirstArgument: Boolean = false) { + put(from) { call -> irCall(call, to.symbol, dispatchReceiverAsFirstArgument) } +} + +internal fun MutableMap IrExpression>.addWithPredicate( + from: K, + predicate: (IrCall) -> Boolean, + action: (IrCall) -> IrExpression +) { + put(from) { call: IrCall -> if (predicate(call)) action(call) else call } +} + +internal typealias MemberToTransformer = HashMap IrExpression> + +internal fun MemberToTransformer.add(type: IrType, name: Name, v: IrSimpleFunctionSymbol) { + add(type, name) { irCall(it, v, dispatchReceiverAsFirstArgument = true) } +} + +internal fun MemberToTransformer.add(type: IrType, name: Name, v: IrSimpleFunction) { + add(type, name, v.symbol) +} + +internal fun MemberToTransformer.add(type: IrType, name: Name, v: (IrCall) -> IrExpression) { + put(SimpleMemberKey(type, name), v) +} + +internal class SimpleMemberKey(val klass: IrType, val name: Name) { + // TODO drop custom equals and hashCode when IrTypes will have right equals + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as SimpleMemberKey + + if (name != other.name) return false + if (klass.originalKotlinType != other.klass.originalKotlinType) return false + + return true + } + + override fun hashCode(): Int { + var result = klass.originalKotlinType?.hashCode() ?: 0 + result = 31 * result + name.hashCode() + return result + } +} + +enum class PrimitiveType { + FLOATING_POINT_NUMBER, + INTEGER_NUMBER, + STRING, + BOOLEAN, + OTHER +} + +fun IrType.getPrimitiveType() = makeNotNull().run { + when { + isBoolean() -> PrimitiveType.BOOLEAN + isByte() || isShort() || isInt() -> PrimitiveType.INTEGER_NUMBER + isFloat() || isDouble() -> PrimitiveType.FLOATING_POINT_NUMBER + isString() -> PrimitiveType.STRING + else -> PrimitiveType.OTHER + } +} diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/DynamicCallsTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/DynamicCallsTransformer.kt new file mode 100644 index 00000000000..81ccee3fccd --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/DynamicCallsTransformer.kt @@ -0,0 +1,90 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.ir.backend.js.lower.calls + +import org.jetbrains.kotlin.descriptors.PropertyAccessorDescriptor +import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext +import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder +import org.jetbrains.kotlin.ir.backend.js.ir.irCall +import org.jetbrains.kotlin.ir.expressions.IrCall +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin.* +import org.jetbrains.kotlin.ir.util.isDynamic +import org.jetbrains.kotlin.ir.util.isEffectivelyExternal + +class DynamicCallsTransformer(private val context: JsIrBackendContext) : CallsTransformer { + + private val originToIrFunction = + context.intrinsics.run { + mapOf( + EXCL to jsNot, + LT to jsLt, + GT to jsGt, + LTEQ to jsLtEq, + GTEQ to jsGtEq, + EQEQ to jsEqeq, + EQEQEQ to jsEqeqeq, + EXCLEQ to jsNotEq, + EXCLEQEQ to jsNotEqeq, + ANDAND to jsAnd, + OROR to jsOr, + UMINUS to jsUnaryMinus, + UPLUS to jsUnaryPlus, + PLUS to jsPlus, + MINUS to jsMinus, + MUL to jsMult, + DIV to jsDiv, + PERC to jsMod, + PLUSEQ to jsPlusAssign, + MINUSEQ to jsMinusAssign, + MULTEQ to jsMultAssign, + DIVEQ to jsDivAssign, + PERCEQ to jsModAssign, + PREFIX_INCR to jsPrefixInc, + PREFIX_DECR to jsPrefixDec, + POSTFIX_INCR to jsPostfixInc, + POSTFIX_DECR to jsPostfixDec, + GET_ARRAY_ELEMENT to jsArrayGet, + // TODO add a special statement origin, e.g. SET_ARRAY_ELEMENT + EQ to jsArraySet + ) + } + + override fun transformCall(call: IrCall): IrExpression { + val symbol = call.symbol + val function = call.symbol.owner + + if (function.isDynamic() || function.isEffectivelyExternal()) { + when (call.origin) { + GET_PROPERTY -> { + val fieldSymbol = context.symbolTable.lazyWrapper.referenceField( + (symbol.descriptor as PropertyAccessorDescriptor).correspondingProperty + ) + return JsIrBuilder.buildGetField(fieldSymbol, call.dispatchReceiver, type = call.type) + } + + // assignment to a property + EQ -> { + if (symbol.descriptor is PropertyAccessorDescriptor) { + val fieldSymbol = context.symbolTable.lazyWrapper.referenceField( + (symbol.descriptor as PropertyAccessorDescriptor).correspondingProperty + ) + return call.run { + JsIrBuilder.buildSetField(fieldSymbol, dispatchReceiver, getValueArgument(0)!!, type) + } + } + } + } + } + + if (function.isDynamic()) { + originToIrFunction[call.origin]?.let { + return irCall(call, it.symbol, dispatchReceiverAsFirstArgument = true) + } + } + return call + } +} diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/EqualityAndComparisonCallsTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/EqualityAndComparisonCallsTransformer.kt new file mode 100644 index 00000000000..88c05127b8d --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/EqualityAndComparisonCallsTransformer.kt @@ -0,0 +1,164 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.ir.backend.js.lower.calls + +import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext +import org.jetbrains.kotlin.ir.backend.js.ir.irCall +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin +import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction +import org.jetbrains.kotlin.ir.expressions.IrCall +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.types.* +import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.types.isNullable + + +class EqualityAndComparisonCallsTransformer(context: JsIrBackendContext) : CallsTransformer { + private val intrinsics = context.intrinsics + private val irBuiltIns = context.irBuiltIns + + private val symbolToTransformer: SymbolToTransformer = mutableMapOf() + + init { + symbolToTransformer.run { + add(irBuiltIns.eqeqeqSymbol, intrinsics.jsEqeqeq) + add(irBuiltIns.eqeqSymbol, ::transformEqeqOperator) + // ieee754equals can only be applied in between statically known Floats, Doubles, null or undefined + add(irBuiltIns.ieee754equalsFunByOperandType, ::chooseEqualityOperatorForPrimitiveTypes) + + add(irBuiltIns.booleanNotSymbol, intrinsics.jsNot) + + add(irBuiltIns.lessFunByOperandType, intrinsics.jsLt) + add(irBuiltIns.lessOrEqualFunByOperandType, intrinsics.jsLtEq) + add(irBuiltIns.greaterFunByOperandType, intrinsics.jsGt) + add(irBuiltIns.greaterOrEqualFunByOperandType, intrinsics.jsGtEq) + } + } + + + override fun transformCall(call: IrCall): IrExpression { + val symbol = call.symbol + symbolToTransformer[symbol]?.let { + return it(call) + } + + return when (symbol.owner.name) { + Name.identifier("compareTo") -> transformCompareToMethodCall(call) + Name.identifier("equals") -> transformEqualsMethodCall(call) + else -> call + } + } + + private fun transformEqeqOperator(call: IrCall): IrExpression { + val lhs = call.getValueArgument(0)!! + val rhs = call.getValueArgument(1)!! + + + val lhsJsType = lhs.type.getPrimitiveType() + val rhsJsType = rhs.type.getPrimitiveType() + + val equalsMethod = lhs.type.findEqualsMethod() + val isLhsPrimitive = lhsJsType != PrimitiveType.OTHER + + return when { + lhs.type is IrDynamicType -> + irCall(call, intrinsics.jsEqeq.symbol) + + // Special optimization for " == null" + lhs.isNullConst() || rhs.isNullConst() -> + irCall(call, intrinsics.jsEqeq.symbol) + + // For non-float primitives of the same type use JS `==` + isLhsPrimitive && lhsJsType == rhsJsType && lhsJsType != PrimitiveType.FLOATING_POINT_NUMBER -> + chooseEqualityOperatorForPrimitiveTypes(call) + + !isLhsPrimitive && !lhs.type.toKotlinType().isNullable() && equalsMethod != null -> + irCall(call, equalsMethod.symbol, firstArgumentAsDispatchReceiver = true) + + else -> + irCall(call, intrinsics.jsEquals) + } + } + + private fun chooseEqualityOperatorForPrimitiveTypes(call: IrCall): IrExpression = when { + call.allValueArgumentsAreNullable() -> + irCall(call, intrinsics.jsEqeq.symbol) + else -> + irCall(call, intrinsics.jsEqeqeq.symbol) + } + + private fun IrCall.allValueArgumentsAreNullable() = + (0 until valueArgumentsCount).all { getValueArgument(it)!!.type.isNullable() } + + private fun transformCompareToMethodCall(call: IrCall): IrExpression { + val function = call.symbol.owner as IrSimpleFunction + if (function.parent !is IrClass) return call + + fun IrSimpleFunction.isFakeOverriddenFromComparable(): Boolean = when { + origin != IrDeclarationOrigin.FAKE_OVERRIDE -> + parentAsClass.thisReceiver!!.type.isComparable() + + else -> overriddenSymbols.all { it.owner.isFakeOverriddenFromComparable() } + } + + return when { + // Use runtime function call in case when receiverType is a primitive JS type that doesn't have `compareTo` method, + // or has a potential to be primitive type (being fake overridden from `Comparable`) + function.isMethodOfPrimitiveJSType() || function.isFakeOverriddenFromComparable() -> + irCall(call, intrinsics.jsCompareTo, dispatchReceiverAsFirstArgument = true) + + // Valid `compareTo` method must be present at this point + else -> + call + } + } + + + private fun transformEqualsMethodCall(call: IrCall): IrExpression { + val function = call.symbol.owner + return when { + // Nothing special + !function.isEqualsInheritedFromAny() -> call + + // `Any.equals` works as identity operator + call.isSuperToAny() -> + irCall(call, intrinsics.jsEqeqeq.symbol, dispatchReceiverAsFirstArgument = true) + + // Use runtime function call in case when receiverType is a primitive JS type that doesn't have `equals` method, + // or has a potential to be primitive type (being fake overridden from `Any`) + function.isMethodOfPotentiallyPrimitiveJSType() -> + irCall(call, intrinsics.jsEquals, dispatchReceiverAsFirstArgument = true) + + // Valid `equals` method must be present at this point + else -> call + } + } + + private fun IrType.findEqualsMethod(): IrSimpleFunction? { + val klass = getClass() ?: return null + return klass.declarations + .filterIsInstance() + .filter { it.isEqualsInheritedFromAny() && !it.isFakeOverriddenFromAny() } + .also { assert(it.size <= 1) } + .singleOrNull() + } + + private fun IrFunction.isMethodOfPrimitiveJSType() = + dispatchReceiverParameter?.type?.getPrimitiveType() != PrimitiveType.OTHER + + private fun IrFunction.isMethodOfPotentiallyPrimitiveJSType() = + isMethodOfPrimitiveJSType() || isFakeOverriddenFromAny() + + private fun IrFunction.isEqualsInheritedFromAny() = + name == Name.identifier("equals") && + dispatchReceiverParameter != null && + valueParameters.size == 1 && + valueParameters[0].type.isNullableAny() + +} diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/MethodsOfAnyCallsTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/MethodsOfAnyCallsTransformer.kt new file mode 100644 index 00000000000..405b0a9e9d1 --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/MethodsOfAnyCallsTransformer.kt @@ -0,0 +1,82 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.ir.backend.js.lower.calls + +import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext +import org.jetbrains.kotlin.ir.backend.js.ir.irCall +import org.jetbrains.kotlin.ir.expressions.IrCall +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.types.IrDynamicType +import org.jetbrains.kotlin.ir.types.isAny +import org.jetbrains.kotlin.ir.types.isArray +import org.jetbrains.kotlin.ir.types.isString +import org.jetbrains.kotlin.ir.util.isFakeOverriddenFromAny +import org.jetbrains.kotlin.ir.util.isNullable +import org.jetbrains.kotlin.ir.util.isSuperToAny +import org.jetbrains.kotlin.name.Name + + +class MethodsOfAnyCallsTransformer(context: JsIrBackendContext) : CallsTransformer { + private val intrinsics = context.intrinsics + private val nameToTransformer: Map IrExpression> + + init { + nameToTransformer = mutableMapOf() + nameToTransformer.run { + put(Name.identifier("toString")) { call -> + if (shouldReplaceToStringWithRuntimeCall(call)) { + if (call.isSuperToAny()) { + irCall(call, intrinsics.jsAnyToString, dispatchReceiverAsFirstArgument = true) + } else { + irCall(call, intrinsics.jsToString, dispatchReceiverAsFirstArgument = true) + } + } else { + call + } + } + + put(Name.identifier("hashCode")) { call -> + if (call.symbol.owner.isFakeOverriddenFromAny()) { + if (call.isSuperToAny()) { + irCall(call, intrinsics.jsGetObjectHashCode, dispatchReceiverAsFirstArgument = true) + } else { + irCall(call, intrinsics.jsHashCode, dispatchReceiverAsFirstArgument = true) + } + } else { + call + } + } + } + } + + + override fun transformCall(call: IrCall): IrExpression { + val symbol = call.symbol + nameToTransformer[symbol.owner.name]?.let { + return it(call) + } + + return call + } + + private fun shouldReplaceToStringWithRuntimeCall(call: IrCall): Boolean { + // TODO: (KOTLIN-CR-2079) + // - User defined extension functions Any?.toString() call can be lost during lowering. + // - Use direct method call for dynamic types??? + // - Define Any?.toString() in runtime library and stop intrinsicifying extensions + + if (call.valueArgumentsCount > 0) + return false + + val receiverParameterType = with(call.symbol.owner) { + dispatchReceiverParameter ?: extensionReceiverParameter + }?.type ?: return false + + return receiverParameterType.run { + isArray() || isAny() || isNullable() || this is IrDynamicType || isString() + } + } +} diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/NumberOperatorCallsTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/NumberOperatorCallsTransformer.kt new file mode 100644 index 00000000000..9105496a741 --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/NumberOperatorCallsTransformer.kt @@ -0,0 +1,218 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.ir.backend.js.lower.calls + +import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext +import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder +import org.jetbrains.kotlin.ir.backend.js.ir.irCall +import org.jetbrains.kotlin.ir.backend.js.utils.ConversionNames +import org.jetbrains.kotlin.ir.backend.js.utils.OperatorNames +import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction +import org.jetbrains.kotlin.ir.expressions.IrCall +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl +import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol +import org.jetbrains.kotlin.ir.types.* +import org.jetbrains.kotlin.ir.types.impl.originalKotlinType +import org.jetbrains.kotlin.ir.util.constructors +import org.jetbrains.kotlin.name.Name + +class NumberOperatorCallsTransformer(context: JsIrBackendContext) : CallsTransformer { + private val intrinsics = context.intrinsics + private val irBuiltIns = context.irBuiltIns + + private val primitiveNumbers = + irBuiltIns.run { listOf(intType, shortType, byteType, floatType, doubleType) } + + private val memberToTransformer = MemberToTransformer().apply { + for (type in primitiveNumbers) { + add(type, OperatorNames.UNARY_PLUS, intrinsics.jsUnaryPlus) + add(type, OperatorNames.UNARY_MINUS, intrinsics.jsUnaryMinus) + } + + add(irBuiltIns.stringType, OperatorNames.ADD, intrinsics.jsPlus) + + irBuiltIns.intType.let { + add(it, OperatorNames.SHL, intrinsics.jsBitShiftL) + add(it, OperatorNames.SHR, intrinsics.jsBitShiftR) + add(it, OperatorNames.SHRU, intrinsics.jsBitShiftRU) + add(it, OperatorNames.AND, intrinsics.jsBitAnd) + add(it, OperatorNames.OR, intrinsics.jsBitOr) + add(it, OperatorNames.XOR, intrinsics.jsBitXor) + add(it, OperatorNames.INV, intrinsics.jsBitNot) + } + + irBuiltIns.booleanType.let { + add(it, OperatorNames.AND, intrinsics.jsBitAnd) + add(it, OperatorNames.OR, intrinsics.jsBitOr) + add(it, OperatorNames.NOT, intrinsics.jsNot) + add(it, OperatorNames.XOR, intrinsics.jsBitXor) + } + + // Conversion rules are ported from NumberAndCharConversionFIF + // TODO: Add Char and Number conversions + + irBuiltIns.byteType.let { + add(it, ConversionNames.TO_BYTE, ::useDispatchReceiver) + add(it, ConversionNames.TO_DOUBLE, ::useDispatchReceiver) + add(it, ConversionNames.TO_FLOAT, ::useDispatchReceiver) + add(it, ConversionNames.TO_INT, ::useDispatchReceiver) + add(it, ConversionNames.TO_SHORT, ::useDispatchReceiver) + add(it, ConversionNames.TO_LONG, intrinsics.jsToLong) + } + + for (type in listOf(irBuiltIns.floatType, irBuiltIns.doubleType)) { + add(type, ConversionNames.TO_BYTE, intrinsics.jsNumberToByte) + add(type, ConversionNames.TO_DOUBLE, ::useDispatchReceiver) + add(type, ConversionNames.TO_FLOAT, ::useDispatchReceiver) + add(type, ConversionNames.TO_INT, intrinsics.jsNumberToInt) + add(type, ConversionNames.TO_SHORT, intrinsics.jsNumberToShort) + add(type, ConversionNames.TO_LONG, intrinsics.jsNumberToLong) + } + + irBuiltIns.intType.let { + add(it, ConversionNames.TO_BYTE, intrinsics.jsToByte) + add(it, ConversionNames.TO_DOUBLE, ::useDispatchReceiver) + add(it, ConversionNames.TO_FLOAT, ::useDispatchReceiver) + add(it, ConversionNames.TO_INT, ::useDispatchReceiver) + add(it, ConversionNames.TO_SHORT, intrinsics.jsToShort) + add(it, ConversionNames.TO_LONG, intrinsics.jsToLong) + } + + irBuiltIns.shortType.let { + add(it, ConversionNames.TO_BYTE, intrinsics.jsToByte) + add(it, ConversionNames.TO_DOUBLE, ::useDispatchReceiver) + add(it, ConversionNames.TO_FLOAT, ::useDispatchReceiver) + add(it, ConversionNames.TO_INT, ::useDispatchReceiver) + add(it, ConversionNames.TO_SHORT, ::useDispatchReceiver) + add(it, ConversionNames.TO_LONG, intrinsics.jsToLong) + } + + for (type in primitiveNumbers) { + add(type, Name.identifier("rangeTo"), ::transformRangeTo) + } + + for (type in primitiveNumbers) { + // TODO: use increment and decrement when it's possible + add(type, OperatorNames.INC) { + irCall(it, intrinsics.jsPlus.symbol, dispatchReceiverAsFirstArgument = true).apply { + putValueArgument(1, JsIrBuilder.buildInt(irBuiltIns.intType, 1)) + } + } + add(type, OperatorNames.DEC) { + irCall(it, intrinsics.jsMinus.symbol, dispatchReceiverAsFirstArgument = true).apply { + putValueArgument(1, JsIrBuilder.buildInt(irBuiltIns.intType, 1)) + } + } + } + + for (type in primitiveNumbers) { + add(type, OperatorNames.ADD, withLongCoercion(intrinsics.jsPlus)) + add(type, OperatorNames.SUB, withLongCoercion(intrinsics.jsMinus)) + add(type, OperatorNames.MUL, withLongCoercion(intrinsics.jsMult)) + add(type, OperatorNames.DIV, withLongCoercion(intrinsics.jsDiv)) + add(type, OperatorNames.MOD, withLongCoercion(intrinsics.jsMod)) + add(type, OperatorNames.REM, withLongCoercion(intrinsics.jsMod)) + } + + for (type in arrayOf(irBuiltIns.byteType, irBuiltIns.intType)) { + add(type, ConversionNames.TO_CHAR) { + irCall(it, intrinsics.charClassSymbol.constructors.single(), dispatchReceiverAsFirstArgument = true) + } + } + + for (type in arrayOf(irBuiltIns.floatType, irBuiltIns.doubleType)) { + add(type, ConversionNames.TO_CHAR) { + JsIrBuilder.buildCall(intrinsics.charClassSymbol.constructors.single()).apply { + putValueArgument(0, irCall(it, intrinsics.jsNumberToInt, dispatchReceiverAsFirstArgument = true)) + } + } + } + + add(irBuiltIns.charType, ConversionNames.TO_CHAR) { it.dispatchReceiver!! } + } + + override fun transformCall(call: IrCall): IrExpression { + val function = call.symbol.owner + function.dispatchReceiverParameter?.also { + val key = SimpleMemberKey(it.type, function.name) + memberToTransformer[key]?.also { + return it(call) + } + } + return call + } + + private fun useDispatchReceiver(call: IrCall): IrExpression { + return call.dispatchReceiver!! + } + + private fun transformRangeTo(call: IrCall): IrExpression { + if (call.valueArgumentsCount != 1) return call + return with(call.symbol.owner.valueParameters[0].type) { + when { + isByte() || isShort() || isInt() -> + irCall(call, intrinsics.jsNumberRangeToNumber, dispatchReceiverAsFirstArgument = true) + isLong() -> + irCall(call, intrinsics.jsNumberRangeToLong, dispatchReceiverAsFirstArgument = true) + else -> call + } + } + } + + private fun withLongCoercion(intrinsic: IrSimpleFunction): (IrCall) -> IrExpression = { call -> + assert(call.valueArgumentsCount == 1) + val arg = call.getValueArgument(0)!! + + if (arg.type.isLong()) { + val receiverType = call.dispatchReceiver!!.type + + when { + // Double OP Long => Double OP Long.toDouble() + receiverType.isDouble() -> { + call.putValueArgument(0, IrCallImpl( + call.startOffset, + call.endOffset, + intrinsics.longToDouble.owner.returnType, + intrinsics.longToDouble + ).apply { + dispatchReceiver = arg + }) + } + // Float OP Long => Float OP Long.toFloat() + receiverType.isFloat() -> { + call.putValueArgument(0, IrCallImpl( + call.startOffset, + call.endOffset, + intrinsics.longToFloat.owner.returnType, + intrinsics.longToFloat + ).apply { + dispatchReceiver = arg + }) + } + // {Byte, Short, Int} OP Long => {Byte, Sort, Int}.toLong() OP Long + !receiverType.isLong() -> { + call.dispatchReceiver = IrCallImpl( + call.startOffset, + call.endOffset, + intrinsics.jsNumberToLong.owner.returnType, + intrinsics.jsNumberToLong + ).apply { + putValueArgument(0, call.dispatchReceiver) + } + } + } + } + + if (call.dispatchReceiver!!.type.isLong()) { + // LHS is Long => use as is + call + } else { + irCall(call, intrinsic.symbol, dispatchReceiverAsFirstArgument = true) + } + } +} + diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/PrimitiveContainerMemberCallTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/PrimitiveContainerMemberCallTransformer.kt new file mode 100644 index 00000000000..bdf76a5cea2 --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/PrimitiveContainerMemberCallTransformer.kt @@ -0,0 +1,86 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.ir.backend.js.lower.calls + +import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext +import org.jetbrains.kotlin.ir.declarations.IrConstructor +import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.declarations.IrProperty +import org.jetbrains.kotlin.ir.expressions.IrCall +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl +import org.jetbrains.kotlin.ir.symbols.IrClassSymbol + +class PrimitiveContainerMemberCallTransformer(private val context: JsIrBackendContext) : CallsTransformer { + private val intrinsics = context.intrinsics + + private val symbolToTransformer: SymbolToTransformer = mutableMapOf() + + init { + symbolToTransformer.run { + // Arrays + add(context.intrinsics.array.sizeProperty, context.intrinsics.jsArrayLength, true) + add(context.intrinsics.array.getFunction, context.intrinsics.jsArrayGet, true) + add(context.intrinsics.array.setFunction, context.intrinsics.jsArraySet, true) + add(context.intrinsics.array.iterator, context.intrinsics.jsArrayIteratorFunction.owner, true) + for ((key, elementType) in context.intrinsics.primitiveArrays) { + add(key.sizeProperty, context.intrinsics.jsArrayLength, true) + add(key.getFunction, context.intrinsics.jsArrayGet, true) + add(key.setFunction, context.intrinsics.jsArraySet, true) + add(key.iterator, context.intrinsics.jsPrimitiveArrayIteratorFunctions[elementType]!!.owner, true) + + // TODO irCall? + add(key.sizeConstructor) { call -> + IrCallImpl( + call.startOffset, + call.endOffset, + call.type, + context.intrinsics.primitiveToSizeConstructor[elementType]!! + ).apply { + putValueArgument(0, call.getValueArgument(0)) + } + } + } + + add(context.irBuiltIns.stringClass.lengthProperty, context.intrinsics.jsArrayLength, true) + add(context.irBuiltIns.stringClass.getFunction, intrinsics.jsCharSequenceGet.owner, true) + add(context.irBuiltIns.stringClass.subSequence, intrinsics.jsCharSequenceSubSequence.owner, true) + add(intrinsics.charSequenceLengthPropertyGetterSymbol, intrinsics.jsCharSequenceLength.owner, true) + add(intrinsics.charSequenceGetFunctionSymbol, intrinsics.jsCharSequenceGet.owner, true) + add(intrinsics.charSequenceSubSequenceFunctionSymbol, intrinsics.jsCharSequenceSubSequence.owner, true) + } + } + + override fun transformCall(call: IrCall): IrExpression { + val symbol = call.symbol + symbolToTransformer[symbol]?.let { + return it(call) + } + + return call + } +} + +private val IrClassSymbol.sizeProperty + get() = owner.declarations.filterIsInstance().first { it.name.asString() == "size" }.getter!!.symbol + +private val IrClassSymbol.getFunction + get() = owner.declarations.filterIsInstance().first { it.name.asString() == "get" }.symbol + +private val IrClassSymbol.setFunction + get() = owner.declarations.filterIsInstance().first { it.name.asString() == "set" }.symbol + +private val IrClassSymbol.iterator + get() = owner.declarations.filterIsInstance().first { it.name.asString() == "iterator" }.symbol + +private val IrClassSymbol.sizeConstructor + get() = owner.declarations.filterIsInstance().first { it.valueParameters.size == 1 }.symbol + +private val IrClassSymbol.lengthProperty + get() = owner.declarations.filterIsInstance().first { it.name.asString() == "length" }.getter!!.symbol + +private val IrClassSymbol.subSequence + get() = owner.declarations.filterIsInstance().single { it.name.asString() == "subSequence" }.symbol diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/ReflectionCallsTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/ReflectionCallsTransformer.kt new file mode 100644 index 00000000000..6fa7df92818 --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/ReflectionCallsTransformer.kt @@ -0,0 +1,56 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.ir.backend.js.lower.calls + +import org.jetbrains.kotlin.backend.common.utils.isSubtypeOfClass +import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext +import org.jetbrains.kotlin.ir.backend.js.ir.irCall +import org.jetbrains.kotlin.ir.backend.js.utils.Namer +import org.jetbrains.kotlin.ir.expressions.IrCall +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.name.Name + + +class ReflectionCallsTransformer(private val context: JsIrBackendContext) : CallsTransformer { + private val nameToTransformer: Map IrExpression> + + init { + nameToTransformer = mutableMapOf() + nameToTransformer.run { + addWithPredicate( + Name.special(Namer.KCALLABLE_GET_NAME), + { call -> + call.symbol.owner.dispatchReceiverParameter?.run { type.isSubtypeOfClass(context.irBuiltIns.kCallableClass) } ?: false + }, + { call -> irCall(call, context.intrinsics.jsName.symbol, dispatchReceiverAsFirstArgument = true) }) + + addWithPredicate( + Name.identifier(Namer.KPROPERTY_GET), + { call -> + call.symbol.owner.dispatchReceiverParameter?.run { type.isSubtypeOfClass(context.irBuiltIns.kPropertyClass) } ?: false + }, + { call -> irCall(call, context.intrinsics.jsPropertyGet.symbol, dispatchReceiverAsFirstArgument = true) } + ) + + addWithPredicate( + Name.identifier(Namer.KPROPERTY_SET), + { call -> + call.symbol.owner.dispatchReceiverParameter?.run { type.isSubtypeOfClass(context.irBuiltIns.kPropertyClass) } ?: false + }, + { call -> irCall(call, context.intrinsics.jsPropertySet.symbol, dispatchReceiverAsFirstArgument = true) } + ) + } + } + + override fun transformCall(call: IrCall): IrExpression { + val symbol = call.symbol + nameToTransformer[symbol.owner.name]?.let { + return it(call) + } + + return call + } +}