FIR: Support loading built-in top-level functions
This commit is contained in:
+140
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* 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.fir.deserialization
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.declarations.FirNamedFunction
|
||||
import org.jetbrains.kotlin.fir.declarations.FirValueParameter
|
||||
import org.jetbrains.kotlin.fir.declarations.impl.FirMemberFunctionImpl
|
||||
import org.jetbrains.kotlin.fir.declarations.impl.FirTypeParameterImpl
|
||||
import org.jetbrains.kotlin.fir.declarations.impl.FirValueParameterImpl
|
||||
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
|
||||
import org.jetbrains.kotlin.fir.symbols.CallableId
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||
import org.jetbrains.kotlin.fir.types.FirTypeRef
|
||||
import org.jetbrains.kotlin.fir.types.impl.FirResolvedTypeRefImpl
|
||||
import org.jetbrains.kotlin.metadata.ProtoBuf
|
||||
import org.jetbrains.kotlin.metadata.deserialization.*
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.protobuf.MessageLite
|
||||
import org.jetbrains.kotlin.serialization.deserialization.AnnotatedCallableKind
|
||||
import org.jetbrains.kotlin.serialization.deserialization.ProtoEnumFlags
|
||||
import org.jetbrains.kotlin.serialization.deserialization.getName
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
|
||||
class FirDeserializationContext(
|
||||
val nameResolver: NameResolver,
|
||||
val typeTable: TypeTable,
|
||||
val versionRequirementTable: VersionRequirementTable,
|
||||
val session: FirSession,
|
||||
val packageFqName: FqName,
|
||||
val relativeClassName: FqName?,
|
||||
val typeDeserializer: FirTypeDeserializer,
|
||||
val components: FirDeserializationComponents
|
||||
) {
|
||||
fun childContext(
|
||||
typeParameterProtos: List<ProtoBuf.TypeParameter>
|
||||
): FirDeserializationContext = FirDeserializationContext(
|
||||
nameResolver, typeTable, versionRequirementTable, session, packageFqName, relativeClassName,
|
||||
FirTypeDeserializer(
|
||||
nameResolver, typeTable, typeParameterProtos, typeDeserializer
|
||||
),
|
||||
components
|
||||
)
|
||||
|
||||
val memberDeserializer: FirMemberDeserializer = FirMemberDeserializer(this)
|
||||
}
|
||||
|
||||
// TODO: Move something here
|
||||
class FirDeserializationComponents
|
||||
|
||||
class FirMemberDeserializer(private val c: FirDeserializationContext) {
|
||||
private fun loadOldFlags(oldFlags: Int): Int {
|
||||
val lowSixBits = oldFlags and 0x3f
|
||||
val rest = (oldFlags shr 8) shl 6
|
||||
return lowSixBits + rest
|
||||
}
|
||||
|
||||
fun loadFunction(proto: ProtoBuf.Function): FirNamedFunction {
|
||||
val flags = if (proto.hasFlags()) proto.flags else loadOldFlags(proto.oldFlags)
|
||||
|
||||
val receiverAnnotations =
|
||||
// TODO: support annotations
|
||||
Annotations.EMPTY
|
||||
|
||||
val versionRequirementTable =
|
||||
// TODO: Support case for KOTLIN_SUSPEND_BUILT_IN_FUNCTION_FQ_NAME
|
||||
c.versionRequirementTable
|
||||
|
||||
val callableName = c.nameResolver.getName(proto.name)
|
||||
val symbol = FirFunctionSymbol(CallableId(c.packageFqName, c.relativeClassName, callableName))
|
||||
val local = c.childContext(proto.typeParameterList)
|
||||
|
||||
// TODO: support contracts
|
||||
return FirMemberFunctionImpl(
|
||||
c.session,
|
||||
null,
|
||||
symbol,
|
||||
callableName,
|
||||
ProtoEnumFlags.visibility(Flags.VISIBILITY.get(flags)),
|
||||
ProtoEnumFlags.modality(Flags.MODALITY.get(flags)),
|
||||
Flags.IS_EXPECT_FUNCTION.get(flags),
|
||||
false,
|
||||
false,
|
||||
Flags.IS_OPERATOR.get(flags),
|
||||
Flags.IS_INFIX.get(flags),
|
||||
Flags.IS_INLINE.get(flags),
|
||||
Flags.IS_TAILREC.get(flags),
|
||||
Flags.IS_EXTERNAL_FUNCTION.get(flags),
|
||||
Flags.IS_SUSPEND.get(flags),
|
||||
proto.receiverType(c.typeTable)?.let(local.typeDeserializer::type)?.toTypeRef(),
|
||||
local.typeDeserializer.type(proto.returnType(c.typeTable)).toTypeRef()
|
||||
).apply {
|
||||
typeParameters += local.typeDeserializer.ownTypeParameters.map { createTypeParameterSymbol(it.name).fir }
|
||||
valueParameters += local.memberDeserializer.valueParameters(proto.valueParameterList)
|
||||
annotations += getAnnotations(proto, flags, AnnotatedCallableKind.FUNCTION)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createTypeParameterSymbol(name: Name): FirTypeParameterSymbol {
|
||||
val firSymbol = FirTypeParameterSymbol()
|
||||
FirTypeParameterImpl(c.session, null, firSymbol, name, variance = Variance.INVARIANT, isReified = false)
|
||||
return firSymbol
|
||||
}
|
||||
|
||||
private fun valueParameters(
|
||||
valueParameters: List<ProtoBuf.ValueParameter>
|
||||
): List<FirValueParameter> {
|
||||
return valueParameters.mapIndexed { i, proto ->
|
||||
val flags = if (proto.hasFlags()) proto.flags else 0
|
||||
FirValueParameterImpl(
|
||||
c.session, null, c.nameResolver.getName(proto.name),
|
||||
c.typeDeserializer.type(proto.type(c.typeTable)).toTypeRef(),
|
||||
null, // TODO: default value
|
||||
Flags.IS_CROSSINLINE.get(flags),
|
||||
Flags.IS_NOINLINE.get(flags),
|
||||
proto.varargElementType(c.typeTable) != null
|
||||
).apply {
|
||||
annotations += emptyList() // TODO: parameter annotations
|
||||
}
|
||||
}.toList()
|
||||
}
|
||||
|
||||
|
||||
// TODO: Annotations
|
||||
private fun getAnnotations(proto: MessageLite, flags: Int, kind: AnnotatedCallableKind) = emptyList<FirAnnotationCall>()
|
||||
|
||||
private fun ConeKotlinType.toTypeRef(): FirTypeRef {
|
||||
// TODO: annotations
|
||||
return FirResolvedTypeRefImpl(c.session, null, this, nullability.isNullable, emptyList())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.fir.service
|
||||
import org.jetbrains.kotlin.fir.symbols.*
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
interface FirSymbolProvider {
|
||||
|
||||
@@ -26,6 +27,8 @@ interface FirSymbolProvider {
|
||||
|
||||
fun getCallableSymbols(callableId: CallableId): List<ConeCallableSymbol>
|
||||
|
||||
fun getAllCallableNamesInPackage(fqName: FqName): Set<Name> = emptySet()
|
||||
|
||||
fun getPackage(fqName: FqName): FqName? // TODO: Replace to symbol sometime
|
||||
|
||||
// TODO: should not retrieve session through the FirElement::session
|
||||
|
||||
+7
-6
@@ -11,15 +11,12 @@ import org.jetbrains.kotlin.fir.symbols.ConeCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeSymbol
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
|
||||
|
||||
class FirCompositeSymbolProvider(val providers: List<FirSymbolProvider>) : FirSymbolProvider {
|
||||
override fun getCallableSymbols(callableId: CallableId): List<ConeCallableSymbol> {
|
||||
for (provider in providers) {
|
||||
val symbols = provider.getCallableSymbols(callableId)
|
||||
if (symbols.isNotEmpty()) return symbols
|
||||
}
|
||||
return emptyList()
|
||||
return providers.flatMap { it.getCallableSymbols(callableId) }
|
||||
}
|
||||
|
||||
override fun getPackage(fqName: FqName): FqName? {
|
||||
@@ -29,4 +26,8 @@ class FirCompositeSymbolProvider(val providers: List<FirSymbolProvider>) : FirSy
|
||||
override fun getClassLikeSymbolByFqName(classId: ClassId): ConeClassLikeSymbol? {
|
||||
return providers.firstNotNullResult { it.getClassLikeSymbolByFqName(classId) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun getAllCallableNamesInPackage(fqName: FqName): Set<Name> {
|
||||
return providers.flatMapTo(mutableSetOf()) { it.getAllCallableNamesInPackage(fqName) }
|
||||
}
|
||||
}
|
||||
|
||||
+8
-8
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.fir.symbols.ConeCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeSymbol
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
class FirDependenciesSymbolProviderImpl(val session: FirSession) : AbstractFirSymbolProvider() {
|
||||
private val dependencyProviders by lazy {
|
||||
@@ -26,13 +27,8 @@ class FirDependenciesSymbolProviderImpl(val session: FirSession) : AbstractFirSy
|
||||
|
||||
override fun getCallableSymbols(callableId: CallableId): List<ConeCallableSymbol> {
|
||||
return callableCache.lookupCacheOrCalculate(callableId) {
|
||||
for (provider in dependencyProviders) {
|
||||
provider.getCallableSymbols(callableId).let {
|
||||
if (it.isNotEmpty()) return@lookupCacheOrCalculate it
|
||||
}
|
||||
}
|
||||
emptyList()
|
||||
}.orEmpty()
|
||||
dependencyProviders.flatMap { provider -> provider.getCallableSymbols(callableId) }
|
||||
} ?: emptyList()
|
||||
}
|
||||
|
||||
override fun getClassLikeSymbolByFqName(classId: ClassId): ConeClassLikeSymbol? {
|
||||
@@ -56,4 +52,8 @@ class FirDependenciesSymbolProviderImpl(val session: FirSession) : AbstractFirSy
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getAllCallableNamesInPackage(fqName: FqName): Set<Name> {
|
||||
return dependencyProviders.flatMapTo(mutableSetOf()) { it.getAllCallableNamesInPackage(fqName) }
|
||||
}
|
||||
}
|
||||
|
||||
+46
-9
@@ -14,6 +14,8 @@ import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.declarations.impl.FirClassImpl
|
||||
import org.jetbrains.kotlin.fir.declarations.impl.FirTypeParameterImpl
|
||||
import org.jetbrains.kotlin.fir.deserialization.FirDeserializationComponents
|
||||
import org.jetbrains.kotlin.fir.deserialization.FirDeserializationContext
|
||||
import org.jetbrains.kotlin.fir.deserialization.FirTypeDeserializer
|
||||
import org.jetbrains.kotlin.fir.resolve.FirSymbolProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.getOrPut
|
||||
@@ -26,10 +28,7 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.fir.types.impl.FirResolvedTypeRefImpl
|
||||
import org.jetbrains.kotlin.metadata.ProtoBuf
|
||||
import org.jetbrains.kotlin.metadata.builtins.BuiltInsBinaryVersion
|
||||
import org.jetbrains.kotlin.metadata.deserialization.Flags
|
||||
import org.jetbrains.kotlin.metadata.deserialization.NameResolverImpl
|
||||
import org.jetbrains.kotlin.metadata.deserialization.TypeTable
|
||||
import org.jetbrains.kotlin.metadata.deserialization.supertypes
|
||||
import org.jetbrains.kotlin.metadata.deserialization.*
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
@@ -42,11 +41,6 @@ import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
|
||||
import java.io.InputStream
|
||||
|
||||
class FirLibrarySymbolProviderImpl(val session: FirSession) : FirSymbolProvider {
|
||||
override fun getCallableSymbols(callableId: CallableId): List<ConeCallableSymbol> {
|
||||
// TODO
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
private class BuiltInsPackageFragment(stream: InputStream, val fqName: FqName, val session: FirSession) {
|
||||
lateinit var version: BuiltInsBinaryVersion
|
||||
|
||||
@@ -70,6 +64,22 @@ class FirLibrarySymbolProviderImpl(val session: FirSession) : FirSymbolProvider
|
||||
|
||||
val classDataFinder = ProtoBasedClassDataFinder(packageProto, nameResolver, version) { SourceElement.NO_SOURCE }
|
||||
|
||||
private val memberDeserializer by lazy {
|
||||
FirDeserializationContext(
|
||||
nameResolver, TypeTable(packageProto.`package`.typeTable),
|
||||
VersionRequirementTable.EMPTY, // TODO:
|
||||
session,
|
||||
fqName,
|
||||
null,
|
||||
FirTypeDeserializer(
|
||||
nameResolver,
|
||||
TypeTable(packageProto.`package`.typeTable),
|
||||
emptyList(),
|
||||
null
|
||||
),
|
||||
FirDeserializationComponents()
|
||||
).memberDeserializer
|
||||
}
|
||||
|
||||
val lookup = mutableMapOf<ClassId, ConeClassLikeSymbol>()
|
||||
|
||||
@@ -124,6 +134,16 @@ class FirLibrarySymbolProviderImpl(val session: FirSession) : FirSymbolProvider
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getTopLevelCallableSymbols(name: Name): List<ConeCallableSymbol> {
|
||||
return packageProto.`package`.functionList.filter { nameResolver.getName(it.name) == name }.map {
|
||||
memberDeserializer.loadFunction(it).symbol
|
||||
}
|
||||
}
|
||||
|
||||
fun getAllCallableNames(): Set<Name> {
|
||||
return packageProto.`package`.functionList.mapTo(mutableSetOf()) { nameResolver.getName(it.name) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun getPackage(fqName: FqName): FqName? {
|
||||
@@ -176,4 +196,21 @@ class FirLibrarySymbolProviderImpl(val session: FirSession) : FirSymbolProvider
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getCallableSymbols(callableId: CallableId): List<ConeCallableSymbol> {
|
||||
if (callableId.classId != null) {
|
||||
// TODO: Support classes
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
return allPackageFragments[callableId.packageName]?.flatMap {
|
||||
it.getTopLevelCallableSymbols(callableId.callableName)
|
||||
} ?: emptyList()
|
||||
}
|
||||
|
||||
override fun getAllCallableNamesInPackage(fqName: FqName): Set<Name> {
|
||||
return allPackageFragments[fqName]?.flatMapTo(mutableSetOf()) {
|
||||
it.getAllCallableNames()
|
||||
} ?: emptySet()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
<T> public final inline fun arrayOf(vararg elements: R|kotlin/Array<out T>|): R|kotlin/Array<T>|
|
||||
|
||||
<T> public final fun arrayOfNulls(size: R|kotlin/Int|): R|kotlin/Array<T>|
|
||||
|
||||
public final fun booleanArrayOf(vararg elements: R|kotlin/BooleanArray|): R|kotlin/BooleanArray|
|
||||
|
||||
public final fun byteArrayOf(vararg elements: R|kotlin/ByteArray|): R|kotlin/ByteArray|
|
||||
|
||||
public final fun charArrayOf(vararg elements: R|kotlin/CharArray|): R|kotlin/CharArray|
|
||||
|
||||
public final fun doubleArrayOf(vararg elements: R|kotlin/DoubleArray|): R|kotlin/DoubleArray|
|
||||
|
||||
<T> public final inline fun emptyArray(): R|kotlin/Array<T>|
|
||||
|
||||
<T> public final inline fun enumValueOf(name: R|kotlin/String|): R|T|
|
||||
|
||||
<T> public final inline fun enumValues(): R|kotlin/Array<T>|
|
||||
|
||||
public final fun floatArrayOf(vararg elements: R|kotlin/FloatArray|): R|kotlin/FloatArray|
|
||||
|
||||
public final fun intArrayOf(vararg elements: R|kotlin/IntArray|): R|kotlin/IntArray|
|
||||
|
||||
public final fun longArrayOf(vararg elements: R|kotlin/LongArray|): R|kotlin/LongArray|
|
||||
|
||||
public final fun shortArrayOf(vararg elements: R|kotlin/ShortArray|): R|kotlin/ShortArray|
|
||||
|
||||
public final operator fun R|kotlin/String|.plus(other: R|kotlin/Any|): R|kotlin/String|
|
||||
|
||||
public final fun R|kotlin/Any|.toString(): R|kotlin/String|
|
||||
|
||||
@@ -13,7 +13,7 @@ FILE: simpleClass.kt
|
||||
private get(): R|kotlin/Int|
|
||||
|
||||
public final override fun foo(x: R|kotlin/Int|, y: R|kotlin/String|): R|kotlin/String| {
|
||||
^foo <Unresolved name: plus>#(<Unresolved name: plus>#(R|<local>/y|, R|<local>/x|), R|/SomeClass.baz|)
|
||||
^foo R|kotlin/plus|(R|kotlin/plus|(R|<local>/y|, R|<local>/x|), R|/SomeClass.baz|)
|
||||
}
|
||||
|
||||
public final override var bar: R|kotlin/Boolean|
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ FILE: simpleClass.kt
|
||||
private get(): R|kotlin/Int|
|
||||
|
||||
public final override fun foo(x: R|kotlin/Int|, y: R|kotlin/String|): R|kotlin/String| {
|
||||
^foo <Unresolved name: plus>#(<Unresolved name: plus>#(R|<local>/y|, R|<local>/x|), R|/SomeClass.baz|)
|
||||
^foo R|kotlin/plus|(R|kotlin/plus|(R|<local>/y|, R|<local>/x|), R|/SomeClass.baz|)
|
||||
}
|
||||
|
||||
public final override var bar: R|kotlin/Boolean|
|
||||
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.fir
|
||||
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.fir.resolve.FirSymbolProvider
|
||||
import org.jetbrains.kotlin.fir.symbols.CallableId
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.test.ConfigurationKind
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import org.jetbrains.kotlin.test.TestJdkKind
|
||||
import java.io.File
|
||||
|
||||
class BuiltInsDeserializationForFirTestCase : AbstractFirResolveWithSessionTestCase() {
|
||||
override fun createEnvironment(): KotlinCoreEnvironment {
|
||||
return createEnvironmentWithJdk(ConfigurationKind.ALL, TestJdkKind.FULL_JDK)
|
||||
}
|
||||
|
||||
fun testCallables() {
|
||||
for (packageFqName in listOf(
|
||||
KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME,
|
||||
KotlinBuiltIns.COLLECTIONS_PACKAGE_FQ_NAME,
|
||||
KotlinBuiltIns.RANGES_PACKAGE_FQ_NAME
|
||||
)) {
|
||||
checkCallablesInPackage(packageFqName)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkCallablesInPackage(packageFqName: FqName) {
|
||||
val session = createSession(project, GlobalSearchScope.allScope(project))
|
||||
val provider = session.getService(FirSymbolProvider::class)
|
||||
val names = provider.getAllCallableNamesInPackage(packageFqName)
|
||||
|
||||
val builder = StringBuilder()
|
||||
val firRenderer = FirRenderer(builder)
|
||||
|
||||
for (name in names) {
|
||||
for (symbol in provider.getCallableSymbols(CallableId(packageFqName, null, name))) {
|
||||
(symbol as FirCallableSymbol).fir.accept(firRenderer)
|
||||
builder.appendln()
|
||||
}
|
||||
}
|
||||
|
||||
KotlinTestUtils.assertEqualsToFile(
|
||||
File("compiler/fir/resolve/testData/builtIns/" + packageFqName.asString().replace('.', '-') + ".txt"),
|
||||
builder.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ FILE: jvm.kt
|
||||
public final fun test(): R|kotlin/Unit| {
|
||||
lval res1: R|kotlin/Boolean| = R|/Some.foo|(Int(1))
|
||||
lval res2: R|kotlin/Boolean| = R|/Some.foo|(<Unresolved name: unaryMinus>#(Int(1)))
|
||||
lval res3: R|kotlin/Array<ft<java/lang/String, java/lang/String?>>| = R|/Some.bar|(<Unresolved name: intArrayOf>#(Int(0), Int(2), <Unresolved name: unaryMinus>#(Int(2))))
|
||||
lval res3: R|kotlin/Array<ft<java/lang/String, java/lang/String?>>| = R|/Some.bar|(R|kotlin/intArrayOf|(Int(0), Int(2), <Unresolved name: unaryMinus>#(Int(2))))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user