@@ -8,7 +8,9 @@ package viper.server.vsi
88
99import viper .silver .utility .CacheHelper
1010
11- import scala .collection .mutable .{ListBuffer , Map => MutableMap }
11+ import java .security .MessageDigest
12+ import java .time .Instant
13+ import scala .collection .mutable .ListBuffer
1214
1315/** The goal of this generic caching trait is to provide:
1416 *
@@ -46,10 +48,16 @@ abstract class Cache {
4648 * The inner map is referred to as the fileCache. As the name indicates, it stores, for each
4749 * file, a number of hashes and corresponding cache entries.
4850 */
49- protected val _cache : MutableMap [String , FileCash ] = MutableMap ()
50- type FileCash = MutableMap [String , List [CacheEntry ]]
51+ type FileKey = String
52+ type MemberHash = String
53+ type FileCache = Map [MemberHash , List [CacheEntry ]]
54+ type Cache = Map [FileKey , FileCache ]
55+ protected var _cache : Cache = Map ()
5156
52- protected val _program_cache : MutableMap [Ast , MutableMap [CacheableMember , List [Member ]]] = MutableMap ()
57+ type ProgramHash = String
58+ type DependencyHash = String
59+ type DependencyMap = Map [MemberHash , DependencyHash ]
60+ protected var _program_cache : Map [ProgramHash , DependencyMap ] = Map ()
5361
5462 /** This method transforms a program and returns verification results based on the cache's
5563 * current state.
@@ -72,40 +80,40 @@ abstract class Cache {
7280 // read errors from cache
7381 val cachable_members = input_prog.decompose()
7482
75- val prog_dependencies = _program_cache.find({
76- case (k, _) => k.equals (input_prog)
77- })
83+ val input_prog_hex = MessageDigest .getInstance( " SHA-1 " )
84+ .digest (input_prog.toString.getBytes( " UTF-8 " ) )
85+ .map( " %02x " .format(_)).mkString
7886
79- prog_dependencies match {
80- case Some ((_, dep_map)) =>
81- cachable_members.foreach(cm => {
82- val dependencies = dep_map(cm)
83- get(file_key, cm, dependencies) match {
84- case Some (matched_entry) =>
85- concerningsToCache += cm.transform
86- cache_entries += matched_entry
87- case None =>
88- // Nothing in cache, request verification
89- concerningsToVerify += cm
90- }
91- })
87+ val prog_dependencies = _program_cache.get(input_prog_hex)
88+
89+ val dep_map = prog_dependencies match {
90+ case Some (dep_map) => dep_map
9291 case None =>
93- val dep_map = MutableMap [ CacheableMember , List [ Member ] ]()
92+ var dep_map = Map [ String , String ]()
9493 cachable_members.foreach(cm => {
95- val dependencies = cm.getDependencies(input_prog)
96- dep_map += (cm -> dependencies)
97- get(file_key, cm, dependencies) match {
98- case Some (matched_entry) =>
99- concerningsToCache += cm.transform
100- cache_entries += matched_entry
101- case None =>
102- // Nothing in cache, request verification
103- concerningsToVerify += cm
104- }
94+ val concerning_hash = cm.hash()
95+ val dependency_hash = CacheHelper .buildHash(concerning_hash + cm.getDependencies(input_prog).map(_.hash()).mkString(" " ))
96+ dep_map = dep_map + (concerning_hash -> dependency_hash)
10597 })
106- _program_cache += (input_prog -> dep_map)
98+ _program_cache = _program_cache + (input_prog_hex -> dep_map)
99+ dep_map
107100 }
108101
102+ cachable_members.foreach(cm => {
103+ val concerning_hash = cm.hash()
104+ val dependency_hash = dep_map(concerning_hash)
105+
106+ get(file_key, concerning_hash, dependency_hash) match {
107+ case Some (matched_entry) =>
108+ matched_entry.lastAccessed = Instant .now()
109+ concerningsToCache += cm.transform
110+ cache_entries += matched_entry
111+ case None =>
112+ // Nothing in cache, request verification
113+ concerningsToCache += cm
114+ }
115+ })
116+
109117 val all_concernings : List [CacheableMember ] = concerningsToCache.toList ++ concerningsToVerify.toList
110118 val output_prog : Ast = input_prog.compose(all_concernings)
111119 (output_prog, cache_entries.toList)
@@ -114,18 +122,13 @@ abstract class Cache {
114122 /** Utility function to retrieve entries for single members.
115123 * */
116124 final def get (file_key : String ,
117- key : CacheableMember ,
118- dependencies : List [Member ]): Option [CacheEntry ] = {
119-
120- val concerning_hash = key.hash()
121- val dependencies_hash = dependencies.map(_.hash()).mkString(" " )
122- val dependency_hash = CacheHelper .buildHash(concerning_hash + dependencies_hash)
125+ concerning_hash : String ,
126+ dependency_hash : String ): Option [CacheEntry ] = {
123127 assert(concerning_hash != null )
124-
125128 for {
126129 fileCache <- _cache.get(file_key)
127130 cacheEntries <- fileCache.get(concerning_hash)
128- validEntry <- cacheEntries.find(_.depency_hash == dependency_hash)
131+ validEntry <- cacheEntries.find(_.dependencyHash == dependency_hash)
129132 } yield validEntry
130133 }
131134
@@ -147,7 +150,7 @@ abstract class Cache {
147150 val concerning_hash = key.hash()
148151 val dependencies_hash = dependencies.map(_.hash()).mkString(" " )
149152 val dependency_hash = CacheHelper .buildHash(concerning_hash + dependencies_hash)
150- val new_entry : CacheEntry = new CacheEntry (key, content, dependency_hash)
153+ val new_entry : CacheEntry = CacheEntry (key.hash() , content, dependency_hash)
151154
152155 assert(concerning_hash != null )
153156
@@ -156,28 +159,32 @@ abstract class Cache {
156159 val existing_entries = fileCache.getOrElse(concerning_hash, Nil )
157160 val updated_cacheEntries = new_entry :: existing_entries
158161
159- fileCache(concerning_hash) = updated_cacheEntries
162+ val updatedFileCache = fileCache + (concerning_hash -> updated_cacheEntries)
163+ _cache = _cache + (file_key -> updatedFileCache)
160164 updated_cacheEntries
161165 case None =>
162166 // if file not in cache yet, create new map entry for it. Recall the function.
163- _cache += (file_key -> collection.mutable. Map [ String , List [ CacheEntry ]] ())
167+ _cache = _cache + (file_key -> Map ())
164168 update(file_key, key, dependencies, content)
165169 }
166170 }
167171
168172 /** Resets the cache for a particular file.
169173 * */
170174 def forgetFile (file_key : String ): Option [String ] = {
171- _cache.remove (file_key) match {
175+ val key = _cache.get (file_key) match {
172176 case Some (_) => Some (file_key)
173177 case None => None
174178 }
179+ _cache -= file_key
180+ key
175181 }
176182
177183 /** Resets the entire cache.
178184 * */
179185 def resetCache (): Unit = {
180- _cache.clear()
186+ _program_cache = Map ()
187+ _cache = Map ()
181188 }
182189}
183190
@@ -194,7 +201,7 @@ abstract class Cache {
194201 * created. If, at some point, a members dependency hash is no longer equal to the one stored
195202 * here, the entry is no longer valid.
196203 * */
197- case class CacheEntry (concerning : CacheableMember , content : CacheContent , depency_hash : String )
204+ case class CacheEntry (concerningHash : String , content : CacheContent , dependencyHash : String , created : Instant = Instant .now(), var lastAccessed : Instant = Instant .now() )
198205
199206
200207// ===== AUXILIARY TRAITS ==================================================================
@@ -245,7 +252,7 @@ trait CacheableMember extends Member {
245252 * terms of verification.
246253 *
247254 * A member may hit the cache, but the attached verification results might be invalid. Reason
248- * for this is that other members in the program have might have changed in such a way that
255+ * for this is that other members in the program might have changed in such a way that
249256 * they influenced this member's verification outcome. These influencing members are called
250257 * dependencies and need to be checked when retrieving a member's attached result from cache.
251258 * */
0 commit comments