diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/InterfaceImplBodyCodegen.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/InterfaceImplBodyCodegen.kt index d9624ffd991..84d8294afee 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/InterfaceImplBodyCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/InterfaceImplBodyCodegen.kt @@ -21,6 +21,7 @@ import org.jetbrains.kotlin.backend.common.bridges.findImplementationFromInterfa import org.jetbrains.kotlin.backend.common.bridges.firstSuperMethodFromKotlin import org.jetbrains.kotlin.codegen.context.ClassContext import org.jetbrains.kotlin.codegen.state.GenerationState +import org.jetbrains.kotlin.codegen.state.JvmMethodExceptionTypes import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor import org.jetbrains.kotlin.psi.KtPureClassOrObject @@ -167,7 +168,7 @@ class InterfaceImplBodyCodegen( name: String, desc: String, signature: String?, - exceptions: Array? + exceptions: JvmMethodExceptionTypes ): MethodVisitor { if (shouldCount) { isAnythingGenerated = true diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/InterfaceImplBodyCodegen.kt.202 b/compiler/backend/src/org/jetbrains/kotlin/codegen/InterfaceImplBodyCodegen.kt.202 deleted file mode 100644 index 8ba3fd56ffe..00000000000 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/InterfaceImplBodyCodegen.kt.202 +++ /dev/null @@ -1,182 +0,0 @@ -/* - * 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. - */ - -package org.jetbrains.kotlin.codegen - -import com.intellij.util.ArrayUtil -import org.jetbrains.kotlin.backend.common.bridges.findImplementationFromInterface -import org.jetbrains.kotlin.backend.common.bridges.firstSuperMethodFromKotlin -import org.jetbrains.kotlin.codegen.context.ClassContext -import org.jetbrains.kotlin.codegen.state.GenerationState -import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor -import org.jetbrains.kotlin.psi.KtPureClassOrObject -import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils -import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin -import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind -import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature -import org.jetbrains.org.objectweb.asm.MethodVisitor -import org.jetbrains.org.objectweb.asm.Opcodes.* - -class InterfaceImplBodyCodegen( - aClass: KtPureClassOrObject, - context: ClassContext, - v: ClassBuilder, - state: GenerationState, - parentCodegen: MemberCodegen<*>? -) : ClassBodyCodegen(aClass, context, InterfaceImplBodyCodegen.InterfaceImplClassBuilder(v), state, parentCodegen) { - private var isAnythingGenerated: Boolean = false - get() = (v as InterfaceImplClassBuilder).isAnythingGenerated - - private val defaultImplType = typeMapper.mapDefaultImpls(descriptor) - - override fun generateDeclaration() { - val codegenFlags = ACC_PUBLIC or ACC_FINAL or ACC_SUPER - val flags = if (state.classBuilderMode == ClassBuilderMode.LIGHT_CLASSES) codegenFlags or ACC_STATIC else codegenFlags - v.defineClass( - myClass.psiOrParent, state.classFileVersion, flags, - defaultImplType.internalName, - null, "java/lang/Object", ArrayUtil.EMPTY_STRING_ARRAY - ) - v.visitSource(myClass.containingKtFile.name, null) - } - - override fun classForInnerClassRecord(): ClassDescriptor? { - if (!isAnythingGenerated) return null - return InnerClassConsumer.classForInnerClassRecord(descriptor, true) - } - - override fun generateSyntheticPartsAfterBody() { - for (memberDescriptor in descriptor.defaultType.memberScope.getContributedDescriptors()) { - if (memberDescriptor !is CallableMemberDescriptor) continue - - if (memberDescriptor.kind.isReal) continue - if (memberDescriptor.visibility == Visibilities.INVISIBLE_FAKE) continue - if (memberDescriptor.modality == Modality.ABSTRACT) continue - - val implementation = findImplementationFromInterface(memberDescriptor) ?: continue - - // If implementation is a default interface method (JVM 8 only) - if (implementation.isDefinitelyNotDefaultImplsMethod()) continue - - if (memberDescriptor is FunctionDescriptor) { - generateDelegationToSuperDefaultImpls(memberDescriptor, implementation as FunctionDescriptor) - } - else if (memberDescriptor is PropertyDescriptor) { - implementation as PropertyDescriptor - val getter = memberDescriptor.getter - val implGetter = implementation.getter - if (getter != null && implGetter != null) { - generateDelegationToSuperDefaultImpls(getter, implGetter) - } - val setter = memberDescriptor.setter - val implSetter = implementation.setter - if (setter != null && implSetter != null) { - generateDelegationToSuperDefaultImpls(setter, implSetter) - } - } - } - - generateSyntheticAccessors() - } - - private fun generateDelegationToSuperDefaultImpls(descriptor: FunctionDescriptor, implementation: FunctionDescriptor) { - val delegateTo = firstSuperMethodFromKotlin(descriptor, implementation) as FunctionDescriptor? ?: return - - // We can't call super methods from Java 1.8 interfaces because that requires INVOKESPECIAL which is forbidden from TImpl class - if (delegateTo is JavaMethodDescriptor) return - - functionCodegen.generateMethod( - JvmDeclarationOrigin( - JvmDeclarationOriginKind.DEFAULT_IMPL_DELEGATION_TO_SUPERINTERFACE_DEFAULT_IMPL, - DescriptorToSourceUtils.descriptorToDeclaration(descriptor), descriptor - ), - descriptor, - object : FunctionGenerationStrategy.CodegenBased(state) { - override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) { - val iv = codegen.v - - val method = typeMapper.mapToCallableMethod(delegateTo, true) - val myParameters = signature.valueParameters - val calleeParameters = method.getValueParameters() - - if (myParameters.size != calleeParameters.size) { - throw AssertionError( - "Method from super interface has a different signature.\n" + - "This method:\n%s\n%s\n%s\nSuper method:\n%s\n%s\n%s".format( - descriptor, signature, myParameters, delegateTo, method, calleeParameters - ) - ) - } - - var k = 0 - val it = calleeParameters.iterator() - for (parameter in myParameters) { - val type = parameter.asmType - StackValue.local(k, type).put(it.next().asmType, iv) - k += type.size - } - - method.genInvokeInstruction(iv) - StackValue.coerce(method.returnType, signature.returnType, iv) - iv.areturn(signature.returnType) - } - }) - } - - override fun generateKotlinMetadataAnnotation() { - (v as InterfaceImplClassBuilder).stopCounting() - - writeSyntheticClassMetadata(v, state) - } - - override fun done() { - super.done() - if (!isAnythingGenerated) { - state.factory.removeClasses(setOf(defaultImplType.internalName)) - } - } - - private class InterfaceImplClassBuilder(private val v: ClassBuilder) : DelegatingClassBuilder() { - private var shouldCount: Boolean = true - var isAnythingGenerated: Boolean = false - private set - - fun stopCounting() { - shouldCount = false - } - - override fun getDelegate() = v - - override fun newMethod( - origin: JvmDeclarationOrigin, - access: Int, - name: String, - desc: String, - signature: String?, - exceptions: Array? - ): MethodVisitor { - if (shouldCount) { - isAnythingGenerated = true - } - return super.newMethod(origin, access, name, desc, signature, exceptions) - } - } - - override fun generateSyntheticPartsBeforeBody() { - generatePropertyMetadataArrayFieldIfNeeded(defaultImplType) - } -} diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/OriginCollectingClassBuilderFactory.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/OriginCollectingClassBuilderFactory.kt index 2c521285b27..d9b3869893f 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/OriginCollectingClassBuilderFactory.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/OriginCollectingClassBuilderFactory.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.codegen +import org.jetbrains.kotlin.codegen.state.JvmMethodExceptionTypes import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin import org.jetbrains.org.objectweb.asm.* import org.jetbrains.org.objectweb.asm.tree.* @@ -42,7 +43,7 @@ class OriginCollectingClassBuilderFactory(private val builderMode: ClassBuilderM name: String, desc: String, signature: String?, - exceptions: Array? + exceptions: JvmMethodExceptionTypes ): MethodVisitor { val methodNode = super.newMethod(origin, access, name, desc, signature, exceptions) as MethodNode origins[methodNode] = origin diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/OriginCollectingClassBuilderFactory.kt.202 b/compiler/backend/src/org/jetbrains/kotlin/codegen/OriginCollectingClassBuilderFactory.kt.202 deleted file mode 100644 index b8254176580..00000000000 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/OriginCollectingClassBuilderFactory.kt.202 +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2010-2018 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 - -import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin -import org.jetbrains.org.objectweb.asm.* -import org.jetbrains.org.objectweb.asm.tree.* - -class OriginCollectingClassBuilderFactory(private val builderMode: ClassBuilderMode) : ClassBuilderFactory { - val compiledClasses = mutableListOf() - val origins = mutableMapOf() - - override fun getClassBuilderMode(): ClassBuilderMode = builderMode - - override fun newClassBuilder(origin: JvmDeclarationOrigin): AbstractClassBuilder.Concrete { - val classNode = ClassNode() - compiledClasses += classNode - origins[classNode] = origin - return OriginCollectingClassBuilder(classNode) - } - - private inner class OriginCollectingClassBuilder(val classNode: ClassNode) : AbstractClassBuilder.Concrete(classNode) { - override fun newField( - origin: JvmDeclarationOrigin, - access: Int, - name: String, - desc: String, - signature: String?, - value: Any? - ): FieldVisitor { - val fieldNode = super.newField(origin, access, name, desc, signature, value) as FieldNode - origins[fieldNode] = origin - return fieldNode - } - - override fun newMethod( - origin: JvmDeclarationOrigin, - access: Int, - name: String, - desc: String, - signature: String?, - exceptions: Array? - ): MethodVisitor { - val methodNode = super.newMethod(origin, access, name, desc, signature, exceptions) as MethodNode - origins[methodNode] = origin - - // ASM doesn't read information about local variables for the `abstract` methods so we need to get it manually - if ((access and Opcodes.ACC_ABSTRACT) != 0 && methodNode.localVariables == null) { - methodNode.localVariables = mutableListOf() - } - - return methodNode - } - } - - override fun asBytes(builder: ClassBuilder): ByteArray { - val classWriter = ClassWriter(0) - (builder as OriginCollectingClassBuilder).classNode.accept(classWriter) - return classWriter.toByteArray() - } - - override fun asText(builder: ClassBuilder) = throw UnsupportedOperationException() - - override fun close() {} -} \ No newline at end of file diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/SignatureCollectingClassBuilderFactory.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/SignatureCollectingClassBuilderFactory.kt index a4e54e9eddf..e3f4ca845bd 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/SignatureCollectingClassBuilderFactory.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/SignatureCollectingClassBuilderFactory.kt @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.codegen import com.intellij.psi.PsiElement import com.intellij.util.containers.LinkedMultiMap import com.intellij.util.containers.MultiMap +import org.jetbrains.kotlin.codegen.state.JvmMethodExceptionTypes import org.jetbrains.kotlin.resolve.jvm.diagnostics.ConflictingJvmDeclarationsData import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin import org.jetbrains.kotlin.resolve.jvm.diagnostics.MemberKind @@ -63,7 +64,7 @@ abstract class SignatureCollectingClassBuilderFactory( return super.newField(origin, access, name, desc, signature, value) } - override fun newMethod(origin: JvmDeclarationOrigin, access: Int, name: String, desc: String, signature: String?, exceptions: Array?): MethodVisitor { + override fun newMethod(origin: JvmDeclarationOrigin, access: Int, name: String, desc: String, signature: String?, exceptions: JvmMethodExceptionTypes): MethodVisitor { signatures.putValue(RawSignature(name, desc, MemberKind.METHOD), origin) if (!shouldGenerate(origin)) { return AbstractClassBuilder.EMPTY_METHOD_VISITOR diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/SignatureCollectingClassBuilderFactory.kt.202 b/compiler/backend/src/org/jetbrains/kotlin/codegen/SignatureCollectingClassBuilderFactory.kt.202 deleted file mode 100644 index 96cb43312a0..00000000000 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/SignatureCollectingClassBuilderFactory.kt.202 +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2010-2016 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. - */ - -package org.jetbrains.kotlin.codegen - -import com.intellij.psi.PsiElement -import com.intellij.util.containers.LinkedMultiMap -import com.intellij.util.containers.MultiMap -import org.jetbrains.kotlin.resolve.jvm.diagnostics.ConflictingJvmDeclarationsData -import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin -import org.jetbrains.kotlin.resolve.jvm.diagnostics.MemberKind -import org.jetbrains.kotlin.resolve.jvm.diagnostics.RawSignature -import org.jetbrains.org.objectweb.asm.FieldVisitor -import org.jetbrains.org.objectweb.asm.MethodVisitor - -abstract class SignatureCollectingClassBuilderFactory( - delegate: ClassBuilderFactory, val shouldGenerate: (JvmDeclarationOrigin) -> Boolean -) : DelegatingClassBuilderFactory(delegate) { - - protected abstract fun handleClashingSignatures(data: ConflictingJvmDeclarationsData) - protected abstract fun onClassDone(classOrigin: JvmDeclarationOrigin, - classInternalName: String, - signatures: MultiMap) - - override fun newClassBuilder(origin: JvmDeclarationOrigin): DelegatingClassBuilder { - return SignatureCollectingClassBuilder(origin, delegate.newClassBuilder(origin)) - } - - private inner class SignatureCollectingClassBuilder( - private val classCreatedFor: JvmDeclarationOrigin, - internal val _delegate: ClassBuilder - ) : DelegatingClassBuilder() { - - override fun getDelegate() = _delegate - - private lateinit var classInternalName: String - - private val signatures = LinkedMultiMap() - - override fun defineClass(origin: PsiElement?, version: Int, access: Int, name: String, signature: String?, superName: String, interfaces: Array) { - classInternalName = name - super.defineClass(origin, version, access, name, signature, superName, interfaces) - } - - override fun newField(origin: JvmDeclarationOrigin, access: Int, name: String, desc: String, signature: String?, value: Any?): FieldVisitor { - signatures.putValue(RawSignature(name, desc, MemberKind.FIELD), origin) - if (!shouldGenerate(origin)) { - return AbstractClassBuilder.EMPTY_FIELD_VISITOR - } - return super.newField(origin, access, name, desc, signature, value) - } - - override fun newMethod(origin: JvmDeclarationOrigin, access: Int, name: String, desc: String, signature: String?, exceptions: Array?): MethodVisitor { - signatures.putValue(RawSignature(name, desc, MemberKind.METHOD), origin) - if (!shouldGenerate(origin)) { - return AbstractClassBuilder.EMPTY_METHOD_VISITOR - } - return super.newMethod(origin, access, name, desc, signature, exceptions) - } - - override fun done() { - for ((signature, elementsAndDescriptors) in signatures.entrySet()) { - if (elementsAndDescriptors.size == 1) continue // no clash - handleClashingSignatures(ConflictingJvmDeclarationsData( - classInternalName, - classCreatedFor, - signature, - elementsAndDescriptors - )) - } - onClassDone(classCreatedFor, classInternalName, signatures) - super.done() - } - - } -} diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt index 52c38d6afd8..09768e3c4c4 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt @@ -905,7 +905,7 @@ private class MethodNodeExaminer( val methodNode: MethodNode, disableTailCallOptimizationForFunctionReturningUnit: Boolean ) { - private val sourceFrames: Array?> = + private val sourceFrames: SourceFrames = MethodTransformer.analyze(containingClassInternalName, methodNode, IgnoringCopyOperationSourceInterpreter()) private val controlFlowGraph = ControlFlowGraph.build(methodNode) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt.202 b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt.202 deleted file mode 100644 index 53d3e45a0e5..00000000000 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt.202 +++ /dev/null @@ -1,1236 +0,0 @@ -/* - * Copyright 2010-2019 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.coroutines - -import org.jetbrains.kotlin.backend.common.CodegenUtil -import org.jetbrains.kotlin.codegen.AsmUtil -import org.jetbrains.kotlin.codegen.ClassBuilder -import org.jetbrains.kotlin.codegen.StackValue -import org.jetbrains.kotlin.codegen.TransformationMethodVisitor -import org.jetbrains.kotlin.codegen.inline.* -import org.jetbrains.kotlin.codegen.optimization.boxing.isUnitInstance -import org.jetbrains.kotlin.codegen.optimization.common.* -import org.jetbrains.kotlin.codegen.optimization.fixStack.FixStackMethodTransformer -import org.jetbrains.kotlin.codegen.optimization.fixStack.top -import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer -import org.jetbrains.kotlin.config.LanguageVersionSettings -import org.jetbrains.kotlin.config.isReleaseCoroutines -import org.jetbrains.kotlin.diagnostics.DiagnosticSink -import org.jetbrains.kotlin.psi.KtElement -import org.jetbrains.kotlin.resolve.jvm.AsmTypes -import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm -import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin -import org.jetbrains.kotlin.utils.sure -import org.jetbrains.org.objectweb.asm.Label -import org.jetbrains.org.objectweb.asm.MethodVisitor -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.tree.* -import org.jetbrains.org.objectweb.asm.tree.analysis.* -import kotlin.math.max - -private const val COROUTINES_DEBUG_METADATA_VERSION = 1 - -private const val COROUTINES_METADATA_SOURCE_FILE_JVM_NAME = "f" -private const val COROUTINES_METADATA_LINE_NUMBERS_JVM_NAME = "l" -private const val COROUTINES_METADATA_LOCAL_NAMES_JVM_NAME = "n" -private const val COROUTINES_METADATA_SPILLED_JVM_NAME = "s" -private const val COROUTINES_METADATA_INDEX_TO_LABEL_JVM_NAME = "i" -private const val COROUTINES_METADATA_METHOD_NAME_JVM_NAME = "m" -private const val COROUTINES_METADATA_CLASS_NAME_JVM_NAME = "c" -private const val COROUTINES_METADATA_VERSION_JVM_NAME = "v" - -const val SUSPEND_FUNCTION_COMPLETION_PARAMETER_NAME = "\$completion" -const val SUSPEND_CALL_RESULT_NAME = "\$result" -const val ILLEGAL_STATE_ERROR_MESSAGE = "call to 'resume' before 'invoke' with coroutine" - -class CoroutineTransformerMethodVisitor( - delegate: MethodVisitor, - access: Int, - name: String, - desc: String, - signature: String?, - exceptions: Array?, - private val containingClassInternalName: String, - obtainClassBuilderForCoroutineState: () -> ClassBuilder, - private val isForNamedFunction: Boolean, - private val shouldPreserveClassInitialization: Boolean, - private val languageVersionSettings: LanguageVersionSettings, - // Since tail-call optimization of functions with Unit return type relies on ability of call-site to recognize them, - // in order to ignore return value and push Unit, when we cannot ensure this ability, for example, when the function overrides function, - // returning Any, we need to disable tail-call optimization for these functions. - private val disableTailCallOptimizationForFunctionReturningUnit: Boolean, - private val reportSuspensionPointInsideMonitor: (String) -> Unit, - private val lineNumber: Int, - private val sourceFile: String, - // It's only matters for named functions, may differ from '!isStatic(access)' in case of DefaultImpls - private val needDispatchReceiver: Boolean = false, - // May differ from containingClassInternalName in case of DefaultImpls - private val internalNameForDispatchReceiver: String? = null, - // JVM_IR backend generates $completion, while old backend does not - private val putContinuationParameterToLvt: Boolean = true -) : TransformationMethodVisitor(delegate, access, name, desc, signature, exceptions) { - - private val classBuilderForCoroutineState: ClassBuilder by lazy(obtainClassBuilderForCoroutineState) - - private var continuationIndex = if (isForNamedFunction) -1 else 0 - private var dataIndex = if (isForNamedFunction) -1 else 1 - private var exceptionIndex = if (isForNamedFunction || languageVersionSettings.isReleaseCoroutines()) -1 else 2 - - override fun performTransformations(methodNode: MethodNode) { - removeFakeContinuationConstructorCall(methodNode) - - replaceReturnsUnitMarkersWithPushingUnitOnStack(methodNode) - - replaceFakeContinuationsWithRealOnes( - methodNode, - if (isForNamedFunction) getLastParameterIndex(methodNode.desc, methodNode.access) else 0 - ) - - FixStackMethodTransformer().transform(containingClassInternalName, methodNode) - RedundantLocalsEliminationMethodTransformer(languageVersionSettings).transform(containingClassInternalName, methodNode) - if (languageVersionSettings.isReleaseCoroutines()) { - ChangeBoxingMethodTransformer.transform(containingClassInternalName, methodNode) - } - updateMaxStack(methodNode) - - val suspensionPoints = collectSuspensionPoints(methodNode) - - checkForSuspensionPointInsideMonitor(methodNode, suspensionPoints) - - // First instruction in the method node may change in case of named function - val actualCoroutineStart = methodNode.instructions.first - - if (isForNamedFunction) { - if (putContinuationParameterToLvt) { - addCompletionParameterToLVT(methodNode) - } - - val examiner = MethodNodeExaminer( - languageVersionSettings, - containingClassInternalName, - methodNode, - disableTailCallOptimizationForFunctionReturningUnit - ) - if (examiner.allSuspensionPointsAreTailCalls(suspensionPoints)) { - examiner.replacePopsBeforeSafeUnitInstancesWithCoroutineSuspendedChecks() - dropSuspensionMarkers(methodNode) - return - } - - dataIndex = methodNode.maxLocals++ - if (!languageVersionSettings.isReleaseCoroutines()) { - exceptionIndex = methodNode.maxLocals++ - } - continuationIndex = methodNode.maxLocals++ - - prepareMethodNodePreludeForNamedFunction(methodNode) - } - - for (suspensionPoint in suspensionPoints) { - splitTryCatchBlocksContainingSuspensionPoint(methodNode, suspensionPoint) - } - - // Actual max stack might be increased during the previous phases - updateMaxStack(methodNode) - - UninitializedStoresProcessor(methodNode, shouldPreserveClassInitialization).run() - - val spilledToVariableMapping = spillVariables(suspensionPoints, methodNode) - - val suspendMarkerVarIndex = methodNode.maxLocals++ - - val suspensionPointLineNumbers = suspensionPoints.map { findSuspensionPointLineNumber(it) } - - val continuationLabels = suspensionPoints.withIndex().map { - transformCallAndReturnContinuationLabel( - it.index + 1, it.value, methodNode, suspendMarkerVarIndex, suspensionPointLineNumbers[it.index]) - } - - methodNode.instructions.apply { - val tableSwitchLabel = LabelNode() - val firstStateLabel = LabelNode() - val defaultLabel = LabelNode() - - // tableswitch(this.label) - insertBefore( - actualCoroutineStart, - insnListOf( - *withInstructionAdapter { loadCoroutineSuspendedMarker(languageVersionSettings) }.toArray(), - tableSwitchLabel, - // Allow debugger to stop on enter into suspend function - LineNumberNode(lineNumber, tableSwitchLabel), - VarInsnNode(Opcodes.ASTORE, suspendMarkerVarIndex), - VarInsnNode(Opcodes.ALOAD, continuationIndex), - *withInstructionAdapter { getLabel() }.toArray(), - TableSwitchInsnNode( - 0, - suspensionPoints.size, - defaultLabel, - firstStateLabel, *continuationLabels.toTypedArray() - ), - firstStateLabel - ) - ) - - insert(firstStateLabel, withInstructionAdapter { - generateResumeWithExceptionCheck(languageVersionSettings.isReleaseCoroutines(), dataIndex, exceptionIndex) - }) - insert(last, defaultLabel) - - insert(last, withInstructionAdapter { - AsmUtil.genThrow(this, "java/lang/IllegalStateException", ILLEGAL_STATE_ERROR_MESSAGE) - areturn(Type.VOID_TYPE) - }) - } - - dropSuspensionMarkers(methodNode) - methodNode.removeEmptyCatchBlocks() - - // The parameters (and 'this') shall live throughout the method, otherwise, d8 emits warning about invalid debug info - val startLabel = LabelNode() - val endLabel = LabelNode() - methodNode.instructions.insertBefore(methodNode.instructions.first, startLabel) - methodNode.instructions.insert(methodNode.instructions.last, endLabel) - - fixLvtForParameters(methodNode, startLabel, endLabel) - - if (languageVersionSettings.isReleaseCoroutines()) { - writeDebugMetadata(methodNode, suspensionPointLineNumbers, spilledToVariableMapping) - } - } - - private fun addCompletionParameterToLVT(methodNode: MethodNode) { - val index = - /* all args */ Type.getMethodType(methodNode.desc).argumentTypes.fold(0) { a, b -> a + b.size } + - /* this */ (if (isStatic(methodNode.access)) 0 else 1) - - /* only last */ 1 - val startLabel = with(methodNode.instructions) { - if (first is LabelNode) first as LabelNode - else LabelNode().also { insertBefore(first, it) } - } - - val endLabel = with(methodNode.instructions) { - if (last is LabelNode) last as LabelNode - else LabelNode().also { insert(last, it) } - } - methodNode.localVariables.add( - LocalVariableNode( - SUSPEND_FUNCTION_COMPLETION_PARAMETER_NAME, - languageVersionSettings.continuationAsmType().descriptor, - null, - startLabel, - endLabel, - index - ) - ) - } - - /* Put { POP, GETSTATIC Unit } after suspension point if suspension point is a call of suspend function, that returns Unit. - * - * Otherwise, upon resume, the function would seem to not return Unit, despite being declared as returning Unit. - * - * This happens when said function is tail-call and its callee does not return Unit. - * - * Let's have an example - * - * suspend fun int(): Int = suspendCoroutine { ...; 1 } - * - * suspend fun unit() { - * int() - * } - * - * suspend fun main() { - * println(unit()) - * } - * - * So, in order to understand the necessity of { POP, GETSTATIC Unit } inside `main`, we need to consider two different scenarios - * - * 1. `unit` is not a tail-call function. - * 2. `unit` is a tail-call function. - * - * When `unit` is a not tail-call function, calling `resumeWith` on its continuation will resume `unit`, - * it will hit { GETSTATIC Unit; ARETURN } and this Unit will be the result of the suspend call. `unit`'s continuation will then call - * `main` continuation's `resumeWith`, passing the Unit instance. The continuation in turn will resume `main` and the Unit will be - * the result of `unit()` call. This result will then printed. - * - * However, when `unit` is a tail-call function, there is no continuation, generated for it. This is the point of tail-call - * optimization. Thus, resume call will skip `unit` and land direcly in `main` continuation's `resumeWith`. And its result is not - * Unit. Thus, we must ignore this result on call-site and use Unit instead. In other words, POP the result and GETSTATIC Unit - * instead. - */ - private fun replaceReturnsUnitMarkersWithPushingUnitOnStack(methodNode: MethodNode) { - for (marker in methodNode.instructions.asSequence().filter(::isReturnsUnitMarker).toList()) { - assert(marker.next?.next?.let { isAfterSuspendMarker(it) } == true) { - "Expected AfterSuspendMarker after ReturnUnitMarker, got ${marker.next?.next}" - } - methodNode.instructions.insert( - marker.next.next, - withInstructionAdapter { - pop() - getstatic("kotlin/Unit", "INSTANCE", "Lkotlin/Unit;") - } - ) - methodNode.instructions.removeAll(listOf(marker.previous, marker)) - } - } - - private fun findSuspensionPointLineNumber(suspensionPoint: SuspensionPoint) = - suspensionPoint.suspensionCallBegin.findPreviousOrNull { it is LineNumberNode } as LineNumberNode? - - private fun checkForSuspensionPointInsideMonitor(methodNode: MethodNode, suspensionPoints: List) { - if (methodNode.instructions.asSequence().none { it.opcode == Opcodes.MONITORENTER }) return - - val cfg = ControlFlowGraph.build(methodNode) - val monitorDepthMap = hashMapOf() - fun addMonitorDepthToSuccs(index: Int, depth: Int) { - val insn = methodNode.instructions[index] - monitorDepthMap[insn] = depth - val newDepth = when (insn.opcode) { - Opcodes.MONITORENTER -> depth + 1 - Opcodes.MONITOREXIT -> depth - 1 - else -> depth - } - for (succIndex in cfg.getSuccessorsIndices(index)) { - if (monitorDepthMap[methodNode.instructions[succIndex]] == null) { - addMonitorDepthToSuccs(succIndex, newDepth) - } - } - } - - addMonitorDepthToSuccs(0, 0) - - for (suspensionPoint in suspensionPoints) { - if (monitorDepthMap[suspensionPoint.suspensionCallBegin]?.let { it > 0 } == true) { - // TODO: Support crossinline suspend lambdas - val stackTraceElement = StackTraceElement( - containingClassInternalName, - methodNode.name, - sourceFile, - findSuspensionPointLineNumber(suspensionPoint)?.line ?: -1 - ) - reportSuspensionPointInsideMonitor("$stackTraceElement") - return - } - } - } - - private fun fixLvtForParameters(methodNode: MethodNode, startLabel: LabelNode, endLabel: LabelNode) { - val paramsNum = - /* this */ (if (isStatic(methodNode.access)) 0 else 1) + - /* real params */ Type.getArgumentTypes(methodNode.desc).fold(0) { a, b -> a + b.size } - - for (i in 0 until paramsNum) { - fixRangeOfLvtRecord(methodNode, i, startLabel, endLabel) - } - } - - private fun fixRangeOfLvtRecord(methodNode: MethodNode, index: Int, startLabel: LabelNode, endLabel: LabelNode) { - val vars = methodNode.localVariables.filter { it.index == index } - assert(vars.size <= 1) { - "Someone else occupies parameter's slot at $index" - } - vars.firstOrNull()?.let { - it.start = startLabel - it.end = endLabel - } - } - - private fun writeDebugMetadata( - methodNode: MethodNode, - suspensionPointLineNumbers: List, - spilledToLocalMapping: List> - ) { - val lines = suspensionPointLineNumbers.map { it?.line ?: -1 } - val metadata = classBuilderForCoroutineState.newAnnotation(DEBUG_METADATA_ANNOTATION_ASM_TYPE.descriptor, true) - metadata.visit(COROUTINES_METADATA_SOURCE_FILE_JVM_NAME, sourceFile) - metadata.visit(COROUTINES_METADATA_LINE_NUMBERS_JVM_NAME, lines.toIntArray()) - - val debugIndexToLabel = spilledToLocalMapping.withIndex().flatMap { (labelIndex, list) -> - list.map { labelIndex } - } - val variablesMapping = spilledToLocalMapping.flatten() - metadata.visit(COROUTINES_METADATA_INDEX_TO_LABEL_JVM_NAME, debugIndexToLabel.toIntArray()) - metadata.visitArray(COROUTINES_METADATA_SPILLED_JVM_NAME).also { v -> - variablesMapping.forEach { v.visit(null, it.fieldName) } - }.visitEnd() - metadata.visitArray(COROUTINES_METADATA_LOCAL_NAMES_JVM_NAME).also { v -> - variablesMapping.forEach { v.visit(null, it.variableName) } - }.visitEnd() - metadata.visit(COROUTINES_METADATA_METHOD_NAME_JVM_NAME, methodNode.name) - metadata.visit(COROUTINES_METADATA_CLASS_NAME_JVM_NAME, Type.getObjectType(containingClassInternalName).className) - @Suppress("ConstantConditionIf") - if (COROUTINES_DEBUG_METADATA_VERSION != 1) { - metadata.visit(COROUTINES_METADATA_VERSION_JVM_NAME, COROUTINES_DEBUG_METADATA_VERSION) - } - metadata.visitEnd() - } - - // Warning! This is _continuation_, not _completion_, it can be allocated inside the method, thus, it is incorrect to treat it - // as a parameter - private fun addContinuationAndResultToLvt( - methodNode: MethodNode, - startLabel: Label, - resultStartLabel: Label - ) { - val endLabel = Label() - methodNode.instructions.add(withInstructionAdapter { mark(endLabel) }) - methodNode.visitLocalVariable( - CONTINUATION_VARIABLE_NAME, - languageVersionSettings.continuationAsmType().descriptor, - null, - startLabel, - endLabel, - continuationIndex - ) - methodNode.visitLocalVariable( - SUSPEND_CALL_RESULT_NAME, - AsmTypes.OBJECT_TYPE.descriptor, - null, - resultStartLabel, - endLabel, - dataIndex - ) - } - - private fun removeFakeContinuationConstructorCall(methodNode: MethodNode) { - val seq = methodNode.instructions.asSequence() - val first = seq.firstOrNull(::isBeforeFakeContinuationConstructorCallMarker)?.previous ?: return - val last = seq.firstOrNull(::isAfterFakeContinuationConstructorCallMarker).sure { - "BeforeFakeContinuationConstructorCallMarker without AfterFakeContinuationConstructorCallMarker" - } - val toRemove = InsnSequence(first, last).toList() - methodNode.instructions.removeAll(toRemove) - methodNode.instructions.set(last, InsnNode(Opcodes.ACONST_NULL)) - } - - private fun InstructionAdapter.getLabel() { - if (isForNamedFunction && !languageVersionSettings.isReleaseCoroutines()) - invokevirtual( - classBuilderForCoroutineState.thisName, - "getLabel", - Type.getMethodDescriptor(Type.INT_TYPE), - false - ) - else - getfield( - computeLabelOwner(languageVersionSettings, classBuilderForCoroutineState.thisName).internalName, - COROUTINE_LABEL_FIELD_NAME, Type.INT_TYPE.descriptor - ) - } - - private fun InstructionAdapter.setLabel() { - if (isForNamedFunction && !languageVersionSettings.isReleaseCoroutines()) - invokevirtual( - classBuilderForCoroutineState.thisName, - "setLabel", - Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE), - false - ) - else - putfield( - computeLabelOwner(languageVersionSettings, classBuilderForCoroutineState.thisName).internalName, - COROUTINE_LABEL_FIELD_NAME, Type.INT_TYPE.descriptor - ) - } - - private fun updateMaxStack(methodNode: MethodNode) { - methodNode.instructions.resetLabels() - methodNode.accept( - MaxStackFrameSizeAndLocalsCalculator( - Opcodes.API_VERSION, methodNode.access, methodNode.desc, - object : MethodVisitor(Opcodes.API_VERSION) { - override fun visitMaxs(maxStack: Int, maxLocals: Int) { - methodNode.maxStack = maxStack - } - } - ) - ) - } - - private fun prepareMethodNodePreludeForNamedFunction(methodNode: MethodNode) { - val objectTypeForState = Type.getObjectType(classBuilderForCoroutineState.thisName) - val continuationArgumentIndex = getLastParameterIndex(methodNode.desc, methodNode.access) - methodNode.instructions.asSequence().filterIsInstance().forEach { - if (it.`var` != continuationArgumentIndex) return@forEach - assert(it.opcode == Opcodes.ALOAD) { "Only ALOADs are allowed for continuation arguments" } - it.`var` = continuationIndex - } - - methodNode.instructions.insert(withInstructionAdapter { - val createStateInstance = Label() - val afterCoroutineStateCreated = Label() - - // We have to distinguish the following situations: - // - Our function got called in a common way (e.g. from another function or via recursive call) and we should execute our - // code from the beginning - // - We got called from `doResume` of our continuation, i.e. we need to continue from the last suspension point - // - // Also in the first case we wrap the completion into a special anonymous class instance (let's call it X$1) - // that we'll use as a continuation argument for suspension points - // - // How we distinguish the cases: - // - If the continuation is not an instance of X$1 we know exactly it's not the second case, because when resuming - // the continuation we pass an instance of that class - // - Otherwise it's still can be a recursive call. To check it's not the case we set the last bit in the label in - // `doResume` just before calling the suspend function (see kotlin.coroutines.experimental.jvm.internal.CoroutineImplForNamedFunction). - // So, if it's set we're in continuation. - - visitVarInsn(Opcodes.ALOAD, continuationArgumentIndex) - instanceOf(objectTypeForState) - ifeq(createStateInstance) - - visitVarInsn(Opcodes.ALOAD, continuationArgumentIndex) - checkcast(objectTypeForState) - visitVarInsn(Opcodes.ASTORE, continuationIndex) - - visitVarInsn(Opcodes.ALOAD, continuationIndex) - getLabel() - - iconst(1 shl 31) - and(Type.INT_TYPE) - ifeq(createStateInstance) - - visitVarInsn(Opcodes.ALOAD, continuationIndex) - dup() - getLabel() - - iconst(1 shl 31) - sub(Type.INT_TYPE) - setLabel() - - goTo(afterCoroutineStateCreated) - - visitLabel(createStateInstance) - - generateContinuationConstructorCall( - objectTypeForState, - methodNode, - needDispatchReceiver, - internalNameForDispatchReceiver, - containingClassInternalName, - classBuilderForCoroutineState, - languageVersionSettings - ) - - visitVarInsn(Opcodes.ASTORE, continuationIndex) - - visitLabel(afterCoroutineStateCreated) - - visitVarInsn(Opcodes.ALOAD, continuationIndex) - getfield(classBuilderForCoroutineState.thisName, languageVersionSettings.dataFieldName(), AsmTypes.OBJECT_TYPE.descriptor) - visitVarInsn(Opcodes.ASTORE, dataIndex) - - val resultStartLabel = Label() - visitLabel(resultStartLabel) - - addContinuationAndResultToLvt(methodNode, afterCoroutineStateCreated, resultStartLabel) - - if (!languageVersionSettings.isReleaseCoroutines()) { - visitVarInsn(Opcodes.ALOAD, continuationIndex) - getfield(classBuilderForCoroutineState.thisName, EXCEPTION_FIELD_NAME, AsmTypes.JAVA_THROWABLE_TYPE.descriptor) - visitVarInsn(Opcodes.ASTORE, exceptionIndex) - } - }) - } - - /* - * Every suspension point should be surrounded by two markers: before suspension point marker (start marker) - * and after suspension point marker (end marker) - * - * However, if suspension point comes from inline function and its end marker is unreachable, the end marker is removed by - * either inliner or bytecode optimization. - * - * If this happens, we should restore end marker. - * - * Since in both cases (when end marker is reachable and when it is not) all paths should lead to - * either a single end marker or to ATHROWs and ARETURNs, we just compute all paths from start marker until they reach - * these instructions. - */ - private fun collectSuspensionPoints(methodNode: MethodNode): List { - // Exception paths lead outside suspension points, thus we should ignore them - val cfg = ControlFlowGraph.build(methodNode, followExceptions = false) - - // DFS until end marker or ATHROW or ARETURN. - // return true if it contains nested suspension points, which happens when we inline suspend lambda - // with multiple suspension points via several inlines. See boxInline/state/stateMachine/passLambda.kt as an example. - // In this case we simply ignore them. - fun collectSuspensionPointEnds( - insn: AbstractInsnNode, - visited: MutableSet, - ends: MutableSet - ): Boolean { - if (!visited.add(insn)) return false - if (insn.opcode == Opcodes.ARETURN || insn.opcode == Opcodes.ATHROW || isAfterSuspendMarker(insn)) { - ends.add(insn) - } else { - for (index in cfg.getSuccessorsIndices(insn)) { - val succ = methodNode.instructions[index] - if (isBeforeSuspendMarker(succ)) return true - if (collectSuspensionPointEnds(succ, visited, ends)) return true - } - } - return false - } - - val starts = methodNode.instructions.asSequence().filter { - isBeforeSuspendMarker(it) && - cfg.getPredecessorsIndices(it).isNotEmpty() // Ignore unreachable start markers - }.toList() - return starts.mapNotNull { start -> - val ends = mutableSetOf() - if (collectSuspensionPointEnds(start, mutableSetOf(), ends)) return@mapNotNull null - // Ignore suspension points, if the suspension call begin is alive and suspension call end is dead - // (e.g., an inlined suspend function call ends with throwing a exception -- see KT-15017), - // (also see boxInline/suspend/stateMachine/unreachableSuspendMarker.kt) - // this is an exit point for the corresponding coroutine. - val end = ends.find { isAfterSuspendMarker(it) } ?: return@mapNotNull null - SuspensionPoint(start.previous, end) - } - } - - private fun dropSuspensionMarkers(methodNode: MethodNode) { - // Drop markers, including ones, which we ignored in recognizing phase - for (marker in methodNode.instructions.asSequence().filter { isBeforeSuspendMarker(it) || isAfterSuspendMarker(it) }.toList()) { - methodNode.instructions.removeAll(listOf(marker.previous, marker)) - } - } - - private fun spillVariables(suspensionPoints: List, methodNode: MethodNode): List> { - val instructions = methodNode.instructions - val frames = performRefinedTypeAnalysis(methodNode, containingClassInternalName) - fun AbstractInsnNode.index() = instructions.indexOf(this) - - // We postpone these actions because they change instruction indices that we use when obtaining frames - val postponedActions = mutableListOf<() -> Unit>() - val maxVarsCountByType = mutableMapOf() - val livenessFrames = analyzeLiveness(methodNode) - val spilledToVariableMapping = arrayListOf>() - - for (suspension in suspensionPoints) { - val suspensionCallBegin = suspension.suspensionCallBegin - - assert(frames[suspension.suspensionCallEnd.next.index()]?.stackSize == 1) { - "Stack should be spilled before suspension call" - } - - val frame = frames[suspensionCallBegin.index()].sure { "Suspension points containing in dead code must be removed" } - val localsCount = frame.locals - val varsCountByType = mutableMapOf() - - // We consider variable liveness to avoid problems with inline suspension functions: - // - // * - // RETURN (appears only on further transformation phase) - // ... - // - // - // The problem is that during current phase (before inserting RETURN opcode) we suppose variables generated - // within inline suspension point as correctly initialized, thus trying to spill them. - // While after RETURN introduction these variables become uninitialized (at the same time they can't be used further). - // So we only spill variables that are alive at the begin of suspension point. - // NB: it's also rather useful for sake of optimization - val livenessFrame = livenessFrames[suspensionCallBegin.index()] - - val spilledToVariable = arrayListOf() - - // 0 - this - // 1 - parameter - // ... - // k - continuation - // k + 1 - data - // k + 2 - exception - val variablesToSpill = - (0 until localsCount) - .filterNot { it in setOf(continuationIndex, dataIndex, exceptionIndex) } - .map { Pair(it, frame.getLocal(it)) } - .filter { (index, value) -> - (index == 0 && needDispatchReceiver && isForNamedFunction) || - (value != StrictBasicValue.UNINITIALIZED_VALUE && livenessFrame.isAlive(index)) - } - - for ((index, basicValue) in variablesToSpill) { - if (basicValue === StrictBasicValue.NULL_VALUE) { - postponedActions.add { - with(instructions) { - insert(suspension.tryCatchBlockEndLabelAfterSuspensionCall, withInstructionAdapter { - aconst(null) - store(index, AsmTypes.OBJECT_TYPE) - }) - } - } - continue - } - - val type = basicValue.type - val normalizedType = type.normalize() - - val indexBySort = varsCountByType[normalizedType]?.plus(1) ?: 0 - varsCountByType[normalizedType] = indexBySort - - val fieldName = normalizedType.fieldNameForVar(indexBySort) - localVariableName(methodNode, index, suspension.suspensionCallEnd.next.index()) - ?.let { spilledToVariable.add(SpilledVariableDescriptor(fieldName, it)) } - - postponedActions.add { - with(instructions) { - // store variable before suspension call - insertBefore(suspension.suspensionCallBegin, withInstructionAdapter { - load(continuationIndex, AsmTypes.OBJECT_TYPE) - load(index, type) - StackValue.coerce(type, normalizedType, this) - putfield(classBuilderForCoroutineState.thisName, fieldName, normalizedType.descriptor) - }) - - // restore variable after suspension call - insert(suspension.tryCatchBlockEndLabelAfterSuspensionCall, withInstructionAdapter { - load(continuationIndex, AsmTypes.OBJECT_TYPE) - getfield(classBuilderForCoroutineState.thisName, fieldName, normalizedType.descriptor) - StackValue.coerce(normalizedType, type, this) - store(index, type) - }) - } - } - } - - spilledToVariableMapping.add(spilledToVariable) - - varsCountByType.forEach { - maxVarsCountByType[it.key] = max(maxVarsCountByType[it.key] ?: 0, it.value) - } - } - - postponedActions.forEach(Function0::invoke) - - maxVarsCountByType.forEach { entry -> - val (type, maxIndex) = entry - for (index in 0..maxIndex) { - classBuilderForCoroutineState.newField( - JvmDeclarationOrigin.NO_ORIGIN, AsmUtil.NO_FLAG_PACKAGE_PRIVATE, - type.fieldNameForVar(index), type.descriptor, null, null - ) - } - } - return spilledToVariableMapping - } - - private fun localVariableName( - methodNode: MethodNode, - index: Int, - suspensionCallIndex: Int - ): String? { - val variable = methodNode.localVariables.find { - index == it.index && methodNode.instructions.indexOf(it.start) <= suspensionCallIndex - && suspensionCallIndex < methodNode.instructions.indexOf(it.end) - } - return variable?.name - } - - /** - * See 'splitTryCatchBlocksContainingSuspensionPoint' - */ - private val SuspensionPoint.tryCatchBlockEndLabelAfterSuspensionCall: LabelNode - get() { - assert(suspensionCallEnd.next is LabelNode) { - "Next instruction after ${this} should be a label, but " + - "${suspensionCallEnd.next::class.java}/${suspensionCallEnd.next.opcode} was found" - } - - return suspensionCallEnd.next as LabelNode - } - - private fun transformCallAndReturnContinuationLabel( - id: Int, - suspension: SuspensionPoint, - methodNode: MethodNode, - suspendMarkerVarIndex: Int, - suspendPointLineNumber: LineNumberNode? - ): LabelNode { - val continuationLabel = LabelNode() - val continuationLabelAfterLoadedResult = LabelNode() - val suspendElementLineNumber = lineNumber - var nextLineNumberNode = nextDefinitelyHitLineNumber(suspension) - with(methodNode.instructions) { - // Save state - insertBefore( - suspension.suspensionCallBegin, - withInstructionAdapter { - visitVarInsn(Opcodes.ALOAD, continuationIndex) - iconst(id) - setLabel() - } - ) - - insert(suspension.tryCatchBlockEndLabelAfterSuspensionCall, withInstructionAdapter { - dup() - load(suspendMarkerVarIndex, AsmTypes.OBJECT_TYPE) - ifacmpne(continuationLabelAfterLoadedResult.label) - - // Exit - val returnLabel = LabelNode() - visitLabel(returnLabel.label) - // Special line number to stop in debugger before suspend return - visitLineNumber(suspendElementLineNumber, returnLabel.label) - load(suspendMarkerVarIndex, AsmTypes.OBJECT_TYPE) - areturn(AsmTypes.OBJECT_TYPE) - // Mark place for continuation - visitLabel(continuationLabel.label) - }) - - // After suspension point there is always three nodes: L1, NOP, L2 - // And if there are relevant exception handlers, they always start at L2 - // See 'splitTryCatchBlocksContainingSuspensionPoint' - val possibleTryCatchBlockStart = suspension.tryCatchBlocksContinuationLabel - - // Move NOP, which is inserted in `splitTryCatchBlocksContainingSuspentionPoint`, inside the try catch block, - // so the inliner can transform suspend lambdas during inlining - assert(possibleTryCatchBlockStart.previous.opcode == Opcodes.NOP) { - "NOP expected but ${possibleTryCatchBlockStart.previous.opcode} was found" - } - remove(possibleTryCatchBlockStart.previous) - - insert(possibleTryCatchBlockStart, withInstructionAdapter { - nop() - generateResumeWithExceptionCheck(languageVersionSettings.isReleaseCoroutines(), dataIndex, exceptionIndex) - - // Load continuation argument just like suspending function returns it - load(dataIndex, AsmTypes.OBJECT_TYPE) - - visitLabel(continuationLabelAfterLoadedResult.label) - - if (nextLineNumberNode != null) { - // If there is a clear next linenumber instruction, extend it. Can't use line number of suspension point - // here because both non-suspended execution and re-entering after suspension passes this label. - if (possibleTryCatchBlockStart.next?.opcode?.let { - it != Opcodes.ASTORE && it != Opcodes.CHECKCAST && it != Opcodes.INVOKESTATIC && - it != Opcodes.INVOKEVIRTUAL && it != Opcodes.INVOKEINTERFACE - } == true - ) { - visitLineNumber(nextLineNumberNode!!.line, continuationLabelAfterLoadedResult.label) - } else { - // But keep the linenumber if the result of the call is used afterwards - nextLineNumberNode = null - } - } else if (suspendPointLineNumber != null) { - // If there is no clear next linenumber instruction, the continuation is still on the - // same line as the suspend point. - visitLineNumber(suspendPointLineNumber.line, continuationLabelAfterLoadedResult.label) - } - }) - - if (nextLineNumberNode != null) { - // Remove the line number instruction as it now covered with line number on continuation label. - // If both linenumber are present in bytecode, debugger will trigger line specific events twice. - remove(nextLineNumberNode) - } - } - - return continuationLabel - } - - // Find the next line number instruction that is defintely hit. That is, a line number - // that comes before any branch or method call. - private fun nextDefinitelyHitLineNumber(suspension: SuspensionPoint): LineNumberNode? { - var next = suspension.suspensionCallEnd.next - while (next != null) { - if (next.isBranchOrCall) return null - else if (next is LineNumberNode) return next - else next = next.next - } - return next - } - - // It's necessary to preserve some sensible invariants like there should be no jump in the middle of try-catch-block - // Also it's important that spilled variables are being restored outside of TCB, - // otherwise they would be treated as uninitialized within catch-block while they can be used there - // How suspension point area will look like after all transformations: - // - // INVOKESTATIC beforeSuspensionMarker - // INVOKEVIRTUAL suspensionMethod()Ljava/lang/Object; - // CHECKCAST SomeType - // INVOKESTATIC afterSuspensionMarker - // L1: -- end of all TCB's that are containing the suspension point (inserted by this method) - // RETURN - // L2: -- continuation label (used for the TABLESWITCH) - // (no try-catch blocks here) - // L3: begin/continuation of all TCB's that are containing the suspension point (inserted by this method) - // ... - private fun splitTryCatchBlocksContainingSuspensionPoint(methodNode: MethodNode, suspensionPoint: SuspensionPoint) { - val instructions = methodNode.instructions - val beginIndex = instructions.indexOf(suspensionPoint.suspensionCallBegin) - val endIndex = instructions.indexOf(suspensionPoint.suspensionCallEnd) - - val firstLabel = LabelNode() - val secondLabel = LabelNode() - instructions.insert(suspensionPoint.suspensionCallEnd, firstLabel) - // NOP is needed to preventing these label merge - // Here between these labels additional instructions are supposed to be inserted (variables spilling, etc.) - instructions.insert(firstLabel, InsnNode(Opcodes.NOP)) - instructions.insert(firstLabel.next, secondLabel) - - methodNode.tryCatchBlocks = - methodNode.tryCatchBlocks.flatMap { - val isContainingSuspensionPoint = - instructions.indexOf(it.start) < beginIndex && beginIndex < instructions.indexOf(it.end) - - if (isContainingSuspensionPoint) { - assert(instructions.indexOf(it.start) < endIndex && endIndex < instructions.indexOf(it.end)) { - "Try catch block ${instructions.indexOf(it.start)}:${instructions.indexOf(it.end)} containing marker before " + - "suspension point $beginIndex should also contain the marker after suspension point $endIndex" - } - listOf( - TryCatchBlockNode(it.start, firstLabel, it.handler, it.type), - TryCatchBlockNode(secondLabel, it.end, it.handler, it.type) - ) - } else - listOf(it) - } - - suspensionPoint.tryCatchBlocksContinuationLabel = secondLabel - - return - } - - private data class SpilledVariableDescriptor(val fieldName: String, val variableName: String) -} - -// TODO Use this in variable liveness analysis -private class MethodNodeExaminer( - val languageVersionSettings: LanguageVersionSettings, - val containingClassInternalName: String, - val methodNode: MethodNode, - disableTailCallOptimizationForFunctionReturningUnit: Boolean -) { - private val sourceFrames: Array> = - MethodTransformer.analyze(containingClassInternalName, methodNode, IgnoringCopyOperationSourceInterpreter()) - private val controlFlowGraph = ControlFlowGraph.build(methodNode) - - private val safeUnitInstances = mutableSetOf() - private val popsBeforeSafeUnitInstances = mutableSetOf() - private val areturnsAfterSafeUnitInstances = mutableSetOf() - private val meaningfulSuccessorsCache = hashMapOf>() - private val meaningfulPredecessorsCache = hashMapOf>() - - init { - if (!disableTailCallOptimizationForFunctionReturningUnit) { - // retrieve all POP insns - val pops = methodNode.instructions.asSequence().filter { it.opcode == Opcodes.POP } - // for each of them check that all successors are PUSH Unit - val popsBeforeUnitInstances = pops.map { it to it.meaningfulSuccessors() } - .filter { (_, succs) -> succs.all { it.isUnitInstance() } } - .map { it.first }.toList() - for (pop in popsBeforeUnitInstances) { - val units = pop.meaningfulSuccessors() - val allUnitsAreSafe = units.all { unit -> - // check no other predecessor exists - unit.meaningfulPredecessors().all { it in popsBeforeUnitInstances } && - // check they have only returns among successors - unit.meaningfulSuccessors().all { it.opcode == Opcodes.ARETURN } - } - if (!allUnitsAreSafe) continue - // save them all to the properties - popsBeforeSafeUnitInstances += pop - safeUnitInstances += units - units.flatMapTo(areturnsAfterSafeUnitInstances) { it.meaningfulSuccessors() } - } - } - } - - private fun AbstractInsnNode.index() = methodNode.instructions.indexOf(this) - - // GETSTATIC kotlin/Unit.INSTANCE is considered safe iff - // it is part of POP, PUSH Unit, ARETURN sequence. - private fun AbstractInsnNode.isSafeUnitInstance(): Boolean = this in safeUnitInstances - - private fun AbstractInsnNode.isPopBeforeSafeUnitInstance(): Boolean = this in popsBeforeSafeUnitInstances - private fun AbstractInsnNode.isAreturnAfterSafeUnitInstance(): Boolean = this in areturnsAfterSafeUnitInstances - - private fun AbstractInsnNode.meaningfulSuccessors(): List = meaningfulSuccessorsCache.getOrPut(this) { - meaningfulSuccessorsOrPredecessors(true) - } - - private fun AbstractInsnNode.meaningfulPredecessors(): List = meaningfulPredecessorsCache.getOrPut(this) { - meaningfulSuccessorsOrPredecessors(false) - } - - private fun AbstractInsnNode.meaningfulSuccessorsOrPredecessors(isSuccessors: Boolean): List { - fun AbstractInsnNode.isMeaningful() = isMeaningful && opcode != Opcodes.NOP && opcode != Opcodes.GOTO && this !is LineNumberNode - - fun AbstractInsnNode.getIndices() = - if (isSuccessors) controlFlowGraph.getSuccessorsIndices(this) - else controlFlowGraph.getPredecessorsIndices(this) - - val visited = arrayListOf() - fun dfs(insn: AbstractInsnNode) { - if (insn in visited) return - visited += insn - if (!insn.isMeaningful()) { - for (succIndex in insn.getIndices()) { - dfs(methodNode.instructions[succIndex]) - } - } - } - - for (succIndex in getIndices()) { - dfs(methodNode.instructions[succIndex]) - } - return visited.filter { it.isMeaningful() } - } - - fun replacePopsBeforeSafeUnitInstancesWithCoroutineSuspendedChecks() { - val basicAnalyser = Analyzer(BasicInterpreter()) - basicAnalyser.analyze(containingClassInternalName, methodNode) - val typedFrames = basicAnalyser.frames - - val isReferenceMap = popsBeforeSafeUnitInstances - .map { it to (!isUnreachable(it.index(), sourceFrames) && typedFrames[it.index()]?.top()?.isReference == true) } - .toMap() - - for (pop in popsBeforeSafeUnitInstances) { - if (isReferenceMap[pop] == true) { - val label = Label() - methodNode.instructions.insertBefore(pop, withInstructionAdapter { - dup() - loadCoroutineSuspendedMarker(languageVersionSettings) - ifacmpne(label) - areturn(AsmTypes.OBJECT_TYPE) - mark(label) - }) - } - } - } - - fun allSuspensionPointsAreTailCalls(suspensionPoints: List): Boolean { - val safelyReachableReturns = findSafelyReachableReturns() - - val instructions = methodNode.instructions - return suspensionPoints.all { suspensionPoint -> - val beginIndex = instructions.indexOf(suspensionPoint.suspensionCallBegin) - val endIndex = instructions.indexOf(suspensionPoint.suspensionCallEnd) - - if (isUnreachable(endIndex, sourceFrames)) return@all true - - val insideTryBlock = methodNode.tryCatchBlocks.any { block -> - val tryBlockStartIndex = instructions.indexOf(block.start) - val tryBlockEndIndex = instructions.indexOf(block.end) - - beginIndex in tryBlockStartIndex..tryBlockEndIndex - } - if (insideTryBlock) return@all false - - safelyReachableReturns[endIndex + 1]?.all { returnIndex -> - sourceFrames[returnIndex]?.top().sure { - "There must be some value on stack to return" - }.insns.any { sourceInsn -> - sourceInsn?.let(instructions::indexOf) in beginIndex..endIndex - } - } ?: false - } - } - - /** - * Let's call an instruction safe if its execution is always invisible: stack modifications, branching, variable insns (invisible in debug) - * - * For some instruction `insn` define the result as following: - * - if there is a path leading to the non-safe instruction then result is `null` - * - Otherwise result contains all the reachable ARETURN indices - * - * @return indices of safely reachable returns for each instruction in the method node - */ - private fun findSafelyReachableReturns(): Array?> { - val insns = methodNode.instructions - val reachableReturnsIndices = Array?>(insns.size()) init@{ index -> - val insn = insns[index] - - if (insn.opcode == Opcodes.ARETURN && !insn.isAreturnAfterSafeUnitInstance()) { - if (isUnreachable(index, sourceFrames)) return@init null - return@init setOf(index) - } - - // Since POP, PUSH Unit, ARETURN behaves like normal return in terms of tail-call optimization, set return index to POP - if (insn.isPopBeforeSafeUnitInstance()) { - return@init setOf(index) - } - - if (!insn.isMeaningful || insn.opcode in SAFE_OPCODES || insn.isInvisibleInDebugVarInsn(methodNode) || isInlineMarker(insn) - || insn.isSafeUnitInstance() || insn.isAreturnAfterSafeUnitInstance() - ) { - setOf() - } else null - } - - var changed: Boolean - do { - changed = false - for (index in 0 until insns.size()) { - if (insns[index].opcode == Opcodes.ARETURN) continue - - @Suppress("RemoveExplicitTypeArguments") - val newResult = - controlFlowGraph - .getSuccessorsIndices(index).plus(index) - .map(reachableReturnsIndices::get) - .fold?, Set?>(mutableSetOf()) { acc, successorsResult -> - if (acc != null && successorsResult != null) acc + successorsResult else null - } - - if (newResult != reachableReturnsIndices[index]) { - reachableReturnsIndices[index] = newResult - changed = true - } - } - } while (changed) - - return reachableReturnsIndices - } -} - -internal fun InstructionAdapter.generateContinuationConstructorCall( - objectTypeForState: Type?, - methodNode: MethodNode, - needDispatchReceiver: Boolean, - internalNameForDispatchReceiver: String?, - containingClassInternalName: String, - classBuilderForCoroutineState: ClassBuilder, - languageVersionSettings: LanguageVersionSettings -) { - anew(objectTypeForState) - dup() - - val parameterTypesAndIndices = - getParameterTypesIndicesForCoroutineConstructor( - methodNode.desc, - methodNode.access, - needDispatchReceiver, internalNameForDispatchReceiver ?: containingClassInternalName, - languageVersionSettings - ) - for ((type, index) in parameterTypesAndIndices) { - load(index, type) - } - - invokespecial( - classBuilderForCoroutineState.thisName, - "", - Type.getMethodDescriptor( - Type.VOID_TYPE, - *getParameterTypesForCoroutineConstructor( - methodNode.desc, needDispatchReceiver, - internalNameForDispatchReceiver ?: containingClassInternalName - ) - ), - false - ) -} - -private fun InstructionAdapter.generateResumeWithExceptionCheck(isReleaseCoroutines: Boolean, dataIndex: Int, exceptionIndex: Int) { - // Check if resumeWithException has been called - - if (isReleaseCoroutines) { - load(dataIndex, AsmTypes.OBJECT_TYPE) - invokestatic("kotlin/ResultKt", "throwOnFailure", "(Ljava/lang/Object;)V", false) - } else { - load(exceptionIndex, AsmTypes.OBJECT_TYPE) - dup() - val noExceptionLabel = Label() - ifnull(noExceptionLabel) - athrow() - - mark(noExceptionLabel) - pop() - } -} - -private fun Type.fieldNameForVar(index: Int) = descriptor.first() + "$" + index - -inline fun withInstructionAdapter(block: InstructionAdapter.() -> Unit): InsnList { - val tmpMethodNode = MethodNode() - - InstructionAdapter(tmpMethodNode).apply(block) - - return tmpMethodNode.instructions -} - -private fun Type.normalize() = - when (sort) { - Type.ARRAY, Type.OBJECT -> AsmTypes.OBJECT_TYPE - else -> this - } - -/** - * Suspension call may consists of several instructions: - * ICONST_0 - * INVOKESTATIC InlineMarker.mark() - * INVOKEVIRTUAL suspensionMethod()Ljava/lang/Object; // actually it could be some inline method instead of plain call - * CHECKCAST Type - * ICONST_1 - * INVOKESTATIC InlineMarker.mark() - */ -private class SuspensionPoint( - // ICONST_0 - val suspensionCallBegin: AbstractInsnNode, - // INVOKESTATIC InlineMarker.mark() - val suspensionCallEnd: AbstractInsnNode -) { - lateinit var tryCatchBlocksContinuationLabel: LabelNode -} - -internal fun getLastParameterIndex(desc: String, access: Int) = - Type.getArgumentTypes(desc).dropLast(1).map { it.size }.sum() + (if (!isStatic(access)) 1 else 0) - -private fun getParameterTypesForCoroutineConstructor(desc: String, hasDispatchReceiver: Boolean, thisName: String) = - listOfNotNull(if (!hasDispatchReceiver) null else Type.getObjectType(thisName)).toTypedArray() + - Type.getArgumentTypes(desc).last() - -private fun isStatic(access: Int) = access and Opcodes.ACC_STATIC != 0 - -private fun getParameterTypesIndicesForCoroutineConstructor( - desc: String, - containingFunctionAccess: Int, - needDispatchReceiver: Boolean, - thisName: String, - languageVersionSettings: LanguageVersionSettings -): Collection> { - return mutableListOf>().apply { - if (needDispatchReceiver) { - add(Type.getObjectType(thisName) to 0) - } - val continuationIndex = - getAllParameterTypes(desc, !isStatic(containingFunctionAccess), thisName).dropLast(1).map(Type::getSize).sum() - add(languageVersionSettings.continuationAsmType() to continuationIndex) - } -} - -private fun getAllParameterTypes(desc: String, hasDispatchReceiver: Boolean, thisName: String) = - listOfNotNull(if (!hasDispatchReceiver) null else Type.getObjectType(thisName)).toTypedArray() + - Type.getArgumentTypes(desc) - -internal class IgnoringCopyOperationSourceInterpreter : SourceInterpreter(Opcodes.API_VERSION) { - override fun copyOperation(insn: AbstractInsnNode?, value: SourceValue?) = value -} - -// Check whether this instruction is unreachable, i.e. there is no path leading to this instruction -internal fun isUnreachable(index: Int, sourceFrames: Array?>): Boolean = - sourceFrames.size <= index || sourceFrames[index] == null - -private fun AbstractInsnNode?.isInvisibleInDebugVarInsn(methodNode: MethodNode): Boolean { - val insns = methodNode.instructions - val index = insns.indexOf(this) - return (this is VarInsnNode && methodNode.localVariables.none { - it.index == `var` && index in it.start.let(insns::indexOf)..it.end.let(insns::indexOf) - }) -} - -private val SAFE_OPCODES = - ((Opcodes.DUP..Opcodes.DUP2_X2) + Opcodes.NOP + Opcodes.POP + Opcodes.POP2 + (Opcodes.IFEQ..Opcodes.GOTO)).toSet() - -internal fun replaceFakeContinuationsWithRealOnes(methodNode: MethodNode, continuationIndex: Int) { - val fakeContinuations = methodNode.instructions.asSequence().filter(::isFakeContinuationMarker).toList() - for (fakeContinuation in fakeContinuations) { - methodNode.instructions.removeAll(listOf(fakeContinuation.previous.previous, fakeContinuation.previous)) - methodNode.instructions.set(fakeContinuation, VarInsnNode(Opcodes.ALOAD, continuationIndex)) - } -} diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/SourceFrames.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/SourceFrames.kt new file mode 100644 index 00000000000..9e636294533 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/SourceFrames.kt @@ -0,0 +1,11 @@ +/* + * 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.coroutines + +import org.jetbrains.org.objectweb.asm.tree.analysis.Frame +import org.jetbrains.org.objectweb.asm.tree.analysis.SourceValue + +typealias SourceFrames = Array?> \ No newline at end of file diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/SourceFrames.kt.202 b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/SourceFrames.kt.202 new file mode 100644 index 00000000000..810a5a722c0 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/SourceFrames.kt.202 @@ -0,0 +1,11 @@ +/* + * 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.coroutines + +import org.jetbrains.org.objectweb.asm.tree.analysis.Frame +import org.jetbrains.org.objectweb.asm.tree.analysis.SourceValue + +typealias SourceFrames = Array> \ No newline at end of file diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/TypeAnnotatedFrames.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/TypeAnnotatedFrames.kt new file mode 100644 index 00000000000..774af1054df --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/TypeAnnotatedFrames.kt @@ -0,0 +1,11 @@ +/* + * 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.optimization.common + +import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue +import org.jetbrains.org.objectweb.asm.tree.analysis.Frame + +typealias TypeAnnotatedFrames = Array?> \ No newline at end of file diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/TypeAnnotatedFrames.kt.202 b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/TypeAnnotatedFrames.kt.202 new file mode 100644 index 00000000000..06673d28376 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/TypeAnnotatedFrames.kt.202 @@ -0,0 +1,11 @@ +/* + * 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.optimization.common + +import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue +import org.jetbrains.org.objectweb.asm.tree.analysis.Frame + +typealias TypeAnnotatedFrames = Array> \ No newline at end of file diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/variableLiveness.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/variableLiveness.kt index 27ccd6862d1..bec1d165e68 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/variableLiveness.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/variableLiveness.kt @@ -60,7 +60,7 @@ fun analyzeLiveness(node: MethodNode): List { private fun analyzeVisibleByDebuggerVariables( node: MethodNode, - typeAnnotatedFrames: Array?> + typeAnnotatedFrames: TypeAnnotatedFrames ): Array { val res = Array(node.instructions.size()) { BitSet(node.maxLocals) } for (local in node.localVariables) { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/variableLiveness.kt.202 b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/variableLiveness.kt.202 deleted file mode 100644 index b9b0de93bb8..00000000000 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/variableLiveness.kt.202 +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2010-2019 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.codegen.optimization.common - -import org.jetbrains.kotlin.codegen.coroutines.SUSPEND_FUNCTION_COMPLETION_PARAMETER_NAME -import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer -import org.jetbrains.kotlin.load.java.JvmAbi -import org.jetbrains.org.objectweb.asm.Type -import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode -import org.jetbrains.org.objectweb.asm.tree.IincInsnNode -import org.jetbrains.org.objectweb.asm.tree.MethodNode -import org.jetbrains.org.objectweb.asm.tree.VarInsnNode -import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue -import org.jetbrains.org.objectweb.asm.tree.analysis.Frame -import java.util.* - - -class VariableLivenessFrame(val maxLocals: Int) : VarFrame { - private val bitSet = BitSet(maxLocals) - - override fun mergeFrom(other: VariableLivenessFrame) { - bitSet.or(other.bitSet) - } - - fun markAlive(varIndex: Int) { - bitSet.set(varIndex, true) - } - - fun markAllAlive(bitSet: BitSet) { - this.bitSet.or(bitSet) - } - - fun markDead(varIndex: Int) { - bitSet.set(varIndex, false) - } - - fun isAlive(varIndex: Int): Boolean = bitSet.get(varIndex) - - override fun equals(other: Any?): Boolean { - if (other !is VariableLivenessFrame) return false - return bitSet == other.bitSet - } - - override fun hashCode() = bitSet.hashCode() -} - -fun analyzeLiveness(node: MethodNode): List { - val typeAnnotatedFrames = MethodTransformer.analyze("fake", node, OptimizationBasicInterpreter()) - val visibleByDebuggerVariables = analyzeVisibleByDebuggerVariables(node, typeAnnotatedFrames) - return analyze(node, object : BackwardAnalysisInterpreter { - override fun newFrame(maxLocals: Int) = VariableLivenessFrame(maxLocals) - override fun def(frame: VariableLivenessFrame, insn: AbstractInsnNode) = defVar(frame, insn) - override fun use(frame: VariableLivenessFrame, insn: AbstractInsnNode) = - useVar(frame, insn, node, visibleByDebuggerVariables[node.instructions.indexOf(insn)]) - }) -} - -private fun analyzeVisibleByDebuggerVariables( - node: MethodNode, - typeAnnotatedFrames: Array> -): Array { - val res = Array(node.instructions.size()) { BitSet(node.maxLocals) } - for (local in node.localVariables) { - if (local.name.isInvisibleDebuggerVariable()) continue - for (index in node.instructions.indexOf(local.start) until node.instructions.indexOf(local.end)) { - if (Type.getType(local.desc).sort == typeAnnotatedFrames[index]?.getLocal(local.index)?.type?.sort) { - res[index].set(local.index) - } - } - } - return res -} - -private fun defVar(frame: VariableLivenessFrame, insn: AbstractInsnNode) { - if (insn is VarInsnNode && insn.isStoreOperation()) { - frame.markDead(insn.`var`) - } -} - -private fun useVar( - frame: VariableLivenessFrame, - insn: AbstractInsnNode, - node: MethodNode, - visibleByDebuggerVariables: BitSet -) { - frame.markAllAlive(visibleByDebuggerVariables) - - if (insn is VarInsnNode && insn.isLoadOperation()) { - frame.markAlive(insn.`var`) - } else if (insn is IincInsnNode) { - frame.markAlive(insn.`var`) - } -} - -private fun String.isInvisibleDebuggerVariable(): Boolean = - startsWith(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT) || - startsWith(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_FUNCTION) || - this == SUSPEND_FUNCTION_COMPLETION_PARAMETER_NAME diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JvmMethodExceptionTypes.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JvmMethodExceptionTypes.kt new file mode 100644 index 00000000000..ebab87ad525 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JvmMethodExceptionTypes.kt @@ -0,0 +1,8 @@ +/* + * 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.state + +typealias JvmMethodExceptionTypes = Array? \ No newline at end of file diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JvmMethodExceptionTypes.kt.202 b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JvmMethodExceptionTypes.kt.202 new file mode 100644 index 00000000000..ee6e1762506 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JvmMethodExceptionTypes.kt.202 @@ -0,0 +1,8 @@ +/* + * 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.state + +typealias JvmMethodExceptionTypes = Array? \ No newline at end of file diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/SignatureDumpingBuilderFactory.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/SignatureDumpingBuilderFactory.kt index ee1044a1070..f9446f634eb 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/SignatureDumpingBuilderFactory.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/SignatureDumpingBuilderFactory.kt @@ -83,7 +83,7 @@ class SignatureDumpingBuilderFactory( super.defineClass(origin, version, access, name, signature, superName, interfaces) } - override fun newMethod(origin: JvmDeclarationOrigin, access: Int, name: String, desc: String, signature: String?, exceptions: Array?): MethodVisitor { + override fun newMethod(origin: JvmDeclarationOrigin, access: Int, name: String, desc: String, signature: String?, exceptions: JvmMethodExceptionTypes): MethodVisitor { signatures += RawSignature(name, desc, MemberKind.METHOD) to origin.descriptor?.let { if (it is CallableDescriptor) it.unwrapInitialDescriptorForSuspendFunction() else it } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/SignatureDumpingBuilderFactory.kt.202 b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/SignatureDumpingBuilderFactory.kt.202 deleted file mode 100644 index b3981a00857..00000000000 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/SignatureDumpingBuilderFactory.kt.202 +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright 2010-2016 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. - */ - -package org.jetbrains.kotlin.codegen.state - -import com.intellij.psi.PsiElement -import org.jetbrains.kotlin.codegen.ClassBuilder -import org.jetbrains.kotlin.codegen.ClassBuilderFactory -import org.jetbrains.kotlin.codegen.DelegatingClassBuilder -import org.jetbrains.kotlin.codegen.DelegatingClassBuilderFactory -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithVisibility -import org.jetbrains.kotlin.renderer.DescriptorRenderer -import org.jetbrains.kotlin.renderer.DescriptorRendererModifier -import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin -import org.jetbrains.kotlin.resolve.jvm.diagnostics.MemberKind -import org.jetbrains.kotlin.resolve.jvm.diagnostics.RawSignature -import org.jetbrains.org.objectweb.asm.FieldVisitor -import org.jetbrains.org.objectweb.asm.MethodVisitor -import org.jetbrains.kotlin.codegen.coroutines.unwrapInitialDescriptorForSuspendFunction -import org.jetbrains.kotlin.descriptors.CallableDescriptor -import java.io.BufferedWriter -import java.io.File - - -class SignatureDumpingBuilderFactory( - builderFactory: ClassBuilderFactory, - val destination: File -) : DelegatingClassBuilderFactory(builderFactory) { - - companion object { - val MEMBER_RENDERER = DescriptorRenderer.withOptions { - withDefinedIn = false - modifiers -= DescriptorRendererModifier.VISIBILITY - } - val TYPE_RENDERER = DescriptorRenderer.withOptions { - withSourceFileForTopLevel = false - modifiers -= DescriptorRendererModifier.VISIBILITY - } - } - - private val outputStream: BufferedWriter by lazy { - // TODO: Replace with LOG.info and make log output go to MessageCollector - println("[INFO] Dumping signatures to $destination") - destination.parentFile?.mkdirs() - destination.bufferedWriter().apply { append("[\n") } - } - private var firstClassWritten: Boolean = false - - override fun close() { - outputStream.append("\n]\n") - outputStream.close() - super.close() - } - - override fun newClassBuilder(origin: JvmDeclarationOrigin): DelegatingClassBuilder { - return SignatureDumpingClassBuilder(origin, delegate.newClassBuilder(origin)) - } - - - private inner class SignatureDumpingClassBuilder(val origin: JvmDeclarationOrigin, val _delegate: ClassBuilder) : DelegatingClassBuilder() { - override fun getDelegate() = _delegate - - private val signatures = mutableListOf>() - private lateinit var javaClassName: String - - override fun defineClass(origin: PsiElement?, version: Int, access: Int, name: String, signature: String?, superName: String, interfaces: Array) { - javaClassName = name - - super.defineClass(origin, version, access, name, signature, superName, interfaces) - } - - override fun newMethod(origin: JvmDeclarationOrigin, access: Int, name: String, desc: String, signature: String?, exceptions: Array?): MethodVisitor { - signatures += RawSignature(name, desc, MemberKind.METHOD) to origin.descriptor?.let { - if (it is CallableDescriptor) it.unwrapInitialDescriptorForSuspendFunction() else it - } - return super.newMethod(origin, access, name, desc, signature, exceptions) - } - - override fun newField(origin: JvmDeclarationOrigin, access: Int, name: String, desc: String, signature: String?, value: Any?): FieldVisitor { - signatures += RawSignature(name, desc, MemberKind.FIELD) to origin.descriptor - return super.newField(origin, access, name, desc, signature, value) - } - - override fun done() { - if (firstClassWritten) outputStream.append(",\n") else firstClassWritten = true - outputStream.append("\t{\n") - origin.descriptor?.let { - outputStream.append("\t\t").appendNameValue("declaration", TYPE_RENDERER.render(it)).append(",\n") - (it as? DeclarationDescriptorWithVisibility)?.visibility?.let { - outputStream.append("\t\t").appendNameValue("visibility", it.internalDisplayName).append(",\n") - } - } - outputStream.append("\t\t").appendNameValue("class", javaClassName).append(",\n") - - outputStream.append("\t\t").appendQuoted("members").append(": [\n") - signatures.joinTo(outputStream, ",\n") { buildString { - val (signature, descriptor) = it - append("\t\t\t{") - descriptor?.let { - (it as? DeclarationDescriptorWithVisibility)?.visibility?.let { - appendNameValue("visibility", it.internalDisplayName).append(",\t") - } - appendNameValue("declaration", MEMBER_RENDERER.render(it)).append(", ") - - } - appendNameValue("name", signature.name).append(", ") - appendNameValue("desc", signature.desc).append("}") - }} - outputStream.append("\n\t\t]\n\t}") - - super.done() - } - } -} - -private fun Appendable.appendQuoted(value: String?): Appendable = value?.let { append('"').append(jsonEscape(it)).append('"') } ?: append("null") -private fun Appendable.appendNameValue(name: String, value: String?): Appendable = appendQuoted(name).append(": ").appendQuoted(value) - -private fun jsonEscape(value: String): String = buildString { - for (index in 0..value.length - 1) { - val ch = value[index] - when (ch) { - '\b' -> append("\\b") - '\t' -> append("\\t") - '\n' -> append("\\n") - '\r' -> append("\\r") - '\"' -> append("\\\"") - '\\' -> append("\\\\") - else -> if (ch.toInt() < 32) { - append("\\u" + Integer.toHexString(ch.toInt()).padStart(4, '0')) - } - else { - append(ch) - } - } - } -} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/highlighter/KotlinRecursiveCallLineMarkerProvider.kt b/idea/src/org/jetbrains/kotlin/idea/highlighter/KotlinRecursiveCallLineMarkerProvider.kt index d08c252fc83..0d5b0b629d2 100644 --- a/idea/src/org/jetbrains/kotlin/idea/highlighter/KotlinRecursiveCallLineMarkerProvider.kt +++ b/idea/src/org/jetbrains/kotlin/idea/highlighter/KotlinRecursiveCallLineMarkerProvider.kt @@ -28,6 +28,7 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor import org.jetbrains.kotlin.idea.KotlinBundle import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.highlighter.markers.LineMarkerInfos import org.jetbrains.kotlin.idea.inspections.RecursivePropertyAccessorInspection import org.jetbrains.kotlin.idea.util.getReceiverTargetDescriptor import org.jetbrains.kotlin.lexer.KtToken @@ -45,7 +46,7 @@ import java.util.* class KotlinRecursiveCallLineMarkerProvider : LineMarkerProvider { override fun getLineMarkerInfo(element: PsiElement) = null - override fun collectSlowLineMarkers(elements: MutableList, result: MutableCollection>) { + override fun collectSlowLineMarkers(elements: MutableList, result: LineMarkerInfos) { val markedLineNumbers = HashSet() for (element in elements) { diff --git a/idea/src/org/jetbrains/kotlin/idea/highlighter/KotlinRecursiveCallLineMarkerProvider.kt.202 b/idea/src/org/jetbrains/kotlin/idea/highlighter/KotlinRecursiveCallLineMarkerProvider.kt.202 index a6e776f5460..f8e64f9b4b8 100644 --- a/idea/src/org/jetbrains/kotlin/idea/highlighter/KotlinRecursiveCallLineMarkerProvider.kt.202 +++ b/idea/src/org/jetbrains/kotlin/idea/highlighter/KotlinRecursiveCallLineMarkerProvider.kt.202 @@ -28,6 +28,7 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor import org.jetbrains.kotlin.idea.KotlinBundle import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.highlighter.markers.LineMarkerInfos import org.jetbrains.kotlin.idea.inspections.RecursivePropertyAccessorInspection import org.jetbrains.kotlin.idea.util.getReceiverTargetDescriptor import org.jetbrains.kotlin.lexer.KtToken @@ -45,7 +46,7 @@ import java.util.* class KotlinRecursiveCallLineMarkerProvider : LineMarkerProvider { override fun getLineMarkerInfo(element: PsiElement) = null - override fun collectSlowLineMarkers(elements: MutableList, result: MutableCollection>) { + override fun collectSlowLineMarkers(elements: MutableList, result: LineMarkerInfos) { val markedLineNumbers = HashSet() for (element in elements) { diff --git a/idea/src/org/jetbrains/kotlin/idea/highlighter/KotlinSuspendCallLineMarkerProvider.kt b/idea/src/org/jetbrains/kotlin/idea/highlighter/KotlinSuspendCallLineMarkerProvider.kt index adee69f7706..0b31d4eeffb 100644 --- a/idea/src/org/jetbrains/kotlin/idea/highlighter/KotlinSuspendCallLineMarkerProvider.kt +++ b/idea/src/org/jetbrains/kotlin/idea/highlighter/KotlinSuspendCallLineMarkerProvider.kt @@ -19,6 +19,7 @@ import org.jetbrains.kotlin.descriptors.accessors import org.jetbrains.kotlin.idea.KotlinBundle import org.jetbrains.kotlin.idea.KotlinIcons import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.highlighter.markers.LineMarkerInfos import org.jetbrains.kotlin.idea.refactoring.getLineNumber import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.BindingContext @@ -48,7 +49,7 @@ class KotlinSuspendCallLineMarkerProvider : LineMarkerProvider { override fun collectSlowLineMarkers( elements: MutableList, - result: MutableCollection> + result: LineMarkerInfos ) { val markedLineNumbers = HashSet() diff --git a/idea/src/org/jetbrains/kotlin/idea/highlighter/KotlinSuspendCallLineMarkerProvider.kt.202 b/idea/src/org/jetbrains/kotlin/idea/highlighter/KotlinSuspendCallLineMarkerProvider.kt.202 index 24ea7867362..c121b4199cd 100644 --- a/idea/src/org/jetbrains/kotlin/idea/highlighter/KotlinSuspendCallLineMarkerProvider.kt.202 +++ b/idea/src/org/jetbrains/kotlin/idea/highlighter/KotlinSuspendCallLineMarkerProvider.kt.202 @@ -19,6 +19,7 @@ import org.jetbrains.kotlin.descriptors.accessors import org.jetbrains.kotlin.idea.KotlinBundle import org.jetbrains.kotlin.idea.KotlinIcons import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.highlighter.markers.LineMarkerInfos import org.jetbrains.kotlin.idea.refactoring.getLineNumber import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.BindingContext @@ -48,7 +49,7 @@ class KotlinSuspendCallLineMarkerProvider : LineMarkerProvider { override fun collectSlowLineMarkers( elements: MutableList, - result: MutableCollection> + result: LineMarkerInfos ) { val markedLineNumbers = HashSet() diff --git a/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/DslHighlightingMarker.kt b/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/DslHighlightingMarker.kt index 8d093ed90d7..1be09a1484b 100644 --- a/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/DslHighlightingMarker.kt +++ b/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/DslHighlightingMarker.kt @@ -37,7 +37,7 @@ private val toolTipHandler = Function { fun collectHighlightingColorsMarkers( ktClass: KtClass, - result: MutableCollection> + result: LineMarkerInfos ) { if (!KotlinLineMarkerOptions.dslOption.isEnabled) return diff --git a/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/DslHighlightingMarker.kt.202 b/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/DslHighlightingMarker.kt.202 deleted file mode 100644 index 355b4972bd1..00000000000 --- a/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/DslHighlightingMarker.kt.202 +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2000-2018 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.idea.highlighter.markers - -import com.intellij.application.options.colors.ColorAndFontOptions -import com.intellij.codeHighlighting.Pass -import com.intellij.codeInsight.daemon.GutterIconNavigationHandler -import com.intellij.codeInsight.daemon.LineMarkerInfo -import com.intellij.ide.DataManager -import com.intellij.openapi.editor.markup.GutterIconRenderer -import com.intellij.psi.PsiElement -import com.intellij.util.Function -import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.ClassKind -import org.jetbrains.kotlin.idea.KotlinLanguage -import org.jetbrains.kotlin.idea.core.toDescriptor -import org.jetbrains.kotlin.idea.KotlinBundle -import org.jetbrains.kotlin.idea.highlighter.dsl.DslHighlighterExtension -import org.jetbrains.kotlin.idea.highlighter.dsl.isDslHighlightingMarker -import org.jetbrains.kotlin.psi.KtClass -import javax.swing.JComponent - -private val navHandler = GutterIconNavigationHandler { event, element -> - val dataContext = (event.component as? JComponent)?.let { DataManager.getInstance().getDataContext(it) } - ?: return@GutterIconNavigationHandler - val ktClass = element?.parent as? KtClass ?: return@GutterIconNavigationHandler - val styleId = ktClass.styleIdForMarkerAnnotation() ?: return@GutterIconNavigationHandler - ColorAndFontOptions.selectOrEditColor(dataContext, DslHighlighterExtension.styleOptionDisplayName(styleId), KotlinLanguage.NAME) -} - -private val toolTipHandler = Function { - KotlinBundle.message("highlighter.tool.tip.marker.annotation.for.dsl") -} - -fun collectHighlightingColorsMarkers( - ktClass: KtClass, - result: MutableCollection> -) { - if (!KotlinLineMarkerOptions.dslOption.isEnabled) return - - val styleId = ktClass.styleIdForMarkerAnnotation() ?: return - - val anchor = ktClass.nameIdentifier ?: return - - result.add( - LineMarkerInfo( - anchor, - anchor.textRange, - createDslStyleIcon(styleId), - Pass.LINE_MARKERS, - toolTipHandler, navHandler, - GutterIconRenderer.Alignment.RIGHT - ) - ) -} - -private fun KtClass.styleIdForMarkerAnnotation(): Int? { - val classDescriptor = toDescriptor() as? ClassDescriptor ?: return null - if (classDescriptor.kind != ClassKind.ANNOTATION_CLASS) return null - if (!classDescriptor.isDslHighlightingMarker()) return null - return DslHighlighterExtension.styleIdByMarkerAnnotation(classDescriptor) -} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/KotlinLineMarkerProvider.kt b/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/KotlinLineMarkerProvider.kt index 23bf82722c3..efb5f708e19 100644 --- a/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/KotlinLineMarkerProvider.kt +++ b/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/KotlinLineMarkerProvider.kt @@ -83,7 +83,7 @@ class KotlinLineMarkerProvider : LineMarkerProviderDescriptor() { return info } - override fun collectSlowLineMarkers(elements: List, result: MutableCollection>) { + override fun collectSlowLineMarkers(elements: List, result: LineMarkerInfos) { if (elements.isEmpty()) return if (KotlinLineMarkerOptions.options.none { option -> option.isEnabled }) return @@ -251,7 +251,7 @@ private fun isImplementsAndNotOverrides( return descriptor.modality != Modality.ABSTRACT && overriddenMembers.all { it.modality == Modality.ABSTRACT } } -private fun collectSuperDeclarationMarkers(declaration: KtDeclaration, result: MutableCollection>) { +private fun collectSuperDeclarationMarkers(declaration: KtDeclaration, result: LineMarkerInfos) { if (!(KotlinLineMarkerOptions.implementingOption.isEnabled || KotlinLineMarkerOptions.overridingOption.isEnabled)) return assert(declaration is KtNamedFunction || declaration is KtProperty || declaration is KtParameter) @@ -287,7 +287,7 @@ private fun collectSuperDeclarationMarkers(declaration: KtDeclaration, result: M result.add(lineMarkerInfo) } -private fun collectInheritedClassMarker(element: KtClass, result: MutableCollection>) { +private fun collectInheritedClassMarker(element: KtClass, result: LineMarkerInfos) { if (!(KotlinLineMarkerOptions.implementedOption.isEnabled || KotlinLineMarkerOptions.overriddenOption.isEnabled)) return if (!element.isInheritable()) { @@ -321,7 +321,7 @@ private fun collectInheritedClassMarker(element: KtClass, result: MutableCollect private fun collectOverriddenPropertyAccessors( properties: Collection, - result: MutableCollection> + result: LineMarkerInfos ) { if (!(KotlinLineMarkerOptions.implementedOption.isEnabled || KotlinLineMarkerOptions.overriddenOption.isEnabled)) return @@ -368,7 +368,7 @@ private val KtNamedDeclaration.expectOrActualAnchor private fun collectMultiplatformMarkers( declaration: KtNamedDeclaration, - result: MutableCollection> + result: LineMarkerInfos ) { if (KotlinLineMarkerOptions.actualOption.isEnabled) { if (declaration.isExpectDeclaration()) { @@ -460,7 +460,7 @@ internal fun KtDeclaration.findMarkerBoundDeclarations(): Sequence> + result: LineMarkerInfos ) { if (!KotlinLineMarkerOptions.actualOption.isEnabled) return if (declaration.requiresNoMarkers()) return @@ -491,7 +491,7 @@ private fun collectActualMarkers( private fun collectExpectedMarkers( declaration: KtNamedDeclaration, - result: MutableCollection> + result: LineMarkerInfos ) { if (!KotlinLineMarkerOptions.expectOption.isEnabled) return @@ -520,7 +520,7 @@ private fun collectExpectedMarkers( result.add(lineMarkerInfo) } -private fun collectOverriddenFunctions(functions: Collection, result: MutableCollection>) { +private fun collectOverriddenFunctions(functions: Collection, result: LineMarkerInfos) { if (!(KotlinLineMarkerOptions.implementedOption.isEnabled || KotlinLineMarkerOptions.overriddenOption.isEnabled)) { return } diff --git a/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/KotlinLineMarkerProvider.kt.202 b/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/KotlinLineMarkerProvider.kt.202 deleted file mode 100644 index acbbc399a25..00000000000 --- a/idea/src/org/jetbrains/kotlin/idea/highlighter/markers/KotlinLineMarkerProvider.kt.202 +++ /dev/null @@ -1,561 +0,0 @@ -/* - * Copyright 2000-2018 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.idea.highlighter.markers - -import com.intellij.codeHighlighting.Pass -import com.intellij.codeInsight.daemon.* -import com.intellij.codeInsight.daemon.impl.LineMarkerNavigator -import com.intellij.codeInsight.daemon.impl.MarkerType -import com.intellij.codeInsight.daemon.impl.PsiElementListNavigator -import com.intellij.codeInsight.navigation.ListBackgroundUpdaterTask -import com.intellij.openapi.actionSystem.IdeActions -import com.intellij.openapi.editor.Document -import com.intellij.openapi.editor.colors.CodeInsightColors -import com.intellij.openapi.editor.colors.EditorColorsManager -import com.intellij.openapi.editor.markup.GutterIconRenderer -import com.intellij.openapi.editor.markup.SeparatorPlacement -import com.intellij.openapi.progress.ProgressManager -import com.intellij.openapi.project.DumbService -import com.intellij.openapi.util.text.StringUtil -import com.intellij.psi.* -import com.intellij.psi.search.searches.ClassInheritorsSearch -import com.intellij.psi.util.PsiTreeUtil -import org.jetbrains.kotlin.asJava.LightClassUtil -import org.jetbrains.kotlin.asJava.toLightClass -import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor -import org.jetbrains.kotlin.descriptors.MemberDescriptor -import org.jetbrains.kotlin.descriptors.Modality -import org.jetbrains.kotlin.idea.caches.lightClasses.KtFakeLightClass -import org.jetbrains.kotlin.idea.caches.lightClasses.KtFakeLightMethod -import org.jetbrains.kotlin.idea.caches.project.implementedDescriptors -import org.jetbrains.kotlin.idea.caches.project.implementingDescriptors -import org.jetbrains.kotlin.idea.caches.resolve.findModuleDescriptor -import org.jetbrains.kotlin.idea.core.isInheritable -import org.jetbrains.kotlin.idea.core.isOverridable -import org.jetbrains.kotlin.idea.core.toDescriptor -import org.jetbrains.kotlin.idea.editor.fixers.startLine -import org.jetbrains.kotlin.idea.KotlinBundle -import org.jetbrains.kotlin.idea.presentation.DeclarationByModuleRenderer -import org.jetbrains.kotlin.idea.search.declarationsSearch.toPossiblyFakeLightMethods -import org.jetbrains.kotlin.idea.util.* -import org.jetbrains.kotlin.lexer.KtTokens -import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject -import org.jetbrains.kotlin.psi.psiUtil.getPrevSiblingIgnoringWhitespaceAndComments -import java.awt.event.MouseEvent -import java.util.* -import javax.swing.ListCellRenderer - -class KotlinLineMarkerProvider : LineMarkerProviderDescriptor() { - override fun getName() = KotlinBundle.message("highlighter.name.kotlin.line.markers") - - override fun getOptions(): Array