[JS IR BE] Reflection support

This commit is contained in:
Svyatoslav Kuzmich
2018-08-05 13:09:31 +03:00
parent 1471fe08d7
commit 392ad521fd
75 changed files with 563 additions and 104 deletions
@@ -26,6 +26,8 @@ fun IrType.isInterface() = toKotlinType().isInterface()
fun IrType.isPrimitiveArray() = KotlinBuiltIns.isPrimitiveArray(toKotlinType())
fun IrType.getPrimitiveArrayElementType() = KotlinBuiltIns.getPrimitiveArrayElementType(toKotlinType())
fun IrType.isTypeParameter() = toKotlinType().isTypeParameter()
fun IrType.isFunctionOrKFunction() = toKotlinType().isFunctionOrKFunctionType
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.ir.backend.js
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.PrimitiveType
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
@@ -118,11 +119,21 @@ class JsIntrinsics(
// val isCharSymbol = getInternalFunction("isChar")
val isObjectSymbol = getInternalFunction("isObject")
val isPrimitiveArray = mapOf(
PrimitiveType.BOOLEAN to getInternalFunction("isBooleanArray"),
PrimitiveType.BYTE to getInternalFunction("isByteArray"),
PrimitiveType.SHORT to getInternalFunction("isShortArray"),
PrimitiveType.CHAR to getInternalFunction("isCharArray"),
PrimitiveType.INT to getInternalFunction("isIntArray"),
PrimitiveType.FLOAT to getInternalFunction("isFloatArray"),
PrimitiveType.LONG to getInternalFunction("isLongArray"),
PrimitiveType.DOUBLE to getInternalFunction("isLongArray")
)
// Other:
val jsObjectCreate = defineObjectCreateIntrinsic() // Object.create
val jsSetJSField = defineSetJSPropertyIntrinsic() // till we don't have dynamic type we use intrinsic which sets a field with any name
val jsToJsType = defineToJsType() // creates name reference to KotlinType
val jsCode = getInternalFunction("js") // js("<code>")
val jsHashCode = getInternalFunction("hashCode")
val jsGetObjectHashCode = getInternalFunction("getObjectHashCode")
@@ -139,6 +150,10 @@ class JsIntrinsics(
val f = getInternalFunctions("getContinuation")
symbolTable.referenceSimpleFunction(f.single())
}
val jsGetKClass = getInternalWithoutPackage("getKClass")
val jsGetKClassFromExpression = getInternalWithoutPackage("getKClassFromExpression")
val jsClass = getInternalFunction("jsClass")
val jsNumberRangeToNumber = getInternalFunction("numberRangeToNumber")
val jsNumberRangeToLong = getInternalFunction("numberRangeToLong")
@@ -162,28 +177,9 @@ class JsIntrinsics(
private fun getInternalFunction(name: String) =
context.symbolTable.referenceSimpleFunction(context.getInternalFunctions(name).single())
private fun defineToJsType(): IrSimpleFunction {
val desc = SimpleFunctionDescriptorImpl.create(
module,
Annotations.EMPTY,
Name.identifier("\$toJSType\$"),
CallableMemberDescriptor.Kind.SYNTHESIZED,
SourceElement.NO_SOURCE
).apply {
private fun getInternalWithoutPackage(name: String) =
context.symbolTable.referenceSimpleFunction(context.getFunctions(FqName(name)).single())
val typeParameter = TypeParameterDescriptorImpl.createWithDefaultBound(
this,
Annotations.EMPTY,
false,
Variance.INVARIANT,
Name.identifier("T"),
0
)
initialize(null, null, listOf(typeParameter), emptyList(), builtIns.anyType, Modality.FINAL, Visibilities.PUBLIC)
}
return stubBuilder.generateFunctionStub(desc)
}
// TODO: unify how we create intrinsic symbols
private fun defineObjectCreateIntrinsic(): IrSimpleFunction {
@@ -114,6 +114,7 @@ private fun JsIrBackendContext.lower(moduleFragment: IrModuleFragment) {
moduleFragment.files.forEach(clble.getReferenceCollector())
moduleFragment.files.forEach(clble.getClosureBuilder())
moduleFragment.files.forEach(clble.getReferenceReplacer())
moduleFragment.files.forEach(ClassReferenceLowering(this)::lower)
moduleFragment.files.forEach(IntrinsicifyCallsLowering(this)::lower)
}
@@ -0,0 +1,53 @@
/*
* 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.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.IrClassReference
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrGetClass
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
class ClassReferenceLowering(val context: JsIrBackendContext) : FileLoweringPass {
private val intrinsics = context.intrinsics
private fun callGetKClassFromExpression(returnType: IrType, typeArgument: IrType, argument: IrExpression) =
JsIrBuilder.buildCall(intrinsics.jsGetKClassFromExpression, returnType, listOf(typeArgument)).apply {
putValueArgument(0, argument)
}
private fun callGetKClass(returnType: IrType, typeArgument: IrType, argument: IrExpression) =
JsIrBuilder.buildCall(intrinsics.jsGetKClass, returnType, listOf(typeArgument)).apply {
putValueArgument(0, argument)
}
private fun callJsClass(type: IrType) =
JsIrBuilder.buildCall(intrinsics.jsClass, typeArguments = listOf(type))
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitGetClass(expression: IrGetClass) =
callGetKClassFromExpression(
returnType = expression.type,
typeArgument = expression.argument.type,
argument = expression.argument.transform(this, null)
)
override fun visitClassReference(expression: IrClassReference) =
callGetKClass(
returnType = expression.type,
typeArgument = expression.classType,
argument = callJsClass(expression.classType)
)
})
}
}
@@ -325,9 +325,10 @@ class IntrinsicifyCallsLowering(private val context: JsIrBackendContext) : FileL
// assignment to a property
IrStatementOrigin.EQ -> {
val fieldSymbol = IrFieldSymbolImpl((symbol.descriptor as PropertyAccessorDescriptor).correspondingProperty)
return JsIrBuilder.buildSetField(fieldSymbol, call.dispatchReceiver, call.getValueArgument(0)!!, call.type)
if (symbol.descriptor is PropertyAccessorDescriptor) {
val fieldSymbol = IrFieldSymbolImpl((symbol.descriptor as PropertyAccessorDescriptor).correspondingProperty)
return JsIrBuilder.buildSetField(fieldSymbol, call.dispatchReceiver, call.getValueArgument(0)!!, call.type)
}
}
}
}
@@ -50,7 +50,7 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : FileLoweringPass {
private val instanceOfIntrinsicSymbol = context.intrinsics.jsInstanceOf.symbol
private val typeOfIntrinsicSymbol = context.intrinsics.jsTypeOf.symbol
private val toJSTypeIntrinsicSymbol = context.intrinsics.jsToJsType.symbol
private val jsClassIntrinsicSymbol = context.intrinsics.jsClass
private val stringMarker = JsIrBuilder.buildString(context.irBuiltIns.stringType, "string")
private val booleanMarker = JsIrBuilder.buildString(context.irBuiltIns.stringType, "boolean")
@@ -241,13 +241,14 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : FileLoweringPass {
}
private fun wrapTypeReference(toType: IrType) =
JsIrBuilder.buildCall(toJSTypeIntrinsicSymbol).apply { putTypeArgument(0, toType) }
JsIrBuilder.buildCall(jsClassIntrinsicSymbol).apply { putTypeArgument(0, toType) }
private fun generateGenericArrayCheck(argument: IrExpression) =
JsIrBuilder.buildCall(isArraySymbol).apply { putValueArgument(0, argument) }
private fun generatePrimitiveArrayTypeCheck(argument: IrExpression, toType: IrType): IrExpression {
TODO("Implement Typed Array check")
val f = context.intrinsics.isPrimitiveArray[toType.getPrimitiveArrayElementType()]!!
return JsIrBuilder.buildCall(f).apply { putValueArgument(0, argument) }
}
private fun generateInterfaceCheck(argument: IrExpression, toType: IrType): IrExpression {
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.ir.types.classifierOrFail
import org.jetbrains.kotlin.ir.types.isAny
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.isInterface
import org.jetbrains.kotlin.ir.util.isObject
import org.jetbrains.kotlin.ir.util.isReal
import org.jetbrains.kotlin.ir.util.resolveFakeOverride
import org.jetbrains.kotlin.js.backend.ast.*
@@ -158,6 +159,15 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
metadataLiteral.propertyInitializers += simpleNameProp
}
val classKind = JsStringLiteral(
when {
irClass.isInterface -> "interface"
irClass.isObject -> "object"
else -> "class"
}
)
metadataLiteral.propertyInitializers += JsPropertyInitializer(JsNameRef(Namer.METADATA_CLASS_KIND), classKind)
metadataLiteral.propertyInitializers += generateSuperClasses()
return jsAssignment(JsNameRef(Namer.METADATA, classNameRef), metadataLiteral).makeStmt()
}
@@ -90,7 +90,7 @@ class JsIntrinsicTransformers(backendContext: JsIrBackendContext) {
jsAssignment(JsNameRef(fieldNameLiteral, receiver), fieldValue)
}
add(intrinsics.jsToJsType) { call, context ->
add(intrinsics.jsClass) { call, context ->
val typeName = context.getNameForSymbol(call.getTypeArgument(0)!!.classifierOrFail)
typeName.makeRef()
}