Introduce support for plugin-defined intrinsics in old JVM backend:

(Old is created first because all intrinsics emit bytecode anyway)

Provide intrinsic for serializer<T>() function so it won't
invoke typeOf() construction and KType->KSerializer conversion
making it fast and truly reflectionless

Add support for recalculating stack size in plugin-defined intrinsics
since it is needed for correct work:
Unify method for recalculating stack size with existing typeOf intrinsic

Add testdata for IR for future intrinsic in IR
This commit is contained in:
Leonid Startsev
2020-04-10 17:57:21 +03:00
committed by Space
parent 842f94443a
commit f9edbd825a
15 changed files with 1114 additions and 98 deletions
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.codegen.extensions
@@ -19,10 +8,17 @@ package org.jetbrains.kotlin.codegen.extensions
import org.jetbrains.kotlin.codegen.ExpressionCodegen
import org.jetbrains.kotlin.codegen.ImplementationBodyCodegen
import org.jetbrains.kotlin.codegen.StackValue
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.extensions.ProjectExtensionDescriptor
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeSystemCommonBackendContext
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
import org.jetbrains.org.objectweb.asm.tree.InsnList
import org.jetbrains.org.objectweb.asm.tree.MethodInsnNode
interface ExpressionCodegenExtension {
companion object : ProjectExtensionDescriptor<ExpressionCodegenExtension>(
@@ -46,6 +42,22 @@ interface ExpressionCodegenExtension {
*/
fun applyFunction(receiver: StackValue, resolvedCall: ResolvedCall<*>, c: Context): StackValue? = null
/**
* Called when inliner encounters [ReifiedTypeInliner.OperationKind.PLUGIN_DEFINED] marker
* to perform extension-specific operation with reified type parameter.
*
* @return Required stack size for method after inlining is performed, 0 if the size is unknown, or -1 if extension ignores this marker
*/
fun applyPluginDefinedReifiedOperationMarker(
insn: MethodInsnNode,
instructions: InsnList,
type: KotlinType,
asmType: Type,
typeMapper: KotlinTypeMapper,
typeSystem: TypeSystemCommonBackendContext,
module: ModuleDescriptor
): Int = -1
fun generateClassSyntheticParts(codegen: ImplementationBodyCodegen) {}
val shouldGenerateClassSyntheticPartsInLightClassesMode: Boolean
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@@ -42,7 +42,7 @@ class PsiInlineCodegen(
) : InlineCodegen<ExpressionCodegen>(
codegen, state, signature, typeParameterMappings, sourceCompiler,
ReifiedTypeInliner(
typeParameterMappings, PsiInlineIntrinsicsSupport(state, reportErrorsOn), codegen.typeSystem,
typeParameterMappings, PsiInlineIntrinsicsSupport(state, reportErrorsOn, codegen.typeSystem), codegen.typeSystem,
state.languageVersionSettings, state.unifiedNullChecks
),
), CallGenerator {
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.codegen.inline
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
@@ -19,15 +20,19 @@ import org.jetbrains.kotlin.resolve.jvm.AsmTypes.*
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm.TYPEOF_NON_REIFIED_TYPE_PARAMETER_WITH_RECURSIVE_BOUND
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm.TYPEOF_SUSPEND_TYPE
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeSystemCommonBackendContext
import org.jetbrains.kotlin.types.model.TypeParameterMarker
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.Type.INT_TYPE
import org.jetbrains.org.objectweb.asm.Type.VOID_TYPE
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
import org.jetbrains.org.objectweb.asm.tree.InsnList
import org.jetbrains.org.objectweb.asm.tree.MethodInsnNode
class PsiInlineIntrinsicsSupport(
override val state: GenerationState,
private val reportErrorsOn: KtElement,
private val typeSystem: TypeSystemCommonBackendContext
) : ReifiedTypeInliner.IntrinsicsSupport<KotlinType> {
override fun putClassInstance(v: InstructionAdapter, type: KotlinType) {
DescriptorAsmUtil.putJavaLangClassInstance(v, state.typeMapper.mapType(type), type, state.typeMapper)
@@ -79,4 +84,15 @@ class PsiInlineIntrinsicsSupport(
override fun reportNonReifiedTypeParameterWithRecursiveBoundUnsupported(typeParameterName: Name) {
state.diagnostics.report(TYPEOF_NON_REIFIED_TYPE_PARAMETER_WITH_RECURSIVE_BOUND.on(reportErrorsOn, typeParameterName.asString()))
}
override fun applyPluginDefinedReifiedOperationMarker(
insn: MethodInsnNode,
instructions: InsnList,
type: KotlinType,
asmType: Type
): Int {
return ExpressionCodegenExtension.getInstances(state.project)
.map { it.applyPluginDefinedReifiedOperationMarker(insn, instructions, type, asmType, state.typeMapper, typeSystem, state.module) }
.maxOrNull() ?: -1
}
}
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.codegen.inline
@@ -57,11 +46,12 @@ class ReifiedTypeInliner<KT : KotlinTypeMarker>(
private val unifiedNullChecks: Boolean,
) {
enum class OperationKind {
NEW_ARRAY, AS, SAFE_AS, IS, JAVA_CLASS, ENUM_REIFIED, TYPE_OF;
NEW_ARRAY, AS, SAFE_AS, IS, JAVA_CLASS, ENUM_REIFIED, TYPE_OF, PLUGIN_DEFINED;
val id: Int get() = ordinal
}
interface IntrinsicsSupport<KT : KotlinTypeMarker> {
val state: GenerationState
@@ -75,6 +65,16 @@ class ReifiedTypeInliner<KT : KotlinTypeMarker>(
fun reportSuspendTypeUnsupported()
fun reportNonReifiedTypeParameterWithRecursiveBoundUnsupported(typeParameterName: Name)
/**
* @return Required stack size for method after inlining is performed, 0 if the size is unknown, or -1 if plugin was not applied
*/
fun applyPluginDefinedReifiedOperationMarker(
insn: MethodInsnNode,
instructions: InsnList,
type: KotlinType,
asmType: Type
): Int = -1
}
companion object {
@@ -181,6 +181,7 @@ class ReifiedTypeInliner<KT : KotlinTypeMarker>(
OperationKind.JAVA_CLASS -> processJavaClass(insn, asmType)
OperationKind.ENUM_REIFIED -> processSpecialEnumFunction(insn, instructions, asmType)
OperationKind.TYPE_OF -> processTypeOf(insn, instructions, type)
OperationKind.PLUGIN_DEFINED -> processPluginDefined(insn, instructions, type, asmType)
}
) {
instructions.remove(insn.previous.previous!!) // PUSH operation ID
@@ -196,6 +197,23 @@ class ReifiedTypeInliner<KT : KotlinTypeMarker>(
}
}
private fun processPluginDefined(
insn: MethodInsnNode,
instructions: InsnList,
type: KT,
asmType: Type
): Boolean {
val applyResult = intrinsicsSupport.applyPluginDefinedReifiedOperationMarker(
insn,
instructions,
intrinsicsSupport.toKotlinType(type),
asmType
)
if (applyResult == -1) return false
maxStackSize = max(maxStackSize, applyResult)
return true
}
private fun reify(argument: ReificationArgument, replacementAsmType: Type, type: KT): Pair<Type, KT> =
with(typeSystem) {
val arrayType = type.arrayOf(argument.arrayDepth)
@@ -269,15 +287,11 @@ class ReifiedTypeInliner<KT : KotlinTypeMarker>(
instructions: InsnList,
type: KT
) = rewriteNextTypeInsn(insn, Opcodes.ACONST_NULL) { stubConstNull: AbstractInsnNode ->
val newMethodNode = MethodNode(Opcodes.API_VERSION, "fake", "()V", null, null)
val mv = wrapWithMaxLocalCalc(newMethodNode)
typeSystem.generateTypeOf(InstructionAdapter(mv), type, intrinsicsSupport)
val newMethodNode = newMethodNodeWithCorrectStackSize {
typeSystem.generateTypeOf(it, type, intrinsicsSupport)
}
// Adding a fake return (and removing it below) to trigger maxStack calculation
mv.visitInsn(Opcodes.RETURN)
mv.visitMaxs(-1, -1)
instructions.insert(insn, newMethodNode.instructions.apply { remove(last) })
instructions.insert(insn, newMethodNode.instructions)
instructions.remove(stubConstNull)
maxStackSize = max(maxStackSize, newMethodNode.maxStack)
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@@ -229,6 +229,19 @@ internal fun isAnonymousClass(internalName: String) =
fun wrapWithMaxLocalCalc(methodNode: MethodNode) =
MaxStackFrameSizeAndLocalsCalculator(Opcodes.API_VERSION, methodNode.access, methodNode.desc, methodNode)
fun newMethodNodeWithCorrectStackSize(block: (InstructionAdapter) -> Unit): MethodNode {
val newMethodNode = MethodNode(Opcodes.API_VERSION, "fake", "()V", null, null)
val mv = wrapWithMaxLocalCalc(newMethodNode)
block(InstructionAdapter(mv))
// Adding a fake return (and removing it below) to trigger maxStack calculation
mv.visitInsn(Opcodes.RETURN)
mv.visitMaxs(-1, -1)
newMethodNode.instructions.apply { remove(last) }
return newMethodNode
}
private fun String.isInteger(radix: Int = 10) = toIntOrNull(radix) != null
internal fun isCapturedFieldName(fieldName: String): Boolean {