org.jetbrains.kotlin.codegen.codegenUtil.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kotlin-compiler-embeddable Show documentation
Show all versions of kotlin-compiler-embeddable Show documentation
the Kotlin compiler embeddable
/*
* 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
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.UnsignedTypes
import org.jetbrains.kotlin.codegen.JvmCodegenUtil.isJvmInterface
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
import org.jetbrains.kotlin.codegen.context.CodegenContext
import org.jetbrains.kotlin.codegen.context.FieldOwnerContext
import org.jetbrains.kotlin.codegen.context.MultifileClassFacadeContext
import org.jetbrains.kotlin.codegen.coroutines.CONTINUATION_ASM_TYPE
import org.jetbrains.kotlin.codegen.coroutines.unwrapInitialDescriptorForSuspendFunction
import org.jetbrains.kotlin.codegen.inline.NUMBERED_FUNCTION_PREFIX
import org.jetbrains.kotlin.codegen.inline.ReificationArgument
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeParametersUsages
import org.jetbrains.kotlin.codegen.intrinsics.TypeIntrinsics
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.deserialization.PLATFORM_DEPENDENT_ANNOTATION_FQ_NAME
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
import org.jetbrains.kotlin.load.java.DescriptorsJvmAbiUtil
import org.jetbrains.kotlin.load.java.SpecialGenericSignatures.SpecialSignatureInfo
import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.resolve.DescriptorUtils.isSubclass
import org.jetbrains.kotlin.resolve.annotations.hasJvmStaticAnnotation
import org.jetbrains.kotlin.resolve.calls.util.getFirstArgumentExpression
import org.jetbrains.kotlin.resolve.calls.util.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.kotlin.resolve.jvm.diagnostics.Synthetic
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodGenericSignature
import org.jetbrains.kotlin.resolve.scopes.receivers.TransientReceiver
import org.jetbrains.kotlin.types.error.ErrorUtils
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeSystemCommonBackendContext
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
import org.jetbrains.kotlin.types.model.TypeParameterMarker
import org.jetbrains.kotlin.utils.DFS
import org.jetbrains.org.objectweb.asm.AnnotationVisitor
import org.jetbrains.org.objectweb.asm.Label
import org.jetbrains.org.objectweb.asm.Opcodes.*
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
import org.jetbrains.org.objectweb.asm.commons.Method
import org.jetbrains.org.objectweb.asm.tree.LabelNode
import java.lang.Deprecated
import java.util.*
@JvmField
internal val JAVA_LANG_DEPRECATED = Type.getType(Deprecated::class.java).descriptor
fun generateIsCheck(
v: InstructionAdapter,
kotlinType: KotlinType,
asmType: Type
) {
if (TypeUtils.isNullableType(kotlinType)) {
val nope = Label()
val end = Label()
with(v) {
dup()
ifnull(nope)
TypeIntrinsics.instanceOf(this, kotlinType, asmType)
goTo(end)
mark(nope)
pop()
iconst(1)
mark(end)
}
} else {
TypeIntrinsics.instanceOf(v, kotlinType, asmType)
}
}
fun generateAsCast(
v: InstructionAdapter,
kotlinType: KotlinType,
asmType: Type,
isSafe: Boolean,
unifiedNullChecks: Boolean,
) {
if (!isSafe) {
if (!TypeUtils.isNullableType(kotlinType)) {
generateNullCheckForNonSafeAs(v, kotlinType, unifiedNullChecks)
}
} else {
with(v) {
dup()
TypeIntrinsics.instanceOf(v, kotlinType, asmType)
val ok = Label()
ifne(ok)
pop()
aconst(null)
mark(ok)
}
}
TypeIntrinsics.checkcast(v, kotlinType, asmType, isSafe)
}
private fun generateNullCheckForNonSafeAs(
v: InstructionAdapter,
type: KotlinType,
unifiedNullChecks: Boolean,
) {
with(v) {
dup()
val nonnull = Label()
ifnonnull(nonnull)
val exceptionClass = if (unifiedNullChecks) "java/lang/NullPointerException" else "kotlin/TypeCastException"
AsmUtil.genThrow(
v,
exceptionClass,
"null cannot be cast to non-null type " + DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(type)
)
mark(nonnull)
}
}
fun SpecialSignatureInfo.replaceValueParametersIn(sourceSignature: String?): String? =
valueParametersSignature?.let { sourceSignature?.replace("^\\(.*\\)".toRegex(), "($it)") }
fun populateCompanionBackingFieldNamesToOuterContextIfNeeded(
companion: KtObjectDeclaration,
outerContext: FieldOwnerContext<*>,
state: GenerationState
) {
val descriptor = state.bindingContext.get(BindingContext.CLASS, companion)
if (descriptor == null || ErrorUtils.isError(descriptor)) {
return
}
if (!DescriptorsJvmAbiUtil.isClassCompanionObjectWithBackingFieldsInOuter(descriptor)) {
return
}
val properties = companion.declarations.filterIsInstance()
properties.forEach {
val variableDescriptor = state.bindingContext.get(BindingContext.VARIABLE, it)
if (variableDescriptor is PropertyDescriptor) {
outerContext.getFieldName(variableDescriptor, it.hasDelegate())
}
}
}
// Top level subclasses of a sealed class should be generated before that sealed class,
// so that we'd generate the necessary accessor for its constructor afterwards
fun sortTopLevelClassesAndPrepareContextForSealedClasses(
classOrObjects: List,
context: CodegenContext<*>,
state: GenerationState
): List {
fun prepareContextIfNeeded(descriptor: ClassDescriptor?) {
if (DescriptorUtils.isSealedClass(descriptor)) {
// save context for sealed class
context.intoClass(descriptor!!, OwnerKind.IMPLEMENTATION, state)
}
}
// optimization
when (classOrObjects.size) {
0 -> return emptyList()
1 -> {
prepareContextIfNeeded(state.bindingContext.get(BindingContext.CLASS, classOrObjects.first()))
return classOrObjects
}
}
val result = ArrayList(classOrObjects.size)
val descriptorToPsi = LinkedHashMap()
for (classOrObject in classOrObjects) {
val descriptor = state.bindingContext.get(BindingContext.CLASS, classOrObject)
if (descriptor == null) {
result.add(classOrObject)
} else {
prepareContextIfNeeded(descriptor)
descriptorToPsi[descriptor] = classOrObject
}
}
// topologicalOrder(listOf(1, 2, 3)) { emptyList() } = listOf(3, 2, 1). Because of this used keys.reversed().
val sortedDescriptors = DFS.topologicalOrder(descriptorToPsi.keys.reversed()) { descriptor ->
descriptor.typeConstructor.supertypes
.map { it.constructor.declarationDescriptor as? ClassDescriptor }
.filter { it in descriptorToPsi.keys }
}
sortedDescriptors.mapTo(result) { descriptorToPsi[it]!! }
return result
}
fun CallableMemberDescriptor.isDefinitelyNotDefaultImplsMethod() =
this is JavaCallableMemberDescriptor || this.annotations.hasAnnotation(PLATFORM_DEPENDENT_ANNOTATION_FQ_NAME)
fun ClassBuilder.generateMethod(
debugString: String,
access: Int,
method: Method,
element: PsiElement?,
origin: JvmDeclarationOrigin,
state: GenerationState,
generate: InstructionAdapter.() -> Unit
) {
val mv = this.newMethod(origin, access, method.name, method.descriptor, null, null)
if (state.classBuilderMode.generateBodies) {
val iv = InstructionAdapter(mv)
iv.visitCode()
iv.generate()
iv.areturn(method.returnType)
FunctionCodegen.endVisit(mv, debugString, element)
}
}
fun CallableDescriptor.isJvmStaticInObjectOrClassOrInterface(): Boolean =
isJvmStaticIn {
DescriptorUtils.isNonCompanionObject(it) ||
// This is necessary because for generation of @JvmStatic methods from companion of class A
// we create a synthesized descriptor containing in class A
DescriptorUtils.isClassOrEnumClass(it) || isJvmInterface(it)
}
fun CallableDescriptor.isJvmStaticInCompanionObject(): Boolean =
isJvmStaticIn { DescriptorUtils.isCompanionObject(it) }
fun CallableDescriptor.isJvmStaticInInlineClass(): Boolean =
isJvmStaticIn { it.isInlineClass() }
private fun CallableDescriptor.isJvmStaticIn(predicate: (DeclarationDescriptor) -> Boolean): Boolean =
when (this) {
is PropertyAccessorDescriptor -> {
val propertyDescriptor = correspondingProperty
predicate(propertyDescriptor.containingDeclaration) &&
(hasJvmStaticAnnotation() || propertyDescriptor.hasJvmStaticAnnotation())
}
else -> predicate(containingDeclaration) && hasJvmStaticAnnotation()
}
fun Collection.filterOutDescriptorsWithSpecialNames() = filterNot { it.name.isSpecial }
class JvmKotlinType(val type: Type, val kotlinType: KotlinType? = null)
fun KotlinType.asmType(typeMapper: KotlinTypeMapper) = typeMapper.mapType(this)
fun KtExpression?.asmType(typeMapper: KotlinTypeMapper, bindingContext: BindingContext): Type =
this.kotlinType(bindingContext)?.asmType(typeMapper) ?: Type.VOID_TYPE
fun KtExpression?.kotlinType(bindingContext: BindingContext) = this?.let(bindingContext::getType)
fun Collection.withVariableIndices(): List> = mutableListOf>().apply {
var index = 0
for (type in this@withVariableIndices) {
add(index to type)
index += type.size
}
}
fun FunctionDescriptor.isGenericToArray(): Boolean {
if (name.asString() != "toArray") return false
if (valueParameters.size != 1 || typeParameters.size != 1) return false
val returnType = returnType ?: throw AssertionError(toString())
val paramType = valueParameters[0].type
if (!KotlinBuiltIns.isArray(returnType) || !KotlinBuiltIns.isArray(paramType)) return false
val elementType = typeParameters[0].defaultType
return KotlinTypeChecker.DEFAULT.equalTypes(elementType, builtIns.getArrayElementType(returnType)) &&
KotlinTypeChecker.DEFAULT.equalTypes(elementType, builtIns.getArrayElementType(paramType))
}
fun FunctionDescriptor.isNonGenericToArray(): Boolean {
if (name.asString() != "toArray") return false
if (valueParameters.isNotEmpty() || typeParameters.isNotEmpty()) return false
val returnType = returnType
return returnType != null && KotlinBuiltIns.isArray(returnType)
}
fun MemberDescriptor.isToArrayFromCollection(): Boolean {
if (this !is FunctionDescriptor) return false
val containingClassDescriptor = containingDeclaration as? ClassDescriptor ?: return false
if (containingClassDescriptor.source == SourceElement.NO_SOURCE) return false
val collectionClass = builtIns.collection
if (!isSubclass(containingClassDescriptor, collectionClass)) return false
return isGenericToArray() || isNonGenericToArray()
}
val CallableDescriptor.arity: Int
get() = valueParameters.size +
(if (extensionReceiverParameter != null) 1 else 0) +
(if (dispatchReceiverParameter != null) 1 else 0)
fun FqName.topLevelClassInternalName() = JvmClassName.internalNameByClassId(ClassId(parent(), shortName()))
fun FqName.topLevelClassAsmType(): Type = Type.getObjectType(topLevelClassInternalName())
fun initializeVariablesForDestructuredLambdaParameters(codegen: ExpressionCodegen, valueParameters: List, endLabel: Label?) {
// Do not write line numbers until destructuring happens
// (otherwise destructuring variables will be uninitialized in the beginning of lambda)
codegen.runWithShouldMarkLineNumbers(false) {
for (parameterDescriptor in valueParameters) {
if (parameterDescriptor !is ValueParameterDescriptorImpl.WithDestructuringDeclaration) continue
val variables = parameterDescriptor.destructuringVariables.filterOutDescriptorsWithSpecialNames()
val indices = variables.map {
codegen.myFrameMap.enter(it, codegen.typeMapper.mapType(it.type))
}
val destructuringDeclaration =
(DescriptorToSourceUtils.descriptorToDeclaration(parameterDescriptor) as? KtParameter)?.destructuringDeclaration
?: error("Destructuring declaration for descriptor $parameterDescriptor not found")
codegen.initializeDestructuringDeclarationVariables(
destructuringDeclaration,
TransientReceiver(parameterDescriptor.type),
codegen.findLocalOrCapturedValue(parameterDescriptor) ?: error("Local var not found for parameter $parameterDescriptor")
)
if (endLabel != null) {
val label = Label()
codegen.v.mark(label)
for ((index, entry) in indices.zip(variables)) {
codegen.v.visitLocalVariable(
entry.name.asString(),
codegen.typeMapper.mapType(entry.type).descriptor,
null,
label,
endLabel,
index
)
}
}
}
}
}
fun D.unwrapFrontendVersion() = unwrapInitialDescriptorForSuspendFunction()
inline fun FrameMap.useTmpVar(type: Type, block: (index: Int) -> Unit) {
val index = enterTemp(type)
block(index)
leaveTemp(type)
}
fun InstructionAdapter.generateNewInstanceDupAndPlaceBeforeStackTop(
frameMap: FrameMap,
topStackType: Type,
newInstanceInternalName: String
) {
frameMap.useTmpVar(topStackType) { index ->
store(index, topStackType)
anew(Type.getObjectType(newInstanceInternalName))
dup()
load(index, topStackType)
}
}
fun TypeSystemCommonBackendContext.extractReificationArgument(initialType: KotlinTypeMarker): Pair? {
var type = initialType
var arrayDepth = 0
val isNullable = type.isMarkedNullable()
while (type.isArrayOrNullableArray()) {
arrayDepth++
// TODO: warn that nullability info on argument will be lost?
val argument = type.getArgument(0)
if (argument.isStarProjection()) return null
type = argument.getType()
}
val typeParameter = type.typeConstructor().getTypeParameterClassifier() ?: return null
if (!typeParameter.isReified()) return null
return Pair(typeParameter, ReificationArgument(typeParameter.getName().asString(), isNullable, arrayDepth))
}
fun TypeSystemCommonBackendContext.extractUsedReifiedParameters(type: KotlinTypeMarker): ReifiedTypeParametersUsages =
ReifiedTypeParametersUsages().apply {
fun KotlinTypeMarker.visit() {
val typeParameter = typeConstructor().getTypeParameterClassifier()
if (typeParameter == null) {
for (argument in getArguments()) {
if (!argument.isStarProjection()) {
argument.getType().visit()
}
}
} else if (typeParameter.isReified()) {
addUsedReifiedParameter(typeParameter.getName().asString())
}
}
type.visit()
}
fun unwrapInitialSignatureDescriptor(function: FunctionDescriptor): FunctionDescriptor =
function.initialSignatureDescriptor ?: function
fun ExpressionCodegen.generateCallReceiver(call: ResolvedCall): StackValue =
generateReceiverValue(call.extensionReceiver ?: call.dispatchReceiver!!, false)
fun ExpressionCodegen.generateCallSingleArgument(call: ResolvedCall): StackValue =
gen(call.getFirstArgumentExpression()!!)
fun ClassDescriptor.isPossiblyUninitializedSingleton() =
DescriptorUtils.isEnumEntry(this) ||
DescriptorUtils.isCompanionObject(this) && JvmCodegenUtil.isJvmInterface(this.containingDeclaration)
inline fun FrameMap.evaluateOnce(
value: StackValue,
asType: Type,
v: InstructionAdapter,
body: (StackValue) -> Unit
) {
val valueOrTmp: StackValue =
if (value.canHaveSideEffects())
StackValue.local(enterTemp(asType), asType).apply { store(value, v) }
else
value
body(valueOrTmp)
if (valueOrTmp != value) {
leaveTemp(asType)
}
}
fun KotlinType.isInlineClassTypeWithPrimitiveEquality(): Boolean {
if (!isInlineClassType()) return false
// Always treat unsigned types as inline classes with primitive equality
if (UnsignedTypes.isUnsignedType(this)) return true
// TODO support other inline classes that can be compared as underlying primitives
return false
}
fun recordCallLabelForLambdaArgument(declaration: KtFunctionLiteral, bindingTrace: BindingTrace) {
val labelName = getCallLabelForLambdaArgument(declaration, bindingTrace.bindingContext) ?: return
val functionDescriptor = bindingTrace[BindingContext.FUNCTION, declaration] ?: return
bindingTrace.record(CodegenBinding.CALL_LABEL_FOR_LAMBDA_ARGUMENT, functionDescriptor, labelName)
}
fun getCallLabelForLambdaArgument(declaration: KtFunctionLiteral, bindingContext: BindingContext): String? {
val lambdaExpression = declaration.parent as? KtLambdaExpression ?: return null
val lambdaExpressionParent = lambdaExpression.parent
if (lambdaExpressionParent is KtLabeledExpression) {
lambdaExpressionParent.name?.let { return it }
}
val callExpression = when (val argument = lambdaExpression.parent) {
is KtLambdaArgument -> {
argument.parent as? KtCallExpression ?: return null
}
is KtValueArgument -> {
val valueArgumentList = argument.parent as? KtValueArgumentList ?: return null
valueArgumentList.parent as? KtCallExpression ?: return null
}
else -> return null
}
val call = callExpression.getResolvedCall(bindingContext) ?: return null
return call.resultingDescriptor.name.asString()
}
private val ARRAY_OF_STRINGS_TYPE = Type.getType("[Ljava/lang/String;")
private val METHOD_DESCRIPTOR_FOR_MAIN = Type.getMethodDescriptor(Type.VOID_TYPE, ARRAY_OF_STRINGS_TYPE)
fun generateBridgeForMainFunctionIfNecessary(
state: GenerationState,
packagePartClassBuilder: ClassBuilder,
functionDescriptor: FunctionDescriptor,
signatureOfRealDeclaration: JvmMethodGenericSignature,
origin: JvmDeclarationOrigin,
parentContext: CodegenContext<*>
) {
val originElement = origin.element ?: return
if (functionDescriptor.name.asString() != "main" || !DescriptorUtils.isTopLevelDeclaration(functionDescriptor)) return
val unwrappedFunctionDescriptor = functionDescriptor.unwrapInitialDescriptorForSuspendFunction()
val isParameterless =
unwrappedFunctionDescriptor.extensionReceiverParameter == null && unwrappedFunctionDescriptor.valueParameters.isEmpty()
if (!functionDescriptor.isSuspend && !isParameterless) return
if (!state.mainFunctionDetector.isMain(unwrappedFunctionDescriptor, checkJvmStaticAnnotation = false, checkReturnType = true)) return
val bridgeMethodVisitor = packagePartClassBuilder.newMethod(
Synthetic(originElement, functionDescriptor),
ACC_PUBLIC or ACC_STATIC or ACC_SYNTHETIC,
"main",
METHOD_DESCRIPTOR_FOR_MAIN, null, null
)
if (parentContext is MultifileClassFacadeContext) {
bridgeMethodVisitor.visitCode()
FunctionCodegen.generateFacadeDelegateMethodBody(
bridgeMethodVisitor,
Method("main", METHOD_DESCRIPTOR_FOR_MAIN),
parentContext
)
bridgeMethodVisitor.visitEnd()
return
}
val lambdaInternalName =
if (functionDescriptor.isSuspend)
generateLambdaForRunSuspend(
state,
originElement,
packagePartClassBuilder.thisName,
signatureOfRealDeclaration,
isParameterless
)
else
null
bridgeMethodVisitor.apply {
visitCode()
if (lambdaInternalName != null) {
visitTypeInsn(NEW, lambdaInternalName)
visitInsn(DUP)
visitVarInsn(ALOAD, 0)
visitMethodInsn(
INVOKESPECIAL,
lambdaInternalName,
"",
METHOD_DESCRIPTOR_FOR_MAIN,
false
)
visitMethodInsn(
INVOKESTATIC,
"kotlin/coroutines/jvm/internal/RunSuspendKt", "runSuspend",
Type.getMethodDescriptor(
Type.VOID_TYPE,
Type.getObjectType(NUMBERED_FUNCTION_PREFIX + "1")
),
false
)
} else {
visitMethodInsn(
INVOKESTATIC,
packagePartClassBuilder.thisName, "main",
Type.getMethodDescriptor(
Type.VOID_TYPE
),
false
)
}
visitInsn(RETURN)
visitEnd()
}
}
private fun generateLambdaForRunSuspend(
state: GenerationState,
originElement: PsiElement,
packagePartClassInternalName: String,
signatureOfRealDeclaration: JvmMethodGenericSignature,
parameterless: Boolean
): String {
val internalName = "$packagePartClassInternalName$$\$main"
val lambdaBuilder = state.factory.newVisitor(
JvmDeclarationOrigin.NO_ORIGIN,
Type.getObjectType(internalName),
originElement.containingFile
)
lambdaBuilder.defineClass(
originElement, state.config.classFileVersion,
ACC_FINAL or ACC_SUPER or ACC_SYNTHETIC,
internalName, null,
AsmTypes.LAMBDA.internalName,
arrayOf(NUMBERED_FUNCTION_PREFIX + "1")
)
lambdaBuilder.newField(
JvmDeclarationOrigin.NO_ORIGIN,
ACC_PRIVATE or ACC_FINAL or ACC_SYNTHETIC,
"args",
ARRAY_OF_STRINGS_TYPE.descriptor, null, null
)
lambdaBuilder.newMethod(
JvmDeclarationOrigin.NO_ORIGIN,
AsmUtil.NO_FLAG_PACKAGE_PRIVATE or ACC_SYNTHETIC,
"",
METHOD_DESCRIPTOR_FOR_MAIN, null, null
).apply {
visitCode()
visitVarInsn(ALOAD, 0)
visitVarInsn(ALOAD, 1)
visitFieldInsn(
PUTFIELD,
lambdaBuilder.thisName,
"args",
ARRAY_OF_STRINGS_TYPE.descriptor
)
visitVarInsn(ALOAD, 0)
visitInsn(ICONST_1)
visitMethodInsn(
INVOKESPECIAL,
AsmTypes.LAMBDA.internalName,
"",
Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE),
false
)
visitInsn(RETURN)
visitEnd()
}
lambdaBuilder.newMethod(
JvmDeclarationOrigin.NO_ORIGIN,
ACC_PUBLIC or ACC_FINAL or ACC_SYNTHETIC,
"invoke",
Type.getMethodDescriptor(AsmTypes.OBJECT_TYPE, AsmTypes.OBJECT_TYPE), null, null
).apply {
visitCode()
if (!parameterless) {
// Actually, the field for arguments may also be removed in case of parameterless main,
// but probably it'd much easier when IR is ready
visitVarInsn(ALOAD, 0)
visitFieldInsn(
GETFIELD,
lambdaBuilder.thisName,
"args",
ARRAY_OF_STRINGS_TYPE.descriptor
)
}
visitVarInsn(ALOAD, 1)
val continuationInternalName = CONTINUATION_ASM_TYPE.internalName
visitTypeInsn(
CHECKCAST,
continuationInternalName
)
visitMethodInsn(
INVOKESTATIC,
packagePartClassInternalName,
signatureOfRealDeclaration.asmMethod.name,
signatureOfRealDeclaration.asmMethod.descriptor,
false
)
visitInsn(ARETURN)
visitEnd()
}
writeSyntheticClassMetadata(lambdaBuilder, state.config, false)
lambdaBuilder.done(state.config.generateSmapCopyToAnnotation)
return lambdaBuilder.thisName
}
internal fun LabelNode.linkWithLabel(): LabelNode {
// Remember labelNode in label and vise versa.
// Before ASM 8 there was JB patch in MethodNode that makes such linking in constructor of LabelNode.
//
// protected LabelNode getLabelNode(final Label label) {
// if (!(label.info instanceof LabelNode)) {
// //label.info = new LabelNode(label); //[JB: needed for Coverage agent]
// label.info = new LabelNode(); //ASM 8
// }
// return (LabelNode) label.info;
// }
if (label.info == null) {
label.info = this
}
return this
}
fun linkedLabel(): Label = LabelNode().linkWithLabel().label
// Strings in constant pool contain at most 2^16-1 = 65535 bytes.
const val STRING_UTF8_ENCODING_BYTE_LIMIT: Int = 65535
//Each CHAR could be encoded maximum in 3 bytes
fun String.isDefinitelyFitEncodingLimit() = length <= STRING_UTF8_ENCODING_BYTE_LIMIT / 3
fun splitStringConstant(value: String): List {
return if (value.isDefinitelyFitEncodingLimit()) {
listOf(value)
} else {
val result = arrayListOf()
// Split strings into parts, each of which satisfies JVM class file constant pool constraints.
// Note that even if we split surrogate pairs between parts, they will be joined on concatenation.
var accumulatedSize = 0
var charOffsetInString = 0
var lastStringBeginning = 0
val length = value.length
while (charOffsetInString < length) {
val charCode = value[charOffsetInString].code
val encodedCharSize = when {
charCode in 1..127 -> 1
charCode <= 2047 -> 2
else -> 3
}
if (accumulatedSize + encodedCharSize > STRING_UTF8_ENCODING_BYTE_LIMIT) {
result.add(value.substring(lastStringBeginning, charOffsetInString))
lastStringBeginning = charOffsetInString
accumulatedSize = 0
}
accumulatedSize += encodedCharSize
++charOffsetInString
}
result.add(value.substring(lastStringBeginning, charOffsetInString))
result
}
}
fun AnnotationVisitor.visitWithSplitting(name: String?, value: String) {
val av = visitArray(name)
for (part in splitStringConstant(value)) {
av.visit(null, part)
}
av.visitEnd()
}
fun String.encodedUTF8Size(): Int {
var result = 0
for (char in this) {
val charCode = char.code
when {
charCode in 1..127 -> result++
charCode <= 2047 -> result += 2
else -> result += 3
}
}
return result
}