[JS IR BE] Refactor: split IntrinsicifyCallsLowering into multiple classes
This commit is contained in:
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.config.languageVersionSettings
|
|||||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
|
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
|
||||||
import org.jetbrains.kotlin.ir.backend.js.lower.*
|
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.CoroutineIntrinsicLowering
|
||||||
import org.jetbrains.kotlin.ir.backend.js.lower.coroutines.SuspendFunctionsLowering
|
import org.jetbrains.kotlin.ir.backend.js.lower.coroutines.SuspendFunctionsLowering
|
||||||
import org.jetbrains.kotlin.ir.backend.js.lower.inline.FunctionInlining
|
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(clble.getReferenceReplacer())
|
||||||
moduleFragment.files.forEach(ClassReferenceLowering(this)::lower)
|
moduleFragment.files.forEach(ClassReferenceLowering(this)::lower)
|
||||||
moduleFragment.files.forEach(PrimitiveCompanionLowering(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<IrFile>) = files.forEach { lower(it) }
|
private fun FileLoweringPass.lower(files: List<IrFile>) = files.forEach { lower(it) }
|
||||||
|
|||||||
@@ -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 <C> 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 <T> visitConst(expression: IrConst<T>): 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))
|
||||||
|
}
|
||||||
|
}
|
||||||
-711
@@ -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<SimpleMemberKey, (IrCall) -> IrExpression>
|
|
||||||
private typealias SymbolToTransformer = MutableMap<IrFunctionSymbol, (IrCall) -> 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<Name, (IrCall) -> IrExpression>
|
|
||||||
private val dynamicCallOriginToIrFunction: Map<IrStatementOrigin, IrSimpleFunction>
|
|
||||||
|
|
||||||
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<IrFunction>().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 <C> 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 <T> visitConst(expression: IrConst<T>): 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 "<expression> == 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<IrSimpleFunction>()
|
|
||||||
.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 <V> MutableMap<SimpleMemberKey, V>.op(type: IrType, name: String, v: V) {
|
|
||||||
put(SimpleMemberKey(type, Name.identifier(name)), v)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun SymbolToTransformer.add(from: Map<SimpleType, IrSimpleFunction>, to: IrSimpleFunction) {
|
|
||||||
from.forEach { _, func ->
|
|
||||||
add(func.symbol, to)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun SymbolToTransformer.add(from: Map<SimpleType, IrSimpleFunction>, 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 <K> MutableMap<K, (IrCall) -> IrExpression>.addWithPredicate(
|
|
||||||
from: K,
|
|
||||||
predicate: (IrCall) -> Boolean,
|
|
||||||
action: (IrCall) -> IrExpression
|
|
||||||
) {
|
|
||||||
put(from) { call: IrCall -> select({ predicate(call) }, { action(call) }, { call }) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private inline fun <T> 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<IrProperty>().first { it.name.asString() == "size" }.getter!!.symbol
|
|
||||||
|
|
||||||
private val IrClassSymbol.getFunction
|
|
||||||
get() = owner.declarations.filterIsInstance<IrFunction>().first { it.name.asString() == "get" }.symbol
|
|
||||||
|
|
||||||
private val IrClassSymbol.setFunction
|
|
||||||
get() = owner.declarations.filterIsInstance<IrFunction>().first { it.name.asString() == "set" }.symbol
|
|
||||||
|
|
||||||
private val IrClassSymbol.iterator
|
|
||||||
get() = owner.declarations.filterIsInstance<IrFunction>().first { it.name.asString() == "iterator" }.symbol
|
|
||||||
|
|
||||||
private val IrClassSymbol.sizeConstructor
|
|
||||||
get() = owner.declarations.filterIsInstance<IrConstructor>().first { it.valueParameters.size == 1 }.symbol
|
|
||||||
|
|
||||||
private val IrClassSymbol.lengthProperty
|
|
||||||
get() = owner.declarations.filterIsInstance<IrProperty>().first { it.name.asString() == "length" }.getter!!.symbol
|
|
||||||
+55
@@ -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
|
||||||
|
}
|
||||||
+100
@@ -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<IrFunctionSymbol, (IrCall) -> IrExpression>
|
||||||
|
|
||||||
|
internal fun SymbolToTransformer.add(from: Map<SimpleType, IrSimpleFunction>, to: IrSimpleFunction) {
|
||||||
|
from.forEach { _, func ->
|
||||||
|
add(func.symbol, to)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun SymbolToTransformer.add(from: Map<SimpleType, IrSimpleFunction>, 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 <K> MutableMap<K, (IrCall) -> 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<SimpleMemberKey, (IrCall) -> 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
|
||||||
|
}
|
||||||
|
}
|
||||||
+90
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
+164
@@ -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 "<expression> == 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<IrSimpleFunction>()
|
||||||
|
.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()
|
||||||
|
|
||||||
|
}
|
||||||
+82
@@ -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<Name, (IrCall) -> 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+218
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
+86
@@ -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<IrProperty>().first { it.name.asString() == "size" }.getter!!.symbol
|
||||||
|
|
||||||
|
private val IrClassSymbol.getFunction
|
||||||
|
get() = owner.declarations.filterIsInstance<IrFunction>().first { it.name.asString() == "get" }.symbol
|
||||||
|
|
||||||
|
private val IrClassSymbol.setFunction
|
||||||
|
get() = owner.declarations.filterIsInstance<IrFunction>().first { it.name.asString() == "set" }.symbol
|
||||||
|
|
||||||
|
private val IrClassSymbol.iterator
|
||||||
|
get() = owner.declarations.filterIsInstance<IrFunction>().first { it.name.asString() == "iterator" }.symbol
|
||||||
|
|
||||||
|
private val IrClassSymbol.sizeConstructor
|
||||||
|
get() = owner.declarations.filterIsInstance<IrConstructor>().first { it.valueParameters.size == 1 }.symbol
|
||||||
|
|
||||||
|
private val IrClassSymbol.lengthProperty
|
||||||
|
get() = owner.declarations.filterIsInstance<IrProperty>().first { it.name.asString() == "length" }.getter!!.symbol
|
||||||
|
|
||||||
|
private val IrClassSymbol.subSequence
|
||||||
|
get() = owner.declarations.filterIsInstance<IrFunction>().single { it.name.asString() == "subSequence" }.symbol
|
||||||
+56
@@ -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<Name, (IrCall) -> 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
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user