From f866eff93ea2367c967a057b1f84e480c846f2d7 Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Wed, 14 Oct 2020 11:52:06 +0300 Subject: [PATCH] Extract special generic signatures to compiler.common.jvm to reuse in FIR --- .../jetbrains/kotlin/codegen/codegenUtil.kt | 2 +- .../jvm/codegen/MethodSignatureMapper.kt | 4 +- .../load/java/SpecialGenericSignatures.kt | 136 ++++++++++++++++++ .../kotlin/load/java/specialBuiltinMembers.kt | 126 +--------------- .../caches/lightClasses/platformWrappers.kt | 6 +- 5 files changed, 146 insertions(+), 128 deletions(-) create mode 100644 core/compiler.common.jvm/src/org/jetbrains/kotlin/load/java/SpecialGenericSignatures.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt index 32c92d1800a..88dcc1a093e 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt @@ -24,7 +24,7 @@ import org.jetbrains.kotlin.config.isReleaseCoroutines 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.BuiltinMethodsWithSpecialGenericSignature.SpecialSignatureInfo +import org.jetbrains.kotlin.load.java.SpecialGenericSignatures.SpecialSignatureInfo import org.jetbrains.kotlin.load.java.DescriptorsJvmAbiUtil import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor import org.jetbrains.kotlin.name.ClassId diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/MethodSignatureMapper.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/MethodSignatureMapper.kt index 94edc2b5a5e..216d93e55bd 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/MethodSignatureMapper.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/MethodSignatureMapper.kt @@ -410,13 +410,13 @@ class MethodSignatureMapper(private val context: JvmBackendContext) { // From BuiltinMethodsWithDifferentJvmName.isBuiltinFunctionWithDifferentNameInJvm, BuiltinMethodsWithDifferentJvmName.getJvmName private fun IrSimpleFunction.getDifferentNameForJvmBuiltinFunction(): String? { - if (name !in BuiltinMethodsWithDifferentJvmName.ORIGINAL_SHORT_NAMES) return null + if (name !in SpecialGenericSignatures.ORIGINAL_SHORT_NAMES) return null if (!isBuiltIn) return null return allOverridden(includeSelf = true) .filter { it.isBuiltIn } .mapNotNull { val signature = it.computeJvmSignature() - BuiltinMethodsWithDifferentJvmName.SIGNATURE_TO_JVM_REPRESENTATION_NAME[signature]?.asString() + SpecialGenericSignatures.SIGNATURE_TO_JVM_REPRESENTATION_NAME[signature]?.asString() } .firstOrNull() } diff --git a/core/compiler.common.jvm/src/org/jetbrains/kotlin/load/java/SpecialGenericSignatures.kt b/core/compiler.common.jvm/src/org/jetbrains/kotlin/load/java/SpecialGenericSignatures.kt new file mode 100644 index 00000000000..9122e9113c7 --- /dev/null +++ b/core/compiler.common.jvm/src/org/jetbrains/kotlin/load/java/SpecialGenericSignatures.kt @@ -0,0 +1,136 @@ +/* + * 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.load.java + +import org.jetbrains.kotlin.load.kotlin.SignatureBuildingComponents +import org.jetbrains.kotlin.load.kotlin.signatures +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType + + +open class SpecialGenericSignatures { + enum class TypeSafeBarrierDescription(val defaultValue: Any?) { + NULL(null), INDEX(-1), FALSE(false), + + MAP_GET_OR_DEFAULT(null) { + override fun checkParameter(index: Int) = index != 1 + }; + + open fun checkParameter(index: Int) = true + } + + enum class SpecialSignatureInfo(val valueParametersSignature: String?, val isObjectReplacedWithTypeParameter: Boolean) { + ONE_COLLECTION_PARAMETER("Ljava/util/Collection<+Ljava/lang/Object;>;", false), + OBJECT_PARAMETER_NON_GENERIC(null, true), + OBJECT_PARAMETER_GENERIC("Ljava/lang/Object;", true) + } + + companion object { + fun getSpecialSignatureInfo(builtinSignature: String): SpecialSignatureInfo { + if (builtinSignature in ERASED_COLLECTION_PARAMETER_SIGNATURES) return SpecialSignatureInfo.ONE_COLLECTION_PARAMETER + + val defaultValue = SIGNATURE_TO_DEFAULT_VALUES_MAP.getValue(builtinSignature) + + return if (defaultValue == TypeSafeBarrierDescription.NULL) { + // return type is some generic type as 'Map.get' + SpecialSignatureInfo.OBJECT_PARAMETER_GENERIC + } else + SpecialSignatureInfo.OBJECT_PARAMETER_NON_GENERIC + } + + data class NameAndSignature internal constructor(val name: Name, val signature: String) + + private fun String.method(name: String, parameters: String, returnType: String) = + NameAndSignature( + Name.identifier(name), + SignatureBuildingComponents.signature(this@method, "$name($parameters)$returnType") + ) + + private val ERASED_COLLECTION_PARAMETER_NAME_AND_SIGNATURES = setOf( + "containsAll", "removeAll", "retainAll" + ).map { "java/util/Collection".method(it, "Ljava/util/Collection;", JvmPrimitiveType.BOOLEAN.desc) } + + val ERASED_COLLECTION_PARAMETER_SIGNATURES = ERASED_COLLECTION_PARAMETER_NAME_AND_SIGNATURES.map { it.signature } + val ERASED_COLLECTION_PARAMETER_NAMES = ERASED_COLLECTION_PARAMETER_NAME_AND_SIGNATURES.map { it.name.asString() } + + private val GENERIC_PARAMETERS_METHODS_TO_DEFAULT_VALUES_MAP = + signatures { + mapOf( + javaUtil("Collection") + .method("contains", "Ljava/lang/Object;", JvmPrimitiveType.BOOLEAN.desc) to TypeSafeBarrierDescription.FALSE, + javaUtil("Collection") + .method("remove", "Ljava/lang/Object;", JvmPrimitiveType.BOOLEAN.desc) to TypeSafeBarrierDescription.FALSE, + + javaUtil("Map") + .method("containsKey", "Ljava/lang/Object;", JvmPrimitiveType.BOOLEAN.desc) to TypeSafeBarrierDescription.FALSE, + javaUtil("Map") + .method("containsValue", "Ljava/lang/Object;", JvmPrimitiveType.BOOLEAN.desc) to TypeSafeBarrierDescription.FALSE, + javaUtil("Map") + .method( + "remove", "Ljava/lang/Object;Ljava/lang/Object;", + JvmPrimitiveType.BOOLEAN.desc + ) to TypeSafeBarrierDescription.FALSE, + + javaUtil("Map") + .method( + "getOrDefault", "Ljava/lang/Object;Ljava/lang/Object;", + "Ljava/lang/Object;" + ) to TypeSafeBarrierDescription.MAP_GET_OR_DEFAULT, + + javaUtil("Map") + .method("get", "Ljava/lang/Object;", "Ljava/lang/Object;") to TypeSafeBarrierDescription.NULL, + javaUtil("Map") + .method("remove", "Ljava/lang/Object;", "Ljava/lang/Object;") to TypeSafeBarrierDescription.NULL, + + javaUtil("List") + .method("indexOf", "Ljava/lang/Object;", JvmPrimitiveType.INT.desc) to TypeSafeBarrierDescription.INDEX, + javaUtil("List") + .method("lastIndexOf", "Ljava/lang/Object;", JvmPrimitiveType.INT.desc) to TypeSafeBarrierDescription.INDEX + ) + } + + val SIGNATURE_TO_DEFAULT_VALUES_MAP = GENERIC_PARAMETERS_METHODS_TO_DEFAULT_VALUES_MAP.mapKeys { it.key.signature } + val ERASED_VALUE_PARAMETERS_SHORT_NAMES: Set + val ERASED_VALUE_PARAMETERS_SIGNATURES: Set + + init { + val allMethods = GENERIC_PARAMETERS_METHODS_TO_DEFAULT_VALUES_MAP.keys + ERASED_COLLECTION_PARAMETER_NAME_AND_SIGNATURES + ERASED_VALUE_PARAMETERS_SHORT_NAMES = allMethods.map { it.name }.toSet() + ERASED_VALUE_PARAMETERS_SIGNATURES = allMethods.map { it.signature }.toSet() + } + + // Note that signatures here are not real, + // e.g. 'java/lang/CharSequence.get(I)C' does not actually exist in JDK + // But it doesn't matter here, because signatures are only used to distinguish overloaded built-in definitions + val REMOVE_AT_NAME_AND_SIGNATURE = + "java/util/List".method("removeAt", JvmPrimitiveType.INT.desc, "Ljava/lang/Object;") + + private val NAME_AND_SIGNATURE_TO_JVM_REPRESENTATION_NAME_MAP: Map = signatures { + mapOf( + javaLang("Number").method("toByte", "", JvmPrimitiveType.BYTE.desc) to Name.identifier("byteValue"), + javaLang("Number").method("toShort", "", JvmPrimitiveType.SHORT.desc) to Name.identifier("shortValue"), + javaLang("Number").method("toInt", "", JvmPrimitiveType.INT.desc) to Name.identifier("intValue"), + javaLang("Number").method("toLong", "", JvmPrimitiveType.LONG.desc) to Name.identifier("longValue"), + javaLang("Number").method("toFloat", "", JvmPrimitiveType.FLOAT.desc) to Name.identifier("floatValue"), + javaLang("Number").method("toDouble", "", JvmPrimitiveType.DOUBLE.desc) to Name.identifier("doubleValue"), + REMOVE_AT_NAME_AND_SIGNATURE to Name.identifier("remove"), + javaLang("CharSequence") + .method("get", JvmPrimitiveType.INT.desc, JvmPrimitiveType.CHAR.desc) to Name.identifier("charAt") + ) + } + + val SIGNATURE_TO_JVM_REPRESENTATION_NAME: Map = + NAME_AND_SIGNATURE_TO_JVM_REPRESENTATION_NAME_MAP.mapKeys { it.key.signature } + + val ORIGINAL_SHORT_NAMES: List = NAME_AND_SIGNATURE_TO_JVM_REPRESENTATION_NAME_MAP.keys.map { it.name } + + val JVM_SHORT_NAME_TO_BUILTIN_SHORT_NAMES_MAP: Map> = + NAME_AND_SIGNATURE_TO_JVM_REPRESENTATION_NAME_MAP.entries + .map { Pair(it.key.name, it.value) } + .groupBy({ it.second }, { it.first }) + + } +} \ No newline at end of file diff --git a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/specialBuiltinMembers.kt b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/specialBuiltinMembers.kt index 3a5fe973253..e6bcc318b23 100644 --- a/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/specialBuiltinMembers.kt +++ b/core/descriptors.jvm/src/org/jetbrains/kotlin/load/java/specialBuiltinMembers.kt @@ -24,88 +24,14 @@ import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature. import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature.sameAsBuiltinMethodWithErasedValueParameters import org.jetbrains.kotlin.load.java.ClassicBuiltinSpecialProperties.getBuiltinSpecialPropertyGetterName import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor -import org.jetbrains.kotlin.load.kotlin.SignatureBuildingComponents import org.jetbrains.kotlin.load.kotlin.computeJvmSignature -import org.jetbrains.kotlin.load.kotlin.signatures import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.descriptorUtil.firstOverridden import org.jetbrains.kotlin.resolve.descriptorUtil.propertyIfAccessor -import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType import org.jetbrains.kotlin.types.checker.TypeCheckingProcedure -private data class NameAndSignature(val name: Name, val signature: String) - -private fun String.method(name: String, parameters: String, returnType: String) = - NameAndSignature( - Name.identifier(name), - SignatureBuildingComponents.signature(this@method, "$name($parameters)$returnType") - ) - -object BuiltinMethodsWithSpecialGenericSignature { - private val ERASED_COLLECTION_PARAMETER_NAME_AND_SIGNATURES = setOf( - "containsAll", "removeAll", "retainAll" - ).map { "java/util/Collection".method(it, "Ljava/util/Collection;", JvmPrimitiveType.BOOLEAN.desc) } - - private val ERASED_COLLECTION_PARAMETER_SIGNATURES = ERASED_COLLECTION_PARAMETER_NAME_AND_SIGNATURES.map { it.signature } - val ERASED_COLLECTION_PARAMETER_NAMES = ERASED_COLLECTION_PARAMETER_NAME_AND_SIGNATURES.map { it.name.asString() } - - enum class TypeSafeBarrierDescription(val defaultValue: Any?) { - NULL(null), INDEX(-1), FALSE(false), - - MAP_GET_OR_DEFAULT(null) { - override fun checkParameter(index: Int) = index != 1 - }; - - open fun checkParameter(index: Int) = true - } - - private val GENERIC_PARAMETERS_METHODS_TO_DEFAULT_VALUES_MAP = - signatures { - mapOf( - javaUtil("Collection") - .method("contains", "Ljava/lang/Object;", JvmPrimitiveType.BOOLEAN.desc) to TypeSafeBarrierDescription.FALSE, - javaUtil("Collection") - .method("remove", "Ljava/lang/Object;", JvmPrimitiveType.BOOLEAN.desc) to TypeSafeBarrierDescription.FALSE, - - javaUtil("Map") - .method("containsKey", "Ljava/lang/Object;", JvmPrimitiveType.BOOLEAN.desc) to TypeSafeBarrierDescription.FALSE, - javaUtil("Map") - .method("containsValue", "Ljava/lang/Object;", JvmPrimitiveType.BOOLEAN.desc) to TypeSafeBarrierDescription.FALSE, - javaUtil("Map") - .method( - "remove", "Ljava/lang/Object;Ljava/lang/Object;", - JvmPrimitiveType.BOOLEAN.desc - ) to TypeSafeBarrierDescription.FALSE, - - javaUtil("Map") - .method( - "getOrDefault", "Ljava/lang/Object;Ljava/lang/Object;", - "Ljava/lang/Object;" - ) to TypeSafeBarrierDescription.MAP_GET_OR_DEFAULT, - - javaUtil("Map") - .method("get", "Ljava/lang/Object;", "Ljava/lang/Object;") to TypeSafeBarrierDescription.NULL, - javaUtil("Map") - .method("remove", "Ljava/lang/Object;", "Ljava/lang/Object;") to TypeSafeBarrierDescription.NULL, - - javaUtil("List") - .method("indexOf", "Ljava/lang/Object;", JvmPrimitiveType.INT.desc) to TypeSafeBarrierDescription.INDEX, - javaUtil("List") - .method("lastIndexOf", "Ljava/lang/Object;", JvmPrimitiveType.INT.desc) to TypeSafeBarrierDescription.INDEX - ) - } - - private val SIGNATURE_TO_DEFAULT_VALUES_MAP = GENERIC_PARAMETERS_METHODS_TO_DEFAULT_VALUES_MAP.mapKeys { it.key.signature } - private val ERASED_VALUE_PARAMETERS_SHORT_NAMES: Set - val ERASED_VALUE_PARAMETERS_SIGNATURES: Set - - init { - val allMethods = GENERIC_PARAMETERS_METHODS_TO_DEFAULT_VALUES_MAP.keys + ERASED_COLLECTION_PARAMETER_NAME_AND_SIGNATURES - ERASED_VALUE_PARAMETERS_SHORT_NAMES = allMethods.map { it.name }.toSet() - ERASED_VALUE_PARAMETERS_SIGNATURES = allMethods.map { it.signature }.toSet() - } - +object BuiltinMethodsWithSpecialGenericSignature : SpecialGenericSignatures() { private val CallableMemberDescriptor.hasErasedValueParametersInJava: Boolean get() = computeJvmSignature() in ERASED_VALUE_PARAMETERS_SIGNATURES @@ -128,12 +54,6 @@ object BuiltinMethodsWithSpecialGenericSignature { val Name.sameAsBuiltinMethodWithErasedValueParameters: Boolean get() = this in ERASED_VALUE_PARAMETERS_SHORT_NAMES - enum class SpecialSignatureInfo(val valueParametersSignature: String?, val isObjectReplacedWithTypeParameter: Boolean) { - ONE_COLLECTION_PARAMETER("Ljava/util/Collection<+Ljava/lang/Object;>;", false), - OBJECT_PARAMETER_NON_GENERIC(null, true), - OBJECT_PARAMETER_GENERIC("Ljava/lang/Object;", true) - } - fun CallableMemberDescriptor.isBuiltinWithSpecialDescriptorInJvm(): Boolean { if (!KotlinBuiltIns.isBuiltIn(this)) return false return getSpecialSignatureInfo()?.isObjectReplacedWithTypeParameter ?: false || doesOverrideBuiltinWithDifferentJvmName() @@ -146,49 +66,11 @@ object BuiltinMethodsWithSpecialGenericSignature { val builtinSignature = firstOverridden { it is FunctionDescriptor && it.hasErasedValueParametersInJava }?.computeJvmSignature() ?: return null - if (builtinSignature in ERASED_COLLECTION_PARAMETER_SIGNATURES) return SpecialSignatureInfo.ONE_COLLECTION_PARAMETER - - val defaultValue = SIGNATURE_TO_DEFAULT_VALUES_MAP.getValue(builtinSignature) - - return if (defaultValue == TypeSafeBarrierDescription.NULL) { - // return type is some generic type as 'Map.get' - SpecialSignatureInfo.OBJECT_PARAMETER_GENERIC - } else - SpecialSignatureInfo.OBJECT_PARAMETER_NON_GENERIC + return getSpecialSignatureInfo(builtinSignature) } } -object BuiltinMethodsWithDifferentJvmName { - // Note that signatures here are not real, - // e.g. 'java/lang/CharSequence.get(I)C' does not actually exist in JDK - // But it doesn't matter here, because signatures are only used to distinguish overloaded built-in definitions - private val REMOVE_AT_NAME_AND_SIGNATURE = - "java/util/List".method("removeAt", JvmPrimitiveType.INT.desc, "Ljava/lang/Object;") - - private val NAME_AND_SIGNATURE_TO_JVM_REPRESENTATION_NAME_MAP: Map = signatures { - mapOf( - javaLang("Number").method("toByte", "", JvmPrimitiveType.BYTE.desc) to Name.identifier("byteValue"), - javaLang("Number").method("toShort", "", JvmPrimitiveType.SHORT.desc) to Name.identifier("shortValue"), - javaLang("Number").method("toInt", "", JvmPrimitiveType.INT.desc) to Name.identifier("intValue"), - javaLang("Number").method("toLong", "", JvmPrimitiveType.LONG.desc) to Name.identifier("longValue"), - javaLang("Number").method("toFloat", "", JvmPrimitiveType.FLOAT.desc) to Name.identifier("floatValue"), - javaLang("Number").method("toDouble", "", JvmPrimitiveType.DOUBLE.desc) to Name.identifier("doubleValue"), - REMOVE_AT_NAME_AND_SIGNATURE to Name.identifier("remove"), - javaLang("CharSequence") - .method("get", JvmPrimitiveType.INT.desc, JvmPrimitiveType.CHAR.desc) to Name.identifier("charAt") - ) - } - - val SIGNATURE_TO_JVM_REPRESENTATION_NAME: Map = - NAME_AND_SIGNATURE_TO_JVM_REPRESENTATION_NAME_MAP.mapKeys { it.key.signature } - - val ORIGINAL_SHORT_NAMES: List = NAME_AND_SIGNATURE_TO_JVM_REPRESENTATION_NAME_MAP.keys.map { it.name } - - private val JVM_SHORT_NAME_TO_BUILTIN_SHORT_NAMES_MAP: Map> = - NAME_AND_SIGNATURE_TO_JVM_REPRESENTATION_NAME_MAP.entries - .map { Pair(it.key.name, it.value) } - .groupBy({ it.second }, { it.first }) - +object BuiltinMethodsWithDifferentJvmName : SpecialGenericSignatures() { val Name.sameAsRenamedInJvmBuiltin: Boolean get() = this in ORIGINAL_SHORT_NAMES @@ -212,7 +94,7 @@ object BuiltinMethodsWithDifferentJvmName { @Suppress("UNCHECKED_CAST") fun T.getOverriddenBuiltinWithDifferentJvmName(): T? { - if (name !in BuiltinMethodsWithDifferentJvmName.ORIGINAL_SHORT_NAMES + if (name !in SpecialGenericSignatures.ORIGINAL_SHORT_NAMES && propertyIfAccessor.name !in BuiltinSpecialProperties.SPECIAL_SHORT_NAMES ) return null diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/lightClasses/platformWrappers.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/lightClasses/platformWrappers.kt index 3eb7cc8e037..e76c7e7b392 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/lightClasses/platformWrappers.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/lightClasses/platformWrappers.kt @@ -35,9 +35,9 @@ import org.jetbrains.kotlin.builtins.DefaultBuiltIns import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap import org.jetbrains.kotlin.idea.KotlinLanguage import org.jetbrains.kotlin.incremental.components.NoLookupLocation -import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature import org.jetbrains.kotlin.load.java.BuiltinSpecialProperties import org.jetbrains.kotlin.load.java.JvmAbi +import org.jetbrains.kotlin.load.java.SpecialGenericSignatures import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.UserDataProperty @@ -51,7 +51,7 @@ private val mutableQualifiedNamesToJavaClass = JavaToKotlinClassMap.mutabilityMa } private val membersWithSpecializedSignature: Set = - BuiltinMethodsWithSpecialGenericSignature.ERASED_VALUE_PARAMETERS_SIGNATURES.mapTo(LinkedHashSet()) { + SpecialGenericSignatures.ERASED_VALUE_PARAMETERS_SIGNATURES.mapTo(LinkedHashSet()) { val fqNameString = it.substringBefore('(').replace('/', '.') FqName(fqNameString).shortName().asString() } @@ -124,7 +124,7 @@ class KtLightMutabilityPlatformWrapper( return listOf(finalBridgeWithObject, abstractKotlinVariantWithGeneric) } - if (methodName in BuiltinMethodsWithSpecialGenericSignature.ERASED_COLLECTION_PARAMETER_NAMES) { + if (methodName in SpecialGenericSignatures.ERASED_COLLECTION_PARAMETER_NAMES) { return emptyList() }