Generate bytecode for array intrinsics manually
#KT-28285 Fixed
This commit is contained in:
@@ -4834,12 +4834,7 @@ The "returned" value of try expression with no finally is either the last expres
|
||||
if (codegen != null) {
|
||||
codegen.consumeReifiedOperationMarker(typeParameterDescriptor);
|
||||
}
|
||||
v.iconst(operationKind.getId());
|
||||
v.visitLdcInsn(typeParameterAndReificationArgument.getSecond().asString());
|
||||
v.invokestatic(
|
||||
IntrinsicMethods.INTRINSICS_CLASS_NAME, ReifiedTypeInliner.REIFIED_OPERATION_MARKER_METHOD_NAME,
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE, Type.getType(String.class)), false
|
||||
);
|
||||
ReifiedTypeInliner.putReifiedOperationMarker(operationKind, typeParameterAndReificationArgument.getSecond(), v);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,13 +7,12 @@ package org.jetbrains.kotlin.codegen.inline
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.util.ArrayUtil
|
||||
import org.jetbrains.kotlin.builtins.BuiltInsPackageFragment
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil.getMethodAsmFlags
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil.isPrimitive
|
||||
import org.jetbrains.kotlin.codegen.context.ClosureContext
|
||||
import org.jetbrains.kotlin.codegen.intrinsics.bytecode
|
||||
import org.jetbrains.kotlin.codegen.intrinsics.classId
|
||||
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicArrayConstructors
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
||||
import org.jetbrains.kotlin.config.isReleaseCoroutines
|
||||
@@ -520,9 +519,13 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
|
||||
asmMethod: Method
|
||||
): SMAPAndMethodNode? {
|
||||
if (isBuiltInArrayIntrinsic(callableDescriptor)) {
|
||||
val classId = classId
|
||||
val bytes = state.inlineCache.classBytes.getOrPut(classId) { bytecode }
|
||||
return getMethodNode(bytes, asmMethod.name, asmMethod.descriptor, AsmUtil.asmTypeByClassId(classId))
|
||||
val body = when {
|
||||
callableDescriptor is FictitiousArrayConstructor -> IntrinsicArrayConstructors.generateArrayConstructorBody(asmMethod)
|
||||
callableDescriptor.name.asString() == "emptyArray" -> IntrinsicArrayConstructors.generateEmptyArrayBody(asmMethod)
|
||||
callableDescriptor.name.asString() == "arrayOf" -> IntrinsicArrayConstructors.generateArrayOfBody(asmMethod)
|
||||
else -> throw UnsupportedOperationException("Not an array intrinsic: $callableDescriptor")
|
||||
}
|
||||
return SMAPAndMethodNode(body, SMAP(listOf(FileMapping.SKIP)))
|
||||
}
|
||||
|
||||
assert(callableDescriptor is DeserializedCallableMemberDescriptor) { "Not a deserialized function or proper: " + callableDescriptor }
|
||||
@@ -552,7 +555,9 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
|
||||
private fun isBuiltInArrayIntrinsic(callableDescriptor: CallableMemberDescriptor): Boolean {
|
||||
if (callableDescriptor is FictitiousArrayConstructor) return true
|
||||
val name = callableDescriptor.name.asString()
|
||||
return (name == "arrayOf" || name == "emptyArray") && callableDescriptor.containingDeclaration is BuiltInsPackageFragment
|
||||
return (name == "arrayOf" || name == "emptyArray") && callableDescriptor.containingDeclaration.let { container ->
|
||||
container is PackageFragmentDescriptor && container.fqName == KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME
|
||||
}
|
||||
}
|
||||
|
||||
/*descriptor is null for captured vars*/
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.codegen.generateIsCheck
|
||||
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.intConstant
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
@@ -96,6 +97,16 @@ class ReifiedTypeInliner(
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE), false
|
||||
)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun putReifiedOperationMarker(operationKind: OperationKind, argument: ReificationArgument, v: InstructionAdapter) {
|
||||
v.iconst(operationKind.id)
|
||||
v.visitLdcInsn(argument.asString())
|
||||
v.invokestatic(
|
||||
IntrinsicMethods.INTRINSICS_CLASS_NAME, REIFIED_OPERATION_MARKER_METHOD_NAME,
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE, AsmTypes.JAVA_STRING_TYPE), false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private var maxStackSize = 0
|
||||
|
||||
@@ -18,7 +18,6 @@ import org.jetbrains.kotlin.codegen.context.CodegenContextUtil
|
||||
import org.jetbrains.kotlin.codegen.context.InlineLambdaContext
|
||||
import org.jetbrains.kotlin.codegen.context.MethodContext
|
||||
import org.jetbrains.kotlin.codegen.coroutines.unwrapInitialDescriptorForSuspendFunction
|
||||
import org.jetbrains.kotlin.codegen.intrinsics.classId
|
||||
import org.jetbrains.kotlin.codegen.optimization.common.intConstant
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
||||
@@ -75,7 +74,6 @@ private const val INLINE_MARKER_RETURNS_UNIT = 2
|
||||
private const val INLINE_MARKER_FAKE_CONTINUATION = 3
|
||||
private const val INLINE_MARKER_BEFORE_FAKE_CONTINUATION_CONSTRUCTOR_CALL = 4
|
||||
private const val INLINE_MARKER_AFTER_FAKE_CONTINUATION_CONSTRUCTOR_CALL = 5
|
||||
private val INTRINSIC_ARRAY_CONSTRUCTOR_TYPE = AsmUtil.asmTypeByClassId(classId)
|
||||
|
||||
internal fun getMethodNode(
|
||||
classData: ByteArray,
|
||||
@@ -135,11 +133,6 @@ internal fun getMethodNode(
|
||||
return null
|
||||
}
|
||||
|
||||
if (INTRINSIC_ARRAY_CONSTRUCTOR_TYPE == classType) {
|
||||
// Don't load source map for intrinsic array constructors
|
||||
debugInfo[0] = null
|
||||
}
|
||||
|
||||
val smap = SMAPParser.parseOrCreateDefault(debugInfo[1], debugInfo[0], classType.internalName, lines[0], lines[1])
|
||||
return SMAPAndMethodNode(node!!, smap)
|
||||
}
|
||||
|
||||
+102
-88
@@ -14,98 +14,112 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
@file:Suppress("unused", "UNCHECKED_CAST")
|
||||
|
||||
package org.jetbrains.kotlin.codegen.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.codegen.inline.ReificationArgument
|
||||
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
import org.jetbrains.org.objectweb.asm.commons.Method
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
|
||||
// Bodies of methods in this file are used in InlineCodegen to inline array constructors. It is loaded via reflection at runtime.
|
||||
// TODO: generate the bytecode manually instead, do not depend on the previous compiler working correctly here
|
||||
internal object IntrinsicArrayConstructors {
|
||||
fun generateArrayConstructorBody(method: Method): MethodNode {
|
||||
val node = MethodNode(
|
||||
Opcodes.ASM6, Opcodes.ACC_PUBLIC or Opcodes.ACC_STATIC or Opcodes.ACC_FINAL, method.name, method.descriptor, null, null
|
||||
)
|
||||
|
||||
internal val classId: ClassId =
|
||||
ClassId.topLevel(FqName("org.jetbrains.kotlin.codegen.intrinsics.IntrinsicArrayConstructorsKt"))
|
||||
val arrayType = method.returnType
|
||||
val elementType = AsmUtil.correctElementType(arrayType)
|
||||
|
||||
internal val bytecode: ByteArray by lazy {
|
||||
val stream = object {}::class.java.classLoader.getResourceAsStream("${classId.asString()}.class")
|
||||
stream.readBytes().apply {
|
||||
stream.close()
|
||||
val size = StackValue.local(0, Type.INT_TYPE)
|
||||
val lambda = StackValue.local(1, AsmTypes.FUNCTION1)
|
||||
val loopIndex = StackValue.local(2, Type.INT_TYPE)
|
||||
val array = StackValue.local(3, arrayType)
|
||||
|
||||
/*
|
||||
inline fun <reified T> Array(size: Int, init: (Int) -> T): Array<T> {
|
||||
val result = arrayOfNulls<T>(size)
|
||||
for (i in 0 until size) {
|
||||
result[i] = init(i)
|
||||
}
|
||||
return result as Array<T>
|
||||
}
|
||||
*/
|
||||
|
||||
val iv = InstructionAdapter(node)
|
||||
size.put(iv)
|
||||
if (elementType.sort == Type.OBJECT) {
|
||||
ReifiedTypeInliner.putReifiedOperationMarker(ReifiedTypeInliner.OperationKind.NEW_ARRAY, ReificationArgument("T", true, 0), iv)
|
||||
}
|
||||
iv.newarray(elementType)
|
||||
array.store(StackValue.onStack(arrayType), iv)
|
||||
loopIndex.store(StackValue.constant(0), iv)
|
||||
val begin = Label()
|
||||
iv.visitLabel(begin)
|
||||
loopIndex.put(iv)
|
||||
size.put(iv)
|
||||
val end = Label()
|
||||
iv.ificmpge(end)
|
||||
array.put(iv)
|
||||
loopIndex.put(iv)
|
||||
lambda.put(iv)
|
||||
loopIndex.put(AsmUtil.boxType(Type.INT_TYPE), iv)
|
||||
iv.invokeinterface(
|
||||
AsmTypes.FUNCTION1.internalName, OperatorNameConventions.INVOKE.asString(),
|
||||
Type.getMethodDescriptor(AsmTypes.OBJECT_TYPE, AsmTypes.OBJECT_TYPE)
|
||||
)
|
||||
StackValue.coerce(AsmTypes.OBJECT_TYPE, elementType, iv)
|
||||
iv.astore(elementType)
|
||||
iv.iinc(loopIndex.index, 1)
|
||||
iv.goTo(begin)
|
||||
iv.visitLabel(end)
|
||||
array.put(iv)
|
||||
iv.areturn(arrayType)
|
||||
iv.visitMaxs(5, 4)
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
fun generateEmptyArrayBody(method: Method): MethodNode {
|
||||
val node = MethodNode(
|
||||
Opcodes.ASM6, Opcodes.ACC_PUBLIC or Opcodes.ACC_STATIC or Opcodes.ACC_FINAL, method.name, method.descriptor, null, null
|
||||
)
|
||||
|
||||
/*
|
||||
inline fun <reified T> emptyArray(): Array<T> = arrayOfNulls<T>(0) as Array<T>
|
||||
*/
|
||||
|
||||
val iv = InstructionAdapter(node)
|
||||
iv.iconst(0)
|
||||
ReifiedTypeInliner.putReifiedOperationMarker(ReifiedTypeInliner.OperationKind.NEW_ARRAY, ReificationArgument("T", true, 0), iv)
|
||||
iv.newarray(AsmTypes.OBJECT_TYPE)
|
||||
iv.areturn(AsmTypes.OBJECT_TYPE)
|
||||
iv.visitMaxs(3, 1)
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
fun generateArrayOfBody(method: Method): MethodNode {
|
||||
val node = MethodNode(
|
||||
Opcodes.ASM6, Opcodes.ACC_PUBLIC or Opcodes.ACC_STATIC or Opcodes.ACC_FINAL, method.name, method.descriptor, null, null
|
||||
)
|
||||
|
||||
/*
|
||||
inline fun <reified T> arrayOf(vararg elements: T): Array<T> = elements as Array<T>
|
||||
*/
|
||||
|
||||
val iv = InstructionAdapter(node)
|
||||
iv.load(0, AsmTypes.OBJECT_TYPE)
|
||||
iv.areturn(AsmTypes.OBJECT_TYPE)
|
||||
iv.visitMaxs(1, 1)
|
||||
|
||||
return node
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun <reified T> emptyArray(): Array<T> = arrayOfNulls<T>(0) as Array<T>
|
||||
|
||||
private inline fun <reified T> arrayOf(vararg elements: T): Array<T> = elements as Array<T>
|
||||
|
||||
private inline fun <reified T> Array(size: Int, init: (Int) -> T): Array<T> {
|
||||
val result = arrayOfNulls<T>(size)
|
||||
for (i in result.indices) {
|
||||
result[i] = init(i)
|
||||
}
|
||||
return result as Array<T>
|
||||
}
|
||||
|
||||
private inline fun DoubleArray(size: Int, init: (Int) -> Double): DoubleArray {
|
||||
val result = DoubleArray(size)
|
||||
for (i in result.indices) {
|
||||
result[i] = init(i)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private inline fun FloatArray(size: Int, init: (Int) -> Float): FloatArray {
|
||||
val result = FloatArray(size)
|
||||
for (i in result.indices) {
|
||||
result[i] = init(i)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private inline fun LongArray(size: Int, init: (Int) -> Long): LongArray {
|
||||
val result = LongArray(size)
|
||||
for (i in result.indices) {
|
||||
result[i] = init(i)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private inline fun IntArray(size: Int, init: (Int) -> Int): IntArray {
|
||||
val result = IntArray(size)
|
||||
for (i in result.indices) {
|
||||
result[i] = init(i)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private inline fun CharArray(size: Int, init: (Int) -> Char): CharArray {
|
||||
val result = CharArray(size)
|
||||
for (i in result.indices) {
|
||||
result[i] = init(i)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private inline fun ShortArray(size: Int, init: (Int) -> Short): ShortArray {
|
||||
val result = ShortArray(size)
|
||||
for (i in result.indices) {
|
||||
result[i] = init(i)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private inline fun ByteArray(size: Int, init: (Int) -> Byte): ByteArray {
|
||||
val result = ByteArray(size)
|
||||
for (i in result.indices) {
|
||||
result[i] = init(i)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private inline fun BooleanArray(size: Int, init: (Int) -> Boolean): BooleanArray {
|
||||
val result = BooleanArray(size)
|
||||
for (i in result.indices) {
|
||||
result[i] = init(i)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -115,8 +115,6 @@ messages/**)
|
||||
|
||||
-keep class org.jetbrains.kotlin.container.** { *; }
|
||||
|
||||
-keep class org.jetbrains.kotlin.codegen.intrinsics.IntrinsicArrayConstructorsKt { *; }
|
||||
|
||||
-keep class org.jetbrains.org.objectweb.asm.Opcodes { *; }
|
||||
|
||||
-keep class org.jetbrains.kotlin.codegen.extensions.** {
|
||||
|
||||
@@ -100,8 +100,6 @@ messages/**)
|
||||
|
||||
-keep class org.jetbrains.kotlin.container.** { *; }
|
||||
|
||||
-keep class org.jetbrains.kotlin.codegen.intrinsics.IntrinsicArrayConstructorsKt { *; }
|
||||
|
||||
-keep class org.jetbrains.org.objectweb.asm.Opcodes { *; }
|
||||
|
||||
-keep class org.jetbrains.kotlin.codegen.extensions.** {
|
||||
|
||||
@@ -116,8 +116,6 @@ messages/**)
|
||||
|
||||
-keep class org.jetbrains.kotlin.container.** { *; }
|
||||
|
||||
-keep class org.jetbrains.kotlin.codegen.intrinsics.IntrinsicArrayConstructorsKt { *; }
|
||||
|
||||
-keep class org.jetbrains.org.objectweb.asm.Opcodes { *; }
|
||||
|
||||
-keep class org.jetbrains.kotlin.codegen.extensions.** {
|
||||
|
||||
@@ -101,8 +101,6 @@ messages/**)
|
||||
|
||||
-keep class org.jetbrains.kotlin.container.** { *; }
|
||||
|
||||
-keep class org.jetbrains.kotlin.codegen.intrinsics.IntrinsicArrayConstructorsKt { *; }
|
||||
|
||||
-keep class org.jetbrains.org.objectweb.asm.Opcodes { *; }
|
||||
|
||||
-keep class org.jetbrains.kotlin.codegen.extensions.** {
|
||||
|
||||
@@ -116,8 +116,6 @@ messages/**)
|
||||
|
||||
-keep class org.jetbrains.kotlin.container.** { *; }
|
||||
|
||||
-keep class org.jetbrains.kotlin.codegen.intrinsics.IntrinsicArrayConstructorsKt { *; }
|
||||
|
||||
-keep class org.jetbrains.org.objectweb.asm.Opcodes { *; }
|
||||
|
||||
-keep class org.jetbrains.kotlin.codegen.extensions.** {
|
||||
|
||||
@@ -38,6 +38,8 @@ public class AsmTypes {
|
||||
|
||||
public static final Type RESULT_FAILURE = Type.getObjectType("kotlin/Result$Failure");
|
||||
|
||||
public static final Type FUNCTION1 = Type.getObjectType("kotlin/jvm/functions/Function1");
|
||||
|
||||
public static final Type[] PROPERTY_REFERENCE_IMPL = {
|
||||
Type.getObjectType("kotlin/jvm/internal/PropertyReference0Impl"),
|
||||
Type.getObjectType("kotlin/jvm/internal/PropertyReference1Impl"),
|
||||
|
||||
Reference in New Issue
Block a user