[JS IR BE] Support enumValues<T>() and enumValueOf<T>(name)
This commit is contained in:
@@ -124,6 +124,13 @@ class JsIntrinsics(private val irBuiltIns: IrBuiltIns, val context: JsIrBackendC
|
||||
PrimitiveType.DOUBLE to getInternalFunction("isLongArray")
|
||||
)
|
||||
|
||||
|
||||
// Enum
|
||||
|
||||
val enumValueOfIntrinsic = getInternalFunction("enumValueOfIntrinsic")
|
||||
val enumValuesIntrinsic = getInternalFunction("enumValuesIntrinsic")
|
||||
|
||||
|
||||
// Other:
|
||||
|
||||
val jsObjectCreate = defineObjectCreateIntrinsic() // Object.create
|
||||
|
||||
@@ -102,7 +102,6 @@ private fun JsIrBackendContext.performInlining(moduleFragment: IrModuleFragment)
|
||||
}
|
||||
|
||||
private fun JsIrBackendContext.lower(moduleFragment: IrModuleFragment, dependencies: List<IrModuleFragment>) {
|
||||
<<<<<<< HEAD
|
||||
moduleFragment.files.forEach(UnitMaterializationLowering(this)::lower)
|
||||
moduleFragment.files.forEach(EnumClassLowering(this)::runOnFilePostfix)
|
||||
moduleFragment.files.forEach(EnumUsageLowering(this)::lower)
|
||||
|
||||
+2
-5
@@ -148,12 +148,9 @@ class EnumClassTransformer(val context: JsIrBackendContext, private val irClass:
|
||||
}
|
||||
|
||||
private fun List<IrExpression>.toArrayLiteral(arrayType: IrType, elementType: IrType): IrExpression {
|
||||
val startOffset = firstOrNull()?.startOffset ?: UNDEFINED_OFFSET
|
||||
val endOffset = lastOrNull()?.endOffset ?: UNDEFINED_OFFSET
|
||||
val irVararg = IrVarargImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, arrayType, elementType, this)
|
||||
|
||||
val irVararg = IrVarargImpl(startOffset, endOffset, arrayType, elementType, this)
|
||||
|
||||
return IrCallImpl(startOffset, endOffset, arrayType, context.intrinsics.arrayLiteral).apply {
|
||||
return IrCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, arrayType, context.intrinsics.arrayLiteral).apply {
|
||||
putValueArgument(0, irVararg)
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -23,7 +23,8 @@ class CallsLowering(val context: JsIrBackendContext) : FileLoweringPass {
|
||||
EqualityAndComparisonCallsTransformer(context),
|
||||
PrimitiveContainerMemberCallTransformer(context),
|
||||
MethodsOfAnyCallsTransformer(context),
|
||||
ReflectionCallsTransformer(context)
|
||||
ReflectionCallsTransformer(context),
|
||||
EnumIntrinsicsTransformer(context)
|
||||
)
|
||||
|
||||
override fun lower(irFile: IrFile) {
|
||||
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.backend.js.transformers.irToJs.isStatic
|
||||
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.getClass
|
||||
import org.jetbrains.kotlin.ir.types.isString
|
||||
import org.jetbrains.kotlin.ir.util.findDeclaration
|
||||
import org.jetbrains.kotlin.ir.util.isEnumClass
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
|
||||
class EnumIntrinsicsTransformer(private val context: JsIrBackendContext) : CallsTransformer {
|
||||
private fun transformEnumTopLevelIntrinsic(
|
||||
call: IrCall,
|
||||
staticMethodPredicate: (IrSimpleFunction) -> Boolean
|
||||
): IrExpression {
|
||||
val enum = call.getTypeArgument(0)?.getClass() ?: return call
|
||||
if (!enum.isEnumClass) return call
|
||||
val staticMethod = enum.findDeclaration(staticMethodPredicate)
|
||||
if (staticMethod == null || !staticMethod.isStatic)
|
||||
throw IllegalStateException("Enum class should have static method for ${call.symbol.owner.name}")
|
||||
|
||||
return irCall(call, staticMethod.symbol)
|
||||
}
|
||||
|
||||
private fun transformEnumValueOfIntrinsic(call: IrCall) = transformEnumTopLevelIntrinsic(call) {
|
||||
it.name == Name.identifier("valueOf") &&
|
||||
it.valueParameters.count() == 1 &&
|
||||
it.valueParameters[0].type.isString()
|
||||
}
|
||||
|
||||
private fun transformEnumValuesIntrinsic(call: IrCall) = transformEnumTopLevelIntrinsic(call) {
|
||||
it.name == Name.identifier("values") && it.valueParameters.count() == 0
|
||||
}
|
||||
|
||||
override fun transformCall(call: IrCall) = when (call.symbol) {
|
||||
context.intrinsics.enumValueOfIntrinsic -> transformEnumValueOfIntrinsic(call)
|
||||
context.intrinsics.enumValuesIntrinsic -> transformEnumValuesIntrinsic(call)
|
||||
else -> call
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
|
||||
import org.jetbrains.kotlin.backend.common.descriptors.isSuspend
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.Namer
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.js.backend.ast.*
|
||||
@@ -79,7 +80,8 @@ fun translateCallArguments(expression: IrMemberAccessExpression, context: JsGene
|
||||
} else arguments
|
||||
}
|
||||
|
||||
val IrFunction.isStatic: Boolean get() = this.dispatchReceiverParameter == null
|
||||
val IrFunction.isStatic: Boolean
|
||||
get() = parent is IrClass && dispatchReceiverParameter == null
|
||||
|
||||
fun JsStatement.asBlock() = this as? JsBlock ?: JsBlock(this)
|
||||
|
||||
|
||||
+2
-1
@@ -6,6 +6,7 @@
|
||||
package org.jetbrains.kotlin.ir.backend.js.utils
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.isStatic
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrLoop
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||
@@ -176,7 +177,7 @@ class SimpleNameGenerator : NameGenerator {
|
||||
}
|
||||
is IrSimpleFunction -> {
|
||||
|
||||
if (declaration.dispatchReceiverParameter == null && declaration.parent is IrClass) {
|
||||
if (declaration.isStatic) {
|
||||
nameBuilder.append(getNameForDeclaration(declaration.parent as IrDeclaration, context))
|
||||
nameBuilder.append('.')
|
||||
}
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS, NATIVE
|
||||
|
||||
// WITH_REFLECT
|
||||
|
||||
-1
@@ -1,5 +1,4 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||
// IGNORE_BACKEND: JS, NATIVE
|
||||
|
||||
// WITH_REFLECT
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// CHECK_CASES_COUNT: function=crash count=2
|
||||
// CHECK_IF_COUNT: function=crash count=1
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// FILE: 1.kt
|
||||
// WITH_RUNTIME
|
||||
package test
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// FILE: 1.kt
|
||||
// WITH_RUNTIME
|
||||
package test
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// FILE: 1.kt
|
||||
// WITH_RUNTIME
|
||||
package test
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// FILE: 1.kt
|
||||
// WITH_RUNTIME
|
||||
package test
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// FILE: 1.kt
|
||||
// WITH_RUNTIME
|
||||
package test
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// FILE: 1.kt
|
||||
// WITH_RUNTIME
|
||||
package test
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// FILE: 1.kt
|
||||
// WITH_RUNTIME
|
||||
package test
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// FILE: 1.kt
|
||||
// WITH_RUNTIME
|
||||
package test
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// FILE: 1.kt
|
||||
// WITH_RUNTIME
|
||||
package test
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// FILE: 1.kt
|
||||
// WITH_RUNTIME
|
||||
package test
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// FILE: 1.kt
|
||||
// WITH_RUNTIME
|
||||
package test
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// FILE: 1.kt
|
||||
// WITH_RUNTIME
|
||||
package test
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// FILE: 1.kt
|
||||
// WITH_RUNTIME
|
||||
package test
|
||||
|
||||
+5
@@ -1584,6 +1584,11 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
|
||||
runTest("js/js.translator/testData/box/enum/simpleEnum.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("standardFunctions.kt")
|
||||
public void testStandardFunctions() throws Exception {
|
||||
runTest("js/js.translator/testData/box/enum/standardFunctions.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("standardMethods.kt")
|
||||
public void testStandardMethods() throws Exception {
|
||||
runTest("js/js.translator/testData/box/enum/standardMethods.kt");
|
||||
|
||||
+5
@@ -1584,6 +1584,11 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest {
|
||||
runTest("js/js.translator/testData/box/enum/simpleEnum.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("standardFunctions.kt")
|
||||
public void testStandardFunctions() throws Exception {
|
||||
runTest("js/js.translator/testData/box/enum/standardFunctions.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("standardMethods.kt")
|
||||
public void testStandardMethods() throws Exception {
|
||||
runTest("js/js.translator/testData/box/enum/standardMethods.kt");
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
// EXPECTED_REACHABLE_NODES: 1555
|
||||
package foo
|
||||
|
||||
enum class EmptyEnum
|
||||
|
||||
enum class A {
|
||||
a() {
|
||||
},
|
||||
b(),
|
||||
c
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
if (enumValues<EmptyEnum>().size != 0) return "enumValues<EmptyEnum>().size != 0"
|
||||
if (enumValues<A>().asList() != listOf(A.a, A.b, A.c)) return "Wrong enumValues<A>(): " + enumValues<A>().toString()
|
||||
if (enumValueOf<A>("b") != A.b) return "enumValueOf<A>('b') != A.b"
|
||||
return "OK"
|
||||
}
|
||||
@@ -5,7 +5,9 @@
|
||||
|
||||
package kotlin
|
||||
|
||||
public class Enum<E : Enum<E>>(val name: String, val ordinal: Int) : Comparable<E> {
|
||||
import kotlin.js.*
|
||||
|
||||
abstract class Enum<E : Enum<E>>(val name: String, val ordinal: Int) : Comparable<E> {
|
||||
|
||||
override fun compareTo(other: E) = ordinal.compareTo(other.ordinal)
|
||||
|
||||
@@ -16,4 +18,14 @@ public class Enum<E : Enum<E>>(val name: String, val ordinal: Int) : Comparable<
|
||||
override fun toString() = name
|
||||
|
||||
companion object
|
||||
}
|
||||
}
|
||||
|
||||
// Use non-inline calls to enumValuesIntrinsic and enumValueOfIntrinsic calls in order
|
||||
// for compiler to replace them with method calls of concrete enum classes after inlining.
|
||||
// TODO: Figure out better solution (Inline hacks? Dynamic calls to stable mangled names?)
|
||||
|
||||
@SinceKotlin("1.1")
|
||||
public inline fun <reified T : Enum<T>> enumValues(): Array<T> = enumValuesIntrinsic<T>()
|
||||
|
||||
@SinceKotlin("1.1")
|
||||
public inline fun <reified T : Enum<T>> enumValueOf(name: String): T = enumValueOfIntrinsic<T>(name)
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* 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 kotlin.js
|
||||
|
||||
@PublishedApi
|
||||
internal fun <T : Enum<T>> enumValuesIntrinsic(): Array<T> =
|
||||
throw IllegalStateException("Should be replaced by compiler")
|
||||
|
||||
@PublishedApi
|
||||
internal fun <T : Enum<T>> enumValueOfIntrinsic(name: String): T =
|
||||
throw IllegalStateException("Should be replaced by compiler")
|
||||
Reference in New Issue
Block a user