use PsiFileGist instead of time-expensive indices for contract inference
[idea/community.git] / java / java-analysis-impl / src / com / intellij / codeInspection / dataFlow / MethodDataExternalizer.kt
1 /*
2  * Copyright 2000-2016 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.codeInspection.dataFlow
17
18 import com.intellij.util.io.DataExternalizer
19 import com.intellij.util.io.DataInputOutputUtil
20 import java.io.DataInput
21 import java.io.DataOutput
22
23 /**
24  * @author peter
25  */
26 internal object MethodDataExternalizer : DataExternalizer<Map<Int, MethodData>> {
27
28   override fun save(out: DataOutput, value: Map<Int, MethodData>?) {
29     writeList(out, value!!.toList()) { DataInputOutputUtil.writeINT(out, it.first); writeMethod(out, it.second) }
30   }
31
32   override fun read(input: DataInput) = readList(input) { DataInputOutputUtil.readINT(input) to readMethod(input) }.toMap()
33
34   private fun writeMethod(out: DataOutput, data: MethodData) {
35     writeNullable(out, data.nullity) { writeNullity(out, it) }
36     writeNullable(out, data.purity) { writePurity(out, it) }
37     writeList(out, data.contracts) { writeContract(out, it) }
38     DataInputOutputUtil.writeINT(out, data.bodyStart)
39     DataInputOutputUtil.writeINT(out, data.bodyEnd)
40   }
41
42   private fun readMethod(input: DataInput): MethodData {
43     val nullity = readNullable(input) { readNullity(input) }
44     val purity = readNullable(input) { readPurity(input) }
45     val contracts = readList(input) { readContract(input) }
46     return MethodData(nullity, purity, contracts, DataInputOutputUtil.readINT(input), DataInputOutputUtil.readINT(input))
47   }
48
49   private fun writeNullity(out: DataOutput, nullity: NullityInferenceResult) = when (nullity) {
50     is NullityInferenceResult.Predefined -> { out.writeByte(0); out.writeByte(nullity.value.ordinal) }
51     is NullityInferenceResult.FromDelegate -> { out.writeByte(1); writeRanges(out, nullity.delegateCalls) }
52     else -> throw IllegalArgumentException(nullity.toString())
53   }
54   private fun readNullity(input: DataInput): NullityInferenceResult = when (input.readByte().toInt()) {
55     0 -> NullityInferenceResult.Predefined(Nullness.values()[input.readByte().toInt()])
56     else -> NullityInferenceResult.FromDelegate(readRanges(input))
57   }
58
59   private fun writeRanges(out: DataOutput, ranges: List<ExpressionRange>) = writeList(out, ranges) { writeRange(out, it) }
60   private fun readRanges(input: DataInput) = readList(input) { readRange(input) }
61
62   private fun writeRange(out: DataOutput, range: ExpressionRange) {
63     DataInputOutputUtil.writeINT(out, range.startOffset)
64     DataInputOutputUtil.writeINT(out, range.endOffset)
65   }
66   private fun readRange(input: DataInput) = ExpressionRange(DataInputOutputUtil.readINT(input), DataInputOutputUtil.readINT(input))
67
68   private fun writePurity(out: DataOutput, purity: PurityInferenceResult) {
69     writeRanges(out, purity.mutatedRefs)
70     writeNullable(out, purity.singleCall) { writeRange(out, it) }
71   }
72   private fun readPurity(input: DataInput) = PurityInferenceResult(readRanges(input), readNullable(input) { readRange(input) })
73
74   private fun writeContract(out: DataOutput, contract: PreContract): Unit = when (contract) {
75     is DelegationContract -> { out.writeByte(0); writeRange(out, contract.expression); out.writeBoolean(contract.negated) }
76     is KnownContract -> { out.writeByte(1);
77       writeContractArguments(out, contract.contract.arguments.toList())
78       out.writeByte(contract.contract.returnValue.ordinal)
79     }
80     is MethodCallContract -> { out.writeByte(2);
81       writeRange(out, contract.call);
82       writeList(out, contract.states) { writeContractArguments(out, it) }
83     }
84     is NegatingContract -> { out.writeByte(3); writeContract(out, contract.negated) }
85     is SideEffectFilter -> { out.writeByte(4);
86       writeRanges(out, contract.expressionsToCheck)
87       writeList(out, contract.contracts) { writeContract(out, it) }
88     }
89     else -> throw IllegalArgumentException(contract.toString())
90   }
91   private fun readContract(input: DataInput): PreContract = when (input.readByte().toInt()) {
92     0 -> DelegationContract(readRange(input), input.readBoolean())
93     1 -> KnownContract(MethodContract(readContractArguments(input).toTypedArray(), readValueConstraint(input)))
94     2 -> MethodCallContract(readRange(input), readList(input) { readContractArguments(input) })
95     3 -> NegatingContract(readContract(input))
96     else -> SideEffectFilter(readRanges(input), readList(input) { readContract(input) })
97   }
98
99   private fun writeContractArguments(out: DataOutput, arguments: List<MethodContract.ValueConstraint>) =
100       writeList(out, arguments) { out.writeByte(it.ordinal) }
101   private fun readContractArguments(input: DataInput) = readList(input, { readValueConstraint(input) })
102
103   private fun readValueConstraint(input: DataInput) = MethodContract.ValueConstraint.values()[input.readByte().toInt()]
104
105 }
106
107 // utils
108
109 private fun <T> writeNullable(out: DataOutput, value: T?, writeItem: (T) -> Unit) = when (value) {
110   null -> out.writeBoolean(false)
111   else -> { out.writeBoolean(true); writeItem(value) }
112 }
113 private fun <T> readNullable(input: DataInput, readEach: () -> T): T? = if (input.readBoolean()) readEach() else null
114
115 private fun <T> writeList(out: DataOutput, list: List<T>, writeEach: (T) -> Unit) {
116   DataInputOutputUtil.writeINT(out, list.size)
117   list.forEach(writeEach)
118 }
119 private fun <T> readList(input: DataInput, readEach: () -> T) = (0 until DataInputOutputUtil.readINT(input)).map { readEach() }