[JS IR BE] Support typeOf

This commit is contained in:
Svyatoslav Kuzmich
2019-09-30 12:37:45 +03:00
parent 3df8393ede
commit e32ec2a789
13 changed files with 157 additions and 27 deletions
@@ -32,7 +32,7 @@ import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.util.OperatorNameConventions
class FunctionInlining(val context: CommonBackendContext, val dontInlineTypeOf: Boolean = true) : IrElementTransformerVoidWithContext() { class FunctionInlining(val context: CommonBackendContext) : IrElementTransformerVoidWithContext() {
fun inline(irModule: IrModuleFragment) = irModule.accept(this, data = null) fun inline(irModule: IrModuleFragment) = irModule.accept(this, data = null)
@@ -47,8 +47,7 @@ class FunctionInlining(val context: CommonBackendContext, val dontInlineTypeOf:
return expression return expression
if (Symbols.isLateinitIsInitializedPropertyGetter(callee.symbol)) if (Symbols.isLateinitIsInitializedPropertyGetter(callee.symbol))
return expression return expression
// TODO: Temporary hack till typeOf is unimplemented in K/JS if (Symbols.isTypeOfIntrinsic(callee.symbol))
if (dontInlineTypeOf && Symbols.isTypeOfIntrinsic(callee.symbol))
return expression return expression
val actualCallee = getFunctionDeclaration(callee.symbol) val actualCallee = getFunctionDeclaration(callee.symbol)
@@ -15,17 +15,20 @@ import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrExternalPackageFragmentSymbolImpl import org.jetbrains.kotlin.ir.symbols.impl.IrExternalPackageFragmentSymbolImpl
import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.defaultType import org.jetbrains.kotlin.ir.types.defaultType
import org.jetbrains.kotlin.ir.types.isLong import org.jetbrains.kotlin.ir.types.isLong
import org.jetbrains.kotlin.ir.util.companionObject
import org.jetbrains.kotlin.ir.util.constructors import org.jetbrains.kotlin.ir.util.constructors
import org.jetbrains.kotlin.ir.util.findDeclaration import org.jetbrains.kotlin.ir.util.findDeclaration
import org.jetbrains.kotlin.ir.util.kotlinPackageFqn import org.jetbrains.kotlin.ir.util.kotlinPackageFqn
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi2ir.findSingleFunction import org.jetbrains.kotlin.psi2ir.findSingleFunction
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.util.capitalizeDecapitalize.toLowerCaseAsciiOnly import org.jetbrains.kotlin.util.capitalizeDecapitalize.toLowerCaseAsciiOnly
import java.util.* import java.util.*
@@ -235,6 +238,14 @@ class JsIntrinsics(private val irBuiltIns: IrBuiltIns, val context: JsIrBackendC
) )
) )
val createKType = getInternalWithoutPackageOrNull("createKType")
val createDynamicKType = getInternalWithoutPackageOrNull("createDynamicKType")
val createKTypeParameter = getInternalWithoutPackageOrNull("createKTypeParameter")
val getStarKTypeProjection = getInternalWithoutPackageOrNull("getStarKTypeProjection")
val createCovariantKTypeProjection = getInternalWithoutPackageOrNull("createCovariantKTypeProjection")
val createInvariantKTypeProjection = getInternalWithoutPackageOrNull("createInvariantKTypeProjection")
val createContravariantKTypeProjection = getInternalWithoutPackageOrNull("createContravariantKTypeProjection")
val primitiveToSizeConstructor = val primitiveToSizeConstructor =
PrimitiveType.values().associate { type -> PrimitiveType.values().associate { type ->
type to (primitiveToTypedArrayMap[type]?.let { type to (primitiveToTypedArrayMap[type]?.let {
@@ -292,6 +303,11 @@ class JsIntrinsics(private val irBuiltIns: IrBuiltIns, val context: JsIrBackendC
private fun getInternalWithoutPackage(name: String) = private fun getInternalWithoutPackage(name: String) =
context.symbolTable.referenceSimpleFunction(context.getFunctions(FqName(name)).single()) context.symbolTable.referenceSimpleFunction(context.getFunctions(FqName(name)).single())
private fun getInternalWithoutPackageOrNull(name: String): IrSimpleFunctionSymbol? {
val descriptor = context.getFunctions(FqName(name)).singleOrNull() ?: return null
return context.symbolTable.referenceSimpleFunction(descriptor)
}
private fun getFunctionInKotlinPackage(name: String) = private fun getFunctionInKotlinPackage(name: String) =
context.symbolTable.referenceSimpleFunction(context.getFunctions(kotlinPackageFqn.child(Name.identifier(name))).single()) context.symbolTable.referenceSimpleFunction(context.getFunctions(kotlinPackageFqn.child(Name.identifier(name))).single())
@@ -98,7 +98,7 @@ private val arrayConstructorPhase = makeJsModulePhase(
private val functionInliningPhase = makeCustomJsModulePhase( private val functionInliningPhase = makeCustomJsModulePhase(
{ context, module -> { context, module ->
FunctionInlining(context, false).inline(module) FunctionInlining(context).inline(module)
module.patchDeclarationParents() module.patchDeclarationParents()
}, },
name = "FunctionInliningPhase", name = "FunctionInliningPhase",
@@ -6,20 +6,27 @@
package org.jetbrains.kotlin.ir.backend.js.lower package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.ir.Symbols
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext 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.JsIrBuilder
import org.jetbrains.kotlin.ir.backend.js.utils.toJsArrayLiteral
import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.declarations.IrTypeParameter
import org.jetbrains.kotlin.ir.expressions.IrCall import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrClassReference import org.jetbrains.kotlin.ir.expressions.IrClassReference
import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrGetClass import org.jetbrains.kotlin.ir.expressions.IrGetClass
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.isFunction import org.jetbrains.kotlin.ir.util.isFunction
import org.jetbrains.kotlin.ir.util.isThrowable import org.jetbrains.kotlin.ir.util.isThrowable
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.*
class ClassReferenceLowering(val context: JsIrBackendContext) : FileLoweringPass { class ClassReferenceLowering(val context: JsIrBackendContext) : FileLoweringPass {
private val intrinsics = context.intrinsics private val intrinsics = context.intrinsics
@@ -108,23 +115,116 @@ class ClassReferenceLowering(val context: JsIrBackendContext) : FileLoweringPass
return null return null
} }
private fun callGetKClass(returnType: IrType, typeArgument: IrType): IrCall { private fun callGetKClass(
returnType: IrType = intrinsics.jsGetKClass.owner.returnType,
typeArgument: IrType
): IrCall {
val primitiveKClass = val primitiveKClass =
getFinalPrimitiveKClass(returnType, typeArgument) ?: getOpenPrimitiveKClass(returnType, typeArgument) getFinalPrimitiveKClass(returnType, typeArgument) ?: getOpenPrimitiveKClass(returnType, typeArgument)
if (primitiveKClass != null) if (primitiveKClass != null)
return primitiveKClass return primitiveKClass
return JsIrBuilder.buildCall(intrinsics.jsGetKClass, returnType, listOf(typeArgument)).apply { return JsIrBuilder.buildCall(intrinsics.jsGetKClass, returnType, listOf(typeArgument))
putValueArgument(0, callJsClass(typeArgument)) .apply {
} putValueArgument(0, callJsClass(typeArgument))
}
} }
private fun callJsClass(type: IrType) = private fun callJsClass(type: IrType) =
JsIrBuilder.buildCall(intrinsics.jsClass, typeArguments = listOf(type)) JsIrBuilder.buildCall(intrinsics.jsClass, typeArguments = listOf(type))
private fun buildCall(name: IrSimpleFunctionSymbol, vararg args: IrExpression): IrExpression =
JsIrBuilder.buildCall(name).apply {
args.forEachIndexed { index, irExpression ->
putValueArgument(index, irExpression)
}
}
fun createKType(type: IrType): IrExpression {
if (type is IrSimpleType)
return createSimpleKType(type)
if (type is IrDynamicType)
return createDynamicType()
error("Unexpected type $type")
}
private fun createDynamicType(): IrExpression {
return buildCall(context.intrinsics.createDynamicKType!!)
}
private fun createSimpleKType(type: IrSimpleType): IrExpression {
val classifier: IrClassifierSymbol = type.classifier
if (classifier is IrTypeParameterSymbol && classifier.owner.isReified) {
error("Fail")
}
val kClassifier = createKClassifier(classifier)
// TODO: Use static array types
val arguments = type.arguments.map { createKTypeProjection(it) }.toJsArrayLiteral(
context,
context.dynamicType,
context.dynamicType
)
val isMarkedNullable = JsIrBuilder.buildBoolean(context.irBuiltIns.booleanType, type.isMarkedNullable())
return buildCall(
context.intrinsics.createKType!!,
kClassifier,
arguments,
isMarkedNullable
)
}
private fun createKTypeProjection(tp: IrTypeArgument): IrExpression {
if (tp !is IrTypeProjection) {
return buildCall(context.intrinsics.getStarKTypeProjection!!)
}
val factoryName = when (tp.variance) {
Variance.INVARIANT -> context.intrinsics.createInvariantKTypeProjection!!
Variance.IN_VARIANCE -> context.intrinsics.createContravariantKTypeProjection!!
Variance.OUT_VARIANCE -> context.intrinsics.createCovariantKTypeProjection!!
}
val kType = createKType(tp.type)
return buildCall(factoryName, kType)
}
private fun createKClassifier(classifier: IrClassifierSymbol): IrExpression =
when (classifier) {
is IrTypeParameterSymbol -> createKTypeParameter(classifier.owner)
else -> callGetKClass(typeArgument = classifier.defaultType)
}
private fun createKTypeParameter(typeParameter: IrTypeParameter): IrExpression {
val name = JsIrBuilder.buildString(context.irBuiltIns.stringType, typeParameter.name.asString())
val upperBounds = typeParameter.superTypes.map { createKType(it) }.toJsArrayLiteral(
context,
context.dynamicType,
context.dynamicType
)
val variance = when (typeParameter.variance) {
Variance.INVARIANT -> JsIrBuilder.buildString(context.irBuiltIns.stringType, "invariant")
Variance.IN_VARIANCE -> JsIrBuilder.buildString(context.irBuiltIns.stringType, "in")
Variance.OUT_VARIANCE -> JsIrBuilder.buildString(context.irBuiltIns.stringType, "out")
}
if (typeParameter.isReified) {
error("Reified parameter")
}
return buildCall(
context.intrinsics.createKTypeParameter!!,
name,
upperBounds,
variance
)
}
override fun lower(irFile: IrFile) { override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(object : IrElementTransformerVoid() { irFile.transformChildrenVoid(object : IrElementTransformerVoidWithContext() {
override fun visitGetClass(expression: IrGetClass) = override fun visitGetClass(expression: IrGetClass) =
callGetKClassFromExpression( callGetKClassFromExpression(
returnType = expression.type, returnType = expression.type,
@@ -137,6 +237,13 @@ class ClassReferenceLowering(val context: JsIrBackendContext) : FileLoweringPass
returnType = expression.type, returnType = expression.type,
typeArgument = expression.classType.makeNotNull() typeArgument = expression.classType.makeNotNull()
) )
override fun visitCall(expression: IrCall): IrExpression =
if (Symbols.isTypeOfIntrinsic(expression.symbol)) {
createKType(expression.getTypeArgument(0)!!)
} else {
super.visitCall(expression)
}
}) })
} }
} }
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext 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.JsIrBuilder
import org.jetbrains.kotlin.ir.backend.js.utils.toJsArrayLiteral
import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.declarations.buildField import org.jetbrains.kotlin.ir.builders.declarations.buildField
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
@@ -382,23 +383,16 @@ class EnumClassTransformer(val context: JsIrBackendContext, private val irClass:
} }
} }
private fun List<IrExpression>.toArrayLiteral(arrayType: IrType, elementType: IrType): IrExpression {
val irVararg = IrVarargImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, arrayType, elementType, this)
return IrCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, arrayType, context.intrinsics.arrayLiteral).apply {
putValueArgument(0, irVararg)
}
}
private fun createEnumValuesBody(): IrBody { private fun createEnumValuesBody(): IrBody {
val valuesFun = findFunctionDescriptorForMemberWithSyntheticBodyKind(IrSyntheticBodyKind.ENUM_VALUES) val valuesFun = findFunctionDescriptorForMemberWithSyntheticBodyKind(IrSyntheticBodyKind.ENUM_VALUES)
val entryInstanceToFunction = context.enumEntryToGetInstanceFunction val entryInstanceToFunction = context.enumEntryToGetInstanceFunction
val backendContext = context
return context.createIrBuilder(valuesFun.symbol).run { return context.createIrBuilder(valuesFun.symbol).run {
irBlockBody { irBlockBody {
+irReturn( +irReturn(
enumEntries.map { irCall(entryInstanceToFunction[it.symbol]!!) } enumEntries.map { irCall(entryInstanceToFunction[it.symbol]!!) }
.toArrayLiteral(valuesFun.returnType, irClass.defaultType) .toJsArrayLiteral(backendContext, valuesFun.returnType, irClass.defaultType)
) )
} }
} }
@@ -6,7 +6,13 @@
package org.jetbrains.kotlin.ir.backend.js.utils package org.jetbrains.kotlin.ir.backend.js.utils
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.isNullableAny import org.jetbrains.kotlin.ir.types.isNullableAny
import org.jetbrains.kotlin.ir.util.isTopLevelDeclaration import org.jetbrains.kotlin.ir.util.isTopLevelDeclaration
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
@@ -25,3 +31,11 @@ fun IrDeclaration.hasStaticDispatch() = when (this) {
is IrField -> isStatic is IrField -> isStatic
else -> true else -> true
} }
fun List<IrExpression>.toJsArrayLiteral(context: JsIrBackendContext, arrayType: IrType, elementType: IrType): IrExpression {
val irVararg = IrVarargImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, arrayType, elementType, this)
return IrCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, arrayType, context.intrinsics.arrayLiteral).apply {
putValueArgument(0, irVararg)
}
}
@@ -1,7 +1,7 @@
// !USE_EXPERIMENTAL: kotlin.ExperimentalStdlibApi // !USE_EXPERIMENTAL: kotlin.ExperimentalStdlibApi
// TARGET_BACKEND: JS // TARGET_BACKEND: JS
// IGNORE_BACKEND: JS_IR
// WITH_REFLECT // WITH_REFLECT
// KJS_WITH_FULL_RUNTIME
package test package test
@@ -1,7 +1,7 @@
// !USE_EXPERIMENTAL: kotlin.ExperimentalStdlibApi // !USE_EXPERIMENTAL: kotlin.ExperimentalStdlibApi
// TARGET_BACKEND: JS // TARGET_BACKEND: JS
// IGNORE_BACKEND: JS_IR
// WITH_REFLECT // WITH_REFLECT
// KJS_WITH_FULL_RUNTIME
package test package test
@@ -1,7 +1,7 @@
// !USE_EXPERIMENTAL: kotlin.ExperimentalStdlibApi // !USE_EXPERIMENTAL: kotlin.ExperimentalStdlibApi
// TARGET_BACKEND: JS // TARGET_BACKEND: JS
// IGNORE_BACKEND: JS_IR
// WITH_REFLECT // WITH_REFLECT
// KJS_WITH_FULL_RUNTIME
import kotlin.test.* import kotlin.test.*
import kotlin.reflect.* import kotlin.reflect.*
@@ -1,7 +1,7 @@
// !USE_EXPERIMENTAL: kotlin.ExperimentalStdlibApi // !USE_EXPERIMENTAL: kotlin.ExperimentalStdlibApi
// TARGET_BACKEND: JS // TARGET_BACKEND: JS
// IGNORE_BACKEND: JS_IR
// WITH_REFLECT // WITH_REFLECT
// KJS_WITH_FULL_RUNTIME
package test package test
@@ -1,7 +1,7 @@
// !USE_EXPERIMENTAL: kotlin.ExperimentalStdlibApi // !USE_EXPERIMENTAL: kotlin.ExperimentalStdlibApi
// TARGET_BACKEND: JS // TARGET_BACKEND: JS
// IGNORE_BACKEND: JS_IR
// WITH_REFLECT // WITH_REFLECT
// KJS_WITH_FULL_RUNTIME
package test package test
@@ -1,7 +1,7 @@
// !USE_EXPERIMENTAL: kotlin.ExperimentalStdlibApi // !USE_EXPERIMENTAL: kotlin.ExperimentalStdlibApi
// TARGET_BACKEND: JS // TARGET_BACKEND: JS
// IGNORE_BACKEND: JS_IR
// WITH_REFLECT // WITH_REFLECT
// KJS_WITH_FULL_RUNTIME
// MODULE: lib1 // MODULE: lib1
// FILE: lib1.kt // FILE: lib1.kt
@@ -1,7 +1,7 @@
// !USE_EXPERIMENTAL: kotlin.ExperimentalStdlibApi // !USE_EXPERIMENTAL: kotlin.ExperimentalStdlibApi
// TARGET_BACKEND: JS // TARGET_BACKEND: JS
// IGNORE_BACKEND: JS_IR
// WITH_REFLECT // WITH_REFLECT
// KJS_WITH_FULL_RUNTIME
import kotlin.reflect.typeOf import kotlin.reflect.typeOf