[JS IR BE] Support enumValues<T>() and enumValueOf<T>(name)

This commit is contained in:
Svyatoslav Kuzmich
2018-10-01 15:51:52 +03:00
parent bdc3daf972
commit 7074909230
28 changed files with 122 additions and 27 deletions
@@ -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)
@@ -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)
}
}
@@ -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) {
@@ -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
}
}
@@ -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)
@@ -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,4 +1,3 @@
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS, NATIVE
// WITH_REFLECT
@@ -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
View File
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// FILE: 1.kt
// WITH_RUNTIME
package test
-1
View File
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// FILE: 1.kt
// WITH_RUNTIME
package test
-1
View File
@@ -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
View File
@@ -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
@@ -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");
@@ -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");
+18
View File
@@ -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"
}
+14 -2
View File
@@ -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")