[JS IR BE] Reflection support
This commit is contained in:
+2
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
+53
@@ -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)
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+4
-3
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-3
@@ -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 {
|
||||
|
||||
+10
@@ -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()
|
||||
}
|
||||
|
||||
+1
-1
@@ -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()
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
operator fun Int.compareTo(c: Char) = 0
|
||||
|
||||
fun foo(x: Int, y: Char): String {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// !LANGUAGE: +ProperIeee754Comparisons
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
operator fun Int.compareTo(c: Char) = 0
|
||||
|
||||
fun foo(x: Int, y: Char): String {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// !LANGUAGE: +ProperIeee754Comparisons
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
class C {
|
||||
operator fun Int.compareTo(c: Char) = 0
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// WITH_RUNTIME
|
||||
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// IGNORE_BACKEND: NATIVE
|
||||
|
||||
class Generic<P : Any>(val p: P)
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
class B
|
||||
|
||||
fun B.magic() {
|
||||
|
||||
-1
@@ -1,5 +1,4 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// See KT-12337 Reference to property with invisible setter should not be a KMutableProperty
|
||||
|
||||
import kotlin.reflect.KProperty1
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// IGNORE_BACKEND: NATIVE
|
||||
// KT-16291 Smart cast doesn't work when getting class of instance
|
||||
|
||||
|
||||
Vendored
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun box(): String {
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun box(): String {
|
||||
|
||||
Vendored
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun box(): String {
|
||||
|
||||
Vendored
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// WITH_RUNTIME
|
||||
|
||||
class Host(var value: String) {
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun box(): String {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
//WITH_RUNTIME
|
||||
class Test {
|
||||
|
||||
|
||||
Vendored
-1
@@ -1,5 +1,4 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun box(): String {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun box(): String {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
enum class Test(val x: String, val closure1: () -> String) {
|
||||
FOO("O", run { { FOO.x } }) {
|
||||
override val y: String = "K"
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
enum class X {
|
||||
B {
|
||||
val value2 = "K"
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
fun test1(f: (Int) -> Int) = f(1)
|
||||
|
||||
fun test2(f: Int.() -> Int) = 2.f()
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
fun box(): String {
|
||||
var s = ""
|
||||
var foo = "O"
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
fun box(): String {
|
||||
fun foo(x: String) = x
|
||||
fun foo() = foo("K")
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
fun box(): String {
|
||||
var s = ""
|
||||
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
fun box(): String {
|
||||
var s = ""
|
||||
var foo = "K"
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
fun box(): String {
|
||||
var s = ""
|
||||
var foo = "O"
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// !LANGUAGE: +InlineClasses
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
|
||||
inline class Foo<T>(val x: Any) {
|
||||
fun bar() {}
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
// !LANGUAGE: +InlineClasses
|
||||
// IGNORE_BACKEND: JVM_IR, JS_IR
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
|
||||
inline class SuccessOrFailure<out T>(val value: Any?) {
|
||||
val isFailure: Boolean get() = value is Failure
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
abstract class Base(val fn: () -> String)
|
||||
|
||||
class Host {
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
abstract class Base(val fn: () -> String)
|
||||
|
||||
interface Host {
|
||||
|
||||
Vendored
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
abstract class Base(val fn: () -> String)
|
||||
|
||||
object Test : Base(run { { Test.ok() } }) {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// LANGUAGE_VERSION: 1.2
|
||||
|
||||
fun box(): String {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// WITH_RUNTIME
|
||||
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// WITH_RUNTIME
|
||||
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// WITH_RUNTIME
|
||||
|
||||
const val M = 0.toChar()
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS, NATIVE
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// IGNORE_BACKEND: NATIVE
|
||||
// WITH_REFLECT
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// IGNORE_BACKEND: NATIVE
|
||||
// WITH_REFLECT
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// IGNORE_BACKEND: NATIVE
|
||||
// WITH_REFLECT
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// WITH_RUNTIME
|
||||
|
||||
// FILE: test.kt
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun box(): String {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
val foo: ((String) -> String) = run {
|
||||
{ it }
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
fun test() = foo({ line: String -> line })
|
||||
|
||||
fun <T> foo(x: T): T = TODO()
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
inline fun run(block: () -> Unit) = block()
|
||||
inline fun run2(block: () -> Unit) = block()
|
||||
|
||||
class A {
|
||||
val prop: Int
|
||||
constructor(arg: Boolean) {
|
||||
if (arg) {
|
||||
prop = 1
|
||||
run { return }
|
||||
run2 { return }
|
||||
throw RuntimeException("fail 0")
|
||||
}
|
||||
prop = 2
|
||||
|
||||
Reference in New Issue
Block a user