[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.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)
@@ -47,8 +47,7 @@ class FunctionInlining(val context: CommonBackendContext, val dontInlineTypeOf:
return expression
if (Symbols.isLateinitIsInitializedPropertyGetter(callee.symbol))
return expression
// TODO: Temporary hack till typeOf is unimplemented in K/JS
if (dontInlineTypeOf && Symbols.isTypeOfIntrinsic(callee.symbol))
if (Symbols.isTypeOfIntrinsic(callee.symbol))
return expression
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.impl.IrExternalPackageFragmentImpl
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.types.IrType
import org.jetbrains.kotlin.ir.types.defaultType
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.findDeclaration
import org.jetbrains.kotlin.ir.util.kotlinPackageFqn
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
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 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 =
PrimitiveType.values().associate { type ->
type to (primitiveToTypedArrayMap[type]?.let {
@@ -292,6 +303,11 @@ class JsIntrinsics(private val irBuiltIns: IrBuiltIns, val context: JsIrBackendC
private fun getInternalWithoutPackage(name: String) =
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) =
context.symbolTable.referenceSimpleFunction(context.getFunctions(kotlinPackageFqn.child(Name.identifier(name))).single())
@@ -98,7 +98,7 @@ private val arrayConstructorPhase = makeJsModulePhase(
private val functionInliningPhase = makeCustomJsModulePhase(
{ context, module ->
FunctionInlining(context, false).inline(module)
FunctionInlining(context).inline(module)
module.patchDeclarationParents()
},
name = "FunctionInliningPhase",
@@ -6,20 +6,27 @@
package org.jetbrains.kotlin.ir.backend.js.lower
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.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.backend.js.utils.toJsArrayLiteral
import org.jetbrains.kotlin.ir.declarations.IrFile
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.IrClassReference
import org.jetbrains.kotlin.ir.expressions.IrExpression
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.util.isFunction
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.name.Name
import org.jetbrains.kotlin.types.*
class ClassReferenceLowering(val context: JsIrBackendContext) : FileLoweringPass {
private val intrinsics = context.intrinsics
@@ -108,23 +115,116 @@ class ClassReferenceLowering(val context: JsIrBackendContext) : FileLoweringPass
return null
}
private fun callGetKClass(returnType: IrType, typeArgument: IrType): IrCall {
private fun callGetKClass(
returnType: IrType = intrinsics.jsGetKClass.owner.returnType,
typeArgument: IrType
): IrCall {
val primitiveKClass =
getFinalPrimitiveKClass(returnType, typeArgument) ?: getOpenPrimitiveKClass(returnType, typeArgument)
if (primitiveKClass != null)
return primitiveKClass
return JsIrBuilder.buildCall(intrinsics.jsGetKClass, returnType, listOf(typeArgument)).apply {
putValueArgument(0, callJsClass(typeArgument))
}
return JsIrBuilder.buildCall(intrinsics.jsGetKClass, returnType, listOf(typeArgument))
.apply {
putValueArgument(0, callJsClass(typeArgument))
}
}
private fun callJsClass(type: IrType) =
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) {
irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
irFile.transformChildrenVoid(object : IrElementTransformerVoidWithContext() {
override fun visitGetClass(expression: IrGetClass) =
callGetKClassFromExpression(
returnType = expression.type,
@@ -137,6 +237,13 @@ class ClassReferenceLowering(val context: JsIrBackendContext) : FileLoweringPass
returnType = expression.type,
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.backend.js.JsIrBackendContext
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.declarations.buildField
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 {
val valuesFun = findFunctionDescriptorForMemberWithSyntheticBodyKind(IrSyntheticBodyKind.ENUM_VALUES)
val entryInstanceToFunction = context.enumEntryToGetInstanceFunction
val backendContext = context
return context.createIrBuilder(valuesFun.symbol).run {
irBlockBody {
+irReturn(
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
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.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.util.isTopLevelDeclaration
import org.jetbrains.kotlin.name.Name
@@ -24,4 +30,12 @@ fun IrDeclaration.hasStaticDispatch() = when (this) {
is IrProperty -> isTopLevelDeclaration
is IrField -> isStatic
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
// TARGET_BACKEND: JS
// IGNORE_BACKEND: JS_IR
// WITH_REFLECT
// KJS_WITH_FULL_RUNTIME
package test
@@ -1,7 +1,7 @@
// !USE_EXPERIMENTAL: kotlin.ExperimentalStdlibApi
// TARGET_BACKEND: JS
// IGNORE_BACKEND: JS_IR
// WITH_REFLECT
// KJS_WITH_FULL_RUNTIME
package test
@@ -1,7 +1,7 @@
// !USE_EXPERIMENTAL: kotlin.ExperimentalStdlibApi
// TARGET_BACKEND: JS
// IGNORE_BACKEND: JS_IR
// WITH_REFLECT
// KJS_WITH_FULL_RUNTIME
import kotlin.test.*
import kotlin.reflect.*
@@ -1,7 +1,7 @@
// !USE_EXPERIMENTAL: kotlin.ExperimentalStdlibApi
// TARGET_BACKEND: JS
// IGNORE_BACKEND: JS_IR
// WITH_REFLECT
// KJS_WITH_FULL_RUNTIME
package test
@@ -1,7 +1,7 @@
// !USE_EXPERIMENTAL: kotlin.ExperimentalStdlibApi
// TARGET_BACKEND: JS
// IGNORE_BACKEND: JS_IR
// WITH_REFLECT
// KJS_WITH_FULL_RUNTIME
package test
@@ -1,7 +1,7 @@
// !USE_EXPERIMENTAL: kotlin.ExperimentalStdlibApi
// TARGET_BACKEND: JS
// IGNORE_BACKEND: JS_IR
// WITH_REFLECT
// KJS_WITH_FULL_RUNTIME
// MODULE: lib1
// FILE: lib1.kt
@@ -1,7 +1,7 @@
// !USE_EXPERIMENTAL: kotlin.ExperimentalStdlibApi
// TARGET_BACKEND: JS
// IGNORE_BACKEND: JS_IR
// WITH_REFLECT
// KJS_WITH_FULL_RUNTIME
import kotlin.reflect.typeOf