[cls] write contracts information to cls stubs
^ KTIJ-24665 this information would be used to create resolved FirElements from stubs, so no ProtoBuf would be kept in memory
This commit is contained in:
+22
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.contracts.description
|
||||
|
||||
interface KtContractDescriptionElement<Type, Diagnostic> {
|
||||
fun <R, D> accept(contractDescriptionVisitor: KtContractDescriptionVisitor<R, D, Type, Diagnostic>, data: D): R
|
||||
|
||||
val erroneous: Boolean
|
||||
}
|
||||
|
||||
abstract class KtEffectDeclaration<Type, Diagnostic> : KtContractDescriptionElement<Type, Diagnostic> {
|
||||
override fun <R, D> accept(contractDescriptionVisitor: KtContractDescriptionVisitor<R, D, Type, Diagnostic>, data: D): R =
|
||||
contractDescriptionVisitor.visitEffectDeclaration(this, data)
|
||||
}
|
||||
|
||||
interface KtBooleanExpression<Type, Diagnostic> : KtContractDescriptionElement<Type, Diagnostic> {
|
||||
override fun <R, D> accept(contractDescriptionVisitor: KtContractDescriptionVisitor<R, D, Type, Diagnostic>, data: D): R =
|
||||
contractDescriptionVisitor.visitBooleanExpression(this, data)
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.contracts.description
|
||||
|
||||
|
||||
abstract class KtContractDescriptionVisitor<out R, in D, Type, Diagnostic> {
|
||||
open fun visitContractDescriptionElement(contractDescriptionElement: KtContractDescriptionElement<Type, Diagnostic>, data: D): R {
|
||||
throw IllegalStateException("Top of hierarchy reached, no overloads were found for element: $contractDescriptionElement")
|
||||
}
|
||||
|
||||
// Effects
|
||||
open fun visitEffectDeclaration(effectDeclaration: KtEffectDeclaration<Type, Diagnostic>, data: D): R = visitContractDescriptionElement(effectDeclaration, data)
|
||||
|
||||
open fun visitConditionalEffectDeclaration(conditionalEffect: KtConditionalEffectDeclaration<Type, Diagnostic>, data: D): R =
|
||||
visitEffectDeclaration(conditionalEffect, data)
|
||||
|
||||
open fun visitReturnsEffectDeclaration(returnsEffect: KtReturnsEffectDeclaration<Type, Diagnostic>, data: D): R =
|
||||
visitEffectDeclaration(returnsEffect, data)
|
||||
|
||||
open fun visitCallsEffectDeclaration(callsEffect: KtCallsEffectDeclaration<Type, Diagnostic>, data: D): R =
|
||||
visitEffectDeclaration(callsEffect, data)
|
||||
|
||||
open fun visitErroneousCallsEffectDeclaration(callsEffect: KtErroneousCallsEffectDeclaration<Type, Diagnostic>, data: D): R =
|
||||
visitCallsEffectDeclaration(callsEffect, data)
|
||||
|
||||
// Expressions
|
||||
open fun visitBooleanExpression(booleanExpression: KtBooleanExpression<Type, Diagnostic>, data: D): R =
|
||||
visitContractDescriptionElement(booleanExpression, data)
|
||||
|
||||
open fun visitLogicalBinaryOperationContractExpression(binaryLogicExpression: KtBinaryLogicExpression<Type, Diagnostic>, data: D): R =
|
||||
visitBooleanExpression(binaryLogicExpression, data)
|
||||
|
||||
open fun visitLogicalNot(logicalNot: KtLogicalNot<Type, Diagnostic>, data: D): R = visitBooleanExpression(logicalNot, data)
|
||||
|
||||
open fun visitIsInstancePredicate(isInstancePredicate: KtIsInstancePredicate<Type, Diagnostic>, data: D): R =
|
||||
visitBooleanExpression(isInstancePredicate, data)
|
||||
|
||||
open fun visitErroneousIsInstancePredicate(isInstancePredicate: KtErroneousIsInstancePredicate<Type, Diagnostic>, data: D): R =
|
||||
visitIsInstancePredicate(isInstancePredicate, data)
|
||||
|
||||
open fun visitIsNullPredicate(isNullPredicate: KtIsNullPredicate<Type, Diagnostic>, data: D): R = visitBooleanExpression(isNullPredicate, data)
|
||||
|
||||
// Values
|
||||
open fun visitValue(value: KtContractDescriptionValue<Type, Diagnostic>, data: D): R = visitContractDescriptionElement(value, data)
|
||||
|
||||
open fun visitConstantDescriptor(constantReference: KtConstantReference<Type, Diagnostic>, data: D): R = visitValue(constantReference, data)
|
||||
|
||||
open fun visitBooleanConstantDescriptor(booleanConstantDescriptor: KtBooleanConstantReference<Type, Diagnostic>, data: D): R =
|
||||
visitConstantDescriptor(booleanConstantDescriptor, data)
|
||||
|
||||
open fun visitErroneousConstantReference(erroneousConstantReference: KtErroneousConstantReference<Type, Diagnostic>, data: D): R =
|
||||
visitConstantDescriptor(erroneousConstantReference, data)
|
||||
|
||||
open fun visitValueParameterReference(valueParameterReference: KtValueParameterReference<Type, Diagnostic>, data: D): R =
|
||||
visitValue(valueParameterReference, data)
|
||||
|
||||
open fun visitBooleanValueParameterReference(booleanValueParameterReference: KtBooleanValueParameterReference<Type, Diagnostic>, data: D): R =
|
||||
visitValueParameterReference(booleanValueParameterReference, data)
|
||||
|
||||
open fun visitErroneousValueParameterReference(valueParameterReference: KtErroneousValueParameterReference<Type, Diagnostic>, data: D): R =
|
||||
visitValueParameterReference(valueParameterReference, data)
|
||||
|
||||
// Error
|
||||
open fun visitErroneousElement(element: KtErroneousContractElement<Type, Diagnostic>, data: D): R =
|
||||
visitContractDescriptionElement(element, data)
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.contracts.description
|
||||
|
||||
/**
|
||||
* Effect with condition attached to it.
|
||||
*
|
||||
* [condition] is some expression, which result-type is Boolean, and clause should
|
||||
* be interpreted as: "if [effect] took place then [condition]-expression is
|
||||
* guaranteed to be true"
|
||||
*
|
||||
* NB. [effect] and [condition] connected with implication in math logic sense:
|
||||
* [effect] => [condition]. In particular this means that:
|
||||
* - there can be multiple ways how [effect] can be produced, but for any of them
|
||||
* [condition] holds.
|
||||
* - if [effect] wasn't observed, we *can't* reason that [condition] is false
|
||||
* - if [condition] is true, we *can't* reason that [effect] will be observed.
|
||||
*/
|
||||
class KtConditionalEffectDeclaration<Type, Diagnostic>(
|
||||
val effect: KtEffectDeclaration<Type, Diagnostic>,
|
||||
val condition: KtBooleanExpression<Type, Diagnostic>
|
||||
) : KtEffectDeclaration<Type, Diagnostic>() {
|
||||
override val erroneous: Boolean
|
||||
get() = effect.erroneous || condition.erroneous
|
||||
|
||||
override fun <R, D> accept(contractDescriptionVisitor: KtContractDescriptionVisitor<R, D, Type, Diagnostic>, data: D): R =
|
||||
contractDescriptionVisitor.visitConditionalEffectDeclaration(this, data)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Effect which specifies that subroutine returns some particular value
|
||||
*/
|
||||
class KtReturnsEffectDeclaration<Type, Diagnostic>(val value: KtConstantReference<Type, Diagnostic>) :
|
||||
KtEffectDeclaration<Type, Diagnostic>() {
|
||||
override val erroneous: Boolean
|
||||
get() = value.erroneous
|
||||
|
||||
override fun <R, D> accept(contractDescriptionVisitor: KtContractDescriptionVisitor<R, D, Type, Diagnostic>, data: D): R =
|
||||
contractDescriptionVisitor.visitReturnsEffectDeclaration(this, data)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Effect which specifies, that during execution of subroutine, callable [valueParameterReference] will be invoked
|
||||
* [kind] amount of times, and will never be invoked after subroutine call is finished.
|
||||
*/
|
||||
open class KtCallsEffectDeclaration<Type, Diagnostic>(
|
||||
val valueParameterReference: KtValueParameterReference<Type, Diagnostic>,
|
||||
val kind: EventOccurrencesRange
|
||||
) : KtEffectDeclaration<Type, Diagnostic>() {
|
||||
override val erroneous: Boolean
|
||||
get() = valueParameterReference.erroneous
|
||||
|
||||
override fun <R, D> accept(contractDescriptionVisitor: KtContractDescriptionVisitor<R, D, Type, Diagnostic>, data: D): R =
|
||||
contractDescriptionVisitor.visitCallsEffectDeclaration(this, data)
|
||||
}
|
||||
|
||||
class KtErroneousCallsEffectDeclaration<Type, Diagnostic>(
|
||||
valueParameterReference: KtValueParameterReference<Type, Diagnostic>,
|
||||
val diagnostic: Diagnostic
|
||||
) : KtCallsEffectDeclaration<Type, Diagnostic>(valueParameterReference, EventOccurrencesRange.UNKNOWN) {
|
||||
override val erroneous: Boolean
|
||||
get() = true
|
||||
|
||||
override fun <R, D> accept(contractDescriptionVisitor: KtContractDescriptionVisitor<R, D, Type, Diagnostic>, data: D): R =
|
||||
contractDescriptionVisitor.visitErroneousCallsEffectDeclaration(this, data)
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.contracts.description
|
||||
|
||||
class KtErroneousContractElement<Type, Diagnostic>(
|
||||
val diagnostic: Diagnostic
|
||||
) : KtEffectDeclaration<Type, Diagnostic>(), KtBooleanExpression<Type, Diagnostic>, KtContractDescriptionValue<Type, Diagnostic> {
|
||||
override val erroneous: Boolean
|
||||
get() = true
|
||||
|
||||
override fun <R, D> accept(contractDescriptionVisitor: KtContractDescriptionVisitor<R, D, Type, Diagnostic>, data: D): R {
|
||||
return contractDescriptionVisitor.visitErroneousElement(this, data)
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.contracts.description
|
||||
|
||||
class KtBinaryLogicExpression<Type, Diagnostic>(
|
||||
val left: KtBooleanExpression<Type, Diagnostic>,
|
||||
val right: KtBooleanExpression<Type, Diagnostic>,
|
||||
val kind: LogicOperationKind
|
||||
) : KtBooleanExpression<Type, Diagnostic> {
|
||||
override val erroneous: Boolean
|
||||
get() = left.erroneous || right.erroneous
|
||||
|
||||
override fun <R, D> accept(contractDescriptionVisitor: KtContractDescriptionVisitor<R, D, Type, Diagnostic>, data: D): R {
|
||||
return contractDescriptionVisitor.visitLogicalBinaryOperationContractExpression(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
class KtLogicalNot<Type, Diagnostic>(val arg: KtBooleanExpression<Type, Diagnostic>) : KtBooleanExpression<Type, Diagnostic> {
|
||||
override val erroneous: Boolean
|
||||
get() = arg.erroneous
|
||||
|
||||
override fun <R, D> accept(contractDescriptionVisitor: KtContractDescriptionVisitor<R, D, Type, Diagnostic>, data: D): R =
|
||||
contractDescriptionVisitor.visitLogicalNot(this, data)
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.contracts.description
|
||||
|
||||
open class KtIsInstancePredicate<Type, Diagnostic>(val arg: KtValueParameterReference<Type, Diagnostic>, val type: Type, val isNegated: Boolean) :
|
||||
KtBooleanExpression<Type, Diagnostic> {
|
||||
override val erroneous: Boolean
|
||||
get() = arg.erroneous
|
||||
|
||||
override fun <R, D> accept(contractDescriptionVisitor: KtContractDescriptionVisitor<R, D, Type, Diagnostic>, data: D): R =
|
||||
contractDescriptionVisitor.visitIsInstancePredicate(this, data)
|
||||
|
||||
fun negated(): KtIsInstancePredicate<Type, Diagnostic> =
|
||||
KtIsInstancePredicate(arg, type, isNegated.not())
|
||||
}
|
||||
|
||||
class KtErroneousIsInstancePredicate<Type, Diagnostic>(
|
||||
arg: KtValueParameterReference<Type, Diagnostic>,
|
||||
type: Type,
|
||||
isNegated: Boolean,
|
||||
val diagnostic: Diagnostic
|
||||
) : KtIsInstancePredicate<Type, Diagnostic>(arg, type, isNegated) {
|
||||
override val erroneous: Boolean
|
||||
get() = true
|
||||
|
||||
override fun <R, D> accept(contractDescriptionVisitor: KtContractDescriptionVisitor<R, D, Type, Diagnostic>, data: D): R =
|
||||
contractDescriptionVisitor.visitErroneousIsInstancePredicate(this, data)
|
||||
}
|
||||
|
||||
class KtIsNullPredicate<Type, Diagnostic>(val arg: KtValueParameterReference<Type, Diagnostic>, val isNegated: Boolean) :
|
||||
KtBooleanExpression<Type, Diagnostic> {
|
||||
override val erroneous: Boolean
|
||||
get() = arg.erroneous
|
||||
|
||||
override fun <R, D> accept(contractDescriptionVisitor: KtContractDescriptionVisitor<R, D, Type, Diagnostic>, data: D): R =
|
||||
contractDescriptionVisitor.visitIsNullPredicate(this, data)
|
||||
|
||||
fun negated(): KtIsNullPredicate<Type, Diagnostic> =
|
||||
KtIsNullPredicate(arg, isNegated.not())
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.contracts.description
|
||||
|
||||
interface KtContractDescriptionValue<Type, Diagnostic> : KtContractDescriptionElement<Type, Diagnostic> {
|
||||
override fun <R, D> accept(contractDescriptionVisitor: KtContractDescriptionVisitor<R, D, Type, Diagnostic>, data: D): R =
|
||||
contractDescriptionVisitor.visitValue(this, data)
|
||||
}
|
||||
|
||||
open class KtConstantReference<Type, Diagnostic>(val name: String) : KtContractDescriptionValue<Type, Diagnostic> {
|
||||
override val erroneous: Boolean
|
||||
get() = false
|
||||
|
||||
override fun <R, D> accept(contractDescriptionVisitor: KtContractDescriptionVisitor<R, D, Type, Diagnostic>, data: D): R =
|
||||
contractDescriptionVisitor.visitConstantDescriptor(this, data)
|
||||
}
|
||||
|
||||
class KtBooleanConstantReference<Type, Diagnostic>(name: String) : KtConstantReference<Type, Diagnostic>(name),
|
||||
KtBooleanExpression<Type, Diagnostic> {
|
||||
override fun <R, D> accept(contractDescriptionVisitor: KtContractDescriptionVisitor<R, D, Type, Diagnostic>, data: D): R =
|
||||
contractDescriptionVisitor.visitBooleanConstantDescriptor(this, data)
|
||||
}
|
||||
|
||||
class KtErroneousConstantReference<Type, Diagnostic>(val diagnostic: Diagnostic) : KtConstantReference<Type, Diagnostic>("ERROR") {
|
||||
override val erroneous: Boolean
|
||||
get() = true
|
||||
|
||||
override fun <R, D> accept(contractDescriptionVisitor: KtContractDescriptionVisitor<R, D, Type, Diagnostic>, data: D): R =
|
||||
contractDescriptionVisitor.visitErroneousConstantReference(this, data)
|
||||
}
|
||||
|
||||
/*
|
||||
* Index of value parameter of function
|
||||
* -1 means that it is reference to extension receiver
|
||||
*/
|
||||
open class KtValueParameterReference<Type, Diagnostic>(val parameterIndex: Int, val name: String) :
|
||||
KtContractDescriptionValue<Type, Diagnostic> {
|
||||
init {
|
||||
assert(parameterIndex >= -1)
|
||||
}
|
||||
|
||||
override val erroneous: Boolean
|
||||
get() = false
|
||||
|
||||
override fun <R, D> accept(contractDescriptionVisitor: KtContractDescriptionVisitor<R, D, Type, Diagnostic>, data: D): R =
|
||||
contractDescriptionVisitor.visitValueParameterReference(this, data)
|
||||
}
|
||||
|
||||
class KtBooleanValueParameterReference<Type, Diagnostic>(parameterIndex: Int, name: String) : KtValueParameterReference<Type, Diagnostic>(parameterIndex, name),
|
||||
KtBooleanExpression<Type, Diagnostic> {
|
||||
override fun <R, D> accept(contractDescriptionVisitor: KtContractDescriptionVisitor<R, D, Type, Diagnostic>, data: D): R =
|
||||
contractDescriptionVisitor.visitBooleanValueParameterReference(this, data)
|
||||
}
|
||||
|
||||
class KtErroneousValueParameterReference<Type, Diagnostic>(val diagnostic: Diagnostic) : KtValueParameterReference<Type, Diagnostic>(Int.MAX_VALUE, "ERROR") {
|
||||
override val erroneous: Boolean
|
||||
get() = true
|
||||
|
||||
override fun <R, D> accept(contractDescriptionVisitor: KtContractDescriptionVisitor<R, D, Type, Diagnostic>, data: D): R =
|
||||
contractDescriptionVisitor.visitErroneousValueParameterReference(this, data)
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.contracts.description
|
||||
|
||||
enum class LogicOperationKind(val token: String) {
|
||||
AND("&&"), OR("||")
|
||||
}
|
||||
+196
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.serialization.deserialization
|
||||
|
||||
import org.jetbrains.kotlin.contracts.description.*
|
||||
import org.jetbrains.kotlin.contracts.description.LogicOperationKind
|
||||
import org.jetbrains.kotlin.metadata.ProtoBuf
|
||||
import org.jetbrains.kotlin.metadata.deserialization.Flags
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
|
||||
abstract class ProtoBufContractDeserializer<Type, Diagnostic, Owner> {
|
||||
protected fun loadPossiblyConditionalEffect(
|
||||
proto: ProtoBuf.Effect,
|
||||
owner: Owner
|
||||
): KtEffectDeclaration<Type, Diagnostic>? {
|
||||
if (proto.hasConclusionOfConditionalEffect()) {
|
||||
val conclusion = loadExpression(proto.conclusionOfConditionalEffect, owner) ?: return null
|
||||
val effect = loadSimpleEffect(proto, owner) ?: return null
|
||||
return KtConditionalEffectDeclaration(effect, conclusion)
|
||||
}
|
||||
return loadSimpleEffect(proto, owner)
|
||||
}
|
||||
|
||||
private fun loadSimpleEffect(proto: ProtoBuf.Effect, owner: Owner): KtEffectDeclaration<Type, Diagnostic>? {
|
||||
val type: ProtoBuf.Effect.EffectType = if (proto.hasEffectType()) proto.effectType else return null
|
||||
return when(type) {
|
||||
ProtoBuf.Effect.EffectType.RETURNS_CONSTANT -> {
|
||||
val argument = proto.effectConstructorArgumentList.firstOrNull()
|
||||
val returnValue = if (argument == null) {
|
||||
getWildcard()
|
||||
} else {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
loadExpression(argument, owner) as? KtConstantReference<Type, Diagnostic> ?: return null
|
||||
}
|
||||
KtReturnsEffectDeclaration(returnValue)
|
||||
}
|
||||
ProtoBuf.Effect.EffectType.RETURNS_NOT_NULL -> {
|
||||
KtReturnsEffectDeclaration(getNotNull())
|
||||
}
|
||||
ProtoBuf.Effect.EffectType.CALLS -> {
|
||||
val argument = proto.effectConstructorArgumentList.firstOrNull() ?: return null
|
||||
val callable = extractVariable(argument, owner) ?: return null
|
||||
val invocationKind = if (proto.hasKind())
|
||||
proto.kind.toDescriptorInvocationKind()
|
||||
else
|
||||
EventOccurrencesRange.UNKNOWN
|
||||
KtCallsEffectDeclaration(callable, invocationKind)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadExpression(proto: ProtoBuf.Expression, owner: Owner): KtBooleanExpression<Type, Diagnostic>? {
|
||||
val primitiveType = getPrimitiveType(proto)
|
||||
val primitiveExpression = extractPrimitiveExpression(proto, primitiveType, owner)
|
||||
|
||||
val complexType = getComplexType(proto)
|
||||
val childs: MutableList<KtBooleanExpression<Type, Diagnostic>> = mutableListOf()
|
||||
childs.addIfNotNull(primitiveExpression)
|
||||
|
||||
return when (complexType) {
|
||||
ComplexExpressionType.AND_SEQUENCE -> {
|
||||
proto.andArgumentList.mapTo(childs) { loadExpression(it, owner) ?: return null }
|
||||
childs.reduce { acc, booleanExpression -> KtBinaryLogicExpression(acc, booleanExpression, LogicOperationKind.AND) }
|
||||
}
|
||||
|
||||
ComplexExpressionType.OR_SEQUENCE -> {
|
||||
proto.orArgumentList.mapTo(childs) { loadExpression(it, owner) ?: return null }
|
||||
childs.reduce { acc, booleanExpression -> KtBinaryLogicExpression(acc, booleanExpression, LogicOperationKind.OR) }
|
||||
}
|
||||
|
||||
null -> primitiveExpression
|
||||
}
|
||||
}
|
||||
|
||||
private fun extractPrimitiveExpression(proto: ProtoBuf.Expression, primitiveType: PrimitiveExpressionType?, owner: Owner): KtBooleanExpression<Type, Diagnostic>? {
|
||||
val isInverted = Flags.IS_NEGATED.get(proto.flags)
|
||||
|
||||
return when (primitiveType) {
|
||||
PrimitiveExpressionType.VALUE_PARAMETER_REFERENCE, PrimitiveExpressionType.RECEIVER_REFERENCE -> {
|
||||
(extractVariable(proto, owner) as? KtBooleanValueParameterReference<Type, Diagnostic>?)?.invertIfNecessary(isInverted)
|
||||
}
|
||||
|
||||
PrimitiveExpressionType.CONSTANT ->
|
||||
(loadConstant(proto.constantValue) as? KtBooleanConstantReference<Type, Diagnostic>)?.invertIfNecessary(isInverted)
|
||||
|
||||
PrimitiveExpressionType.INSTANCE_CHECK -> {
|
||||
val variable = extractVariable(proto, owner) ?: return null
|
||||
val type = extractType(proto) ?: return null
|
||||
KtIsInstancePredicate(variable, type, isInverted)
|
||||
}
|
||||
|
||||
PrimitiveExpressionType.NULLABILITY_CHECK -> {
|
||||
val variable = extractVariable(proto, owner) ?: return null
|
||||
KtIsNullPredicate(variable, isInverted)
|
||||
}
|
||||
|
||||
null -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtBooleanExpression<Type, Diagnostic>.invertIfNecessary(shouldInvert: Boolean): KtBooleanExpression<Type, Diagnostic> =
|
||||
if (shouldInvert) KtLogicalNot(this) else this
|
||||
|
||||
private fun extractVariable(proto: ProtoBuf.Expression, owner: Owner): KtValueParameterReference<Type, Diagnostic>? {
|
||||
if (!proto.hasValueParameterReference()) return null
|
||||
|
||||
return extractVariable(proto.valueParameterReference - 1, owner)
|
||||
}
|
||||
|
||||
private fun ProtoBuf.Effect.InvocationKind.toDescriptorInvocationKind(): EventOccurrencesRange = when (this) {
|
||||
ProtoBuf.Effect.InvocationKind.AT_MOST_ONCE -> EventOccurrencesRange.AT_MOST_ONCE
|
||||
ProtoBuf.Effect.InvocationKind.EXACTLY_ONCE -> EventOccurrencesRange.EXACTLY_ONCE
|
||||
ProtoBuf.Effect.InvocationKind.AT_LEAST_ONCE -> EventOccurrencesRange.AT_LEAST_ONCE
|
||||
}
|
||||
|
||||
abstract fun extractVariable(valueParameterIndex: Int, owner: Owner): KtValueParameterReference<Type, Diagnostic>?
|
||||
|
||||
abstract fun extractType(proto: ProtoBuf.Expression): Type?
|
||||
|
||||
abstract fun loadConstant(value: ProtoBuf.Expression.ConstantValue): KtConstantReference<Type, Diagnostic>
|
||||
|
||||
|
||||
abstract fun getNotNull(): KtConstantReference<Type, Diagnostic>
|
||||
|
||||
abstract fun getWildcard(): KtConstantReference<Type, Diagnostic>
|
||||
|
||||
|
||||
private fun getComplexType(proto: ProtoBuf.Expression): ComplexExpressionType? {
|
||||
val isOrSequence = proto.orArgumentCount != 0
|
||||
val isAndSequence = proto.andArgumentCount != 0
|
||||
return when {
|
||||
isOrSequence && isAndSequence -> null
|
||||
isOrSequence -> ComplexExpressionType.OR_SEQUENCE
|
||||
isAndSequence -> ComplexExpressionType.AND_SEQUENCE
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun getPrimitiveType(proto: ProtoBuf.Expression): PrimitiveExpressionType? {
|
||||
// Expected to be one element, but can be empty (unknown expression) or contain several elements (invalid data)
|
||||
val expressionTypes: MutableList<PrimitiveExpressionType> = mutableListOf()
|
||||
|
||||
// Check for predicates
|
||||
when {
|
||||
proto.hasValueParameterReference() && proto.hasType() ->
|
||||
expressionTypes.add(PrimitiveExpressionType.INSTANCE_CHECK)
|
||||
|
||||
proto.hasValueParameterReference() && Flags.IS_NULL_CHECK_PREDICATE.get(proto.flags) ->
|
||||
expressionTypes.add(PrimitiveExpressionType.NULLABILITY_CHECK)
|
||||
}
|
||||
|
||||
// If message contains correct predicate, then predicate's type overrides type of value,
|
||||
// even is message has one
|
||||
if (expressionTypes.isNotEmpty()) {
|
||||
return expressionTypes.singleOrNull()
|
||||
}
|
||||
|
||||
// Otherwise, check if it is a value
|
||||
when {
|
||||
proto.hasValueParameterReference() && proto.valueParameterReference > 0 ->
|
||||
expressionTypes.add(PrimitiveExpressionType.VALUE_PARAMETER_REFERENCE)
|
||||
|
||||
proto.hasValueParameterReference() && proto.valueParameterReference == 0 ->
|
||||
expressionTypes.add(PrimitiveExpressionType.RECEIVER_REFERENCE)
|
||||
|
||||
proto.hasConstantValue() -> expressionTypes.add(PrimitiveExpressionType.CONSTANT)
|
||||
}
|
||||
|
||||
return expressionTypes.singleOrNull()
|
||||
}
|
||||
|
||||
private fun ProtoBuf.Expression.hasType(): Boolean = this.hasIsInstanceType() || this.hasIsInstanceTypeId()
|
||||
|
||||
// Arguments of expressions with such types are never other expressions
|
||||
private enum class PrimitiveExpressionType {
|
||||
VALUE_PARAMETER_REFERENCE,
|
||||
RECEIVER_REFERENCE,
|
||||
CONSTANT,
|
||||
INSTANCE_CHECK,
|
||||
NULLABILITY_CHECK
|
||||
}
|
||||
|
||||
// Expressions with such type can take other expressions as arguments.
|
||||
// Additionally, for performance reasons, "complex expression" and "primitive expression"
|
||||
// can co-exist in the one and the same message. If "primitive expression" is present
|
||||
// in the current message, it is treated as the first argument of "complex expression".
|
||||
private enum class ComplexExpressionType {
|
||||
AND_SEQUENCE,
|
||||
OR_SEQUENCE
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user