@@ -64,14 +64,16 @@ private[analyzer] object InfoLoader {
64
64
case object InitialIRCheck extends IRCheckMode
65
65
case object InternalIRCheck extends IRCheckMode
66
66
67
+ private type MethodInfos = Array [Map [MethodName , Infos .MethodInfo ]]
68
+
67
69
private class ClassInfoCache (className : ClassName , irLoader : IRLoader , irCheckMode : InfoLoader .IRCheckMode ) {
68
70
private var cacheUsed : Boolean = false
69
71
private var version : Version = Version .Unversioned
70
72
private var info : Future [Infos .ClassInfo ] = _
71
73
72
- private val methodsInfoCaches = MethodDefsInfosCache ( )
73
- private val jsConstructorInfoCache = new JSConstructorDefInfoCache ()
74
- private val exportedMembersInfoCaches = JSMethodPropDefsInfosCache ()
74
+ private var prevMethodInfos : MethodInfos = Array .fill( MemberNamespace . Count )( Map .empty )
75
+ private var prevJSCtorInfo : Option [ Infos . ReachabilityInfo ] = None
76
+ private var prevJSMethodPropDefInfos : List [ Infos . ReachabilityInfo ] = Nil
75
77
76
78
def loadInfo (logger : Logger )(implicit ec : ExecutionContext ): Future [Infos .ClassInfo ] = synchronized {
77
79
/* If the cache was already used in this run, the classDef and info are
@@ -115,171 +117,86 @@ private[analyzer] object InfoLoader {
115
117
}
116
118
117
119
private def generateInfos (classDef : ClassDef ): Infos .ClassInfo = {
118
- val builder = new Infos .ClassInfoBuilder (classDef.className,
119
- classDef.kind, classDef.superClass.map(_.name),
120
- classDef.interfaces.map(_.name), classDef.jsNativeLoadSpec)
121
-
122
- classDef.fields.foreach {
123
- case FieldDef (flags, FieldIdent (name), _, ftpe) =>
124
- if (! flags.namespace.isStatic)
125
- builder.maybeAddReferencedFieldClass(name, ftpe)
126
-
127
- case _ : JSFieldDef =>
128
- // Nothing to do.
129
- }
120
+ val referencedFieldClasses = Infos .genReferencedFieldClasses(classDef.fields)
130
121
131
- classDef.methods.foreach { method =>
132
- builder.addMethod(methodsInfoCaches.getInfo(method))
133
- }
122
+ prevMethodInfos = genMethodInfos(classDef.methods, prevMethodInfos)
123
+ prevJSCtorInfo = genJSCtorInfo(classDef.jsConstructor, prevJSCtorInfo)
124
+ prevJSMethodPropDefInfos =
125
+ genJSMethodPropDefInfos(classDef.jsMethodProps, prevJSMethodPropDefInfos)
134
126
135
- classDef.jsConstructor.foreach { jsConstructor =>
136
- builder.addExportedMember(jsConstructorInfoCache.getInfo(jsConstructor))
137
- }
138
-
139
- for (info <- exportedMembersInfoCaches.getInfos(classDef.jsMethodProps))
140
- builder.addExportedMember(info)
127
+ val exportedMembers = prevJSCtorInfo.toList ::: prevJSMethodPropDefInfos
141
128
142
129
/* We do not cache top-level exports, because they're quite rare,
143
130
* and usually quite small when they exist.
144
131
*/
145
- classDef.topLevelExportDefs.foreach { topLevelExportDef =>
146
- builder.addTopLevelExport(Infos .generateTopLevelExportInfo(classDef.name.name, topLevelExportDef))
147
- }
132
+ val topLevelExports = classDef.topLevelExportDefs
133
+ .map(Infos .generateTopLevelExportInfo(classDef.name.name, _))
148
134
149
- classDef.jsNativeMembers.foreach(builder.addJSNativeMember(_))
135
+ val jsNativeMembers = classDef.jsNativeMembers
136
+ .map(m => m.name.name -> m.jsNativeLoadSpec).toMap
150
137
151
- builder.result()
138
+ new Infos .ClassInfo (classDef.className, classDef.kind,
139
+ classDef.superClass.map(_.name), classDef.interfaces.map(_.name),
140
+ classDef.jsNativeLoadSpec, referencedFieldClasses, prevMethodInfos,
141
+ jsNativeMembers, exportedMembers, topLevelExports)
152
142
}
153
143
154
144
/** Returns true if the cache has been used and should be kept. */
155
145
def cleanAfterRun (): Boolean = synchronized {
156
146
val result = cacheUsed
157
147
cacheUsed = false
158
- if (result) {
159
- // No point in cleaning the inner caches if the whole class disappears
160
- methodsInfoCaches.cleanAfterRun()
161
- jsConstructorInfoCache.cleanAfterRun()
162
- exportedMembersInfoCaches.cleanAfterRun()
163
- }
164
148
result
165
149
}
166
150
}
167
151
168
- private final class MethodDefsInfosCache private (
169
- val caches : Array [mutable.Map [MethodName , MethodDefInfoCache ]])
170
- extends AnyVal {
171
-
172
- def getInfo (methodDef : MethodDef ): Infos .MethodInfo = {
173
- val cache = caches(methodDef.flags.namespace.ordinal)
174
- .getOrElseUpdate(methodDef.methodName, new MethodDefInfoCache )
175
- cache.getInfo(methodDef)
176
- }
177
-
178
- def cleanAfterRun (): Unit = {
179
- caches.foreach(_.filterInPlace((_, cache) => cache.cleanAfterRun()))
180
- }
181
- }
152
+ private def genMethodInfos (methods : List [MethodDef ],
153
+ prevMethodInfos : MethodInfos ): MethodInfos = {
182
154
183
- private object MethodDefsInfosCache {
184
- def apply (): MethodDefsInfosCache = {
185
- new MethodDefsInfosCache (
186
- Array .fill(MemberNamespace .Count )(mutable.Map .empty))
187
- }
188
- }
155
+ val builders = Array .fill(MemberNamespace .Count )(Map .newBuilder[MethodName , Infos .MethodInfo ])
189
156
190
- /* For JS method and property definitions, we use their index in the list of
191
- * `linkedClass.exportedMembers` as their identity. We cannot use their name
192
- * because the name itself is a `Tree`.
193
- *
194
- * If there is a different number of exported members than in a previous run,
195
- * we always recompute everything. This is fine because, for any given class,
196
- * either all JS methods and properties are reachable, or none are. So we're
197
- * only missing opportunities for incrementality in the case where JS members
198
- * are added or removed in the original .sjsir, which is not a big deal.
199
- */
200
- private final class JSMethodPropDefsInfosCache private (
201
- private var caches : Array [JSMethodPropDefInfoCache ]) {
202
-
203
- def getInfos (members : List [JSMethodPropDef ]): List [Infos .ReachabilityInfo ] = {
204
- if (members.isEmpty) {
205
- caches = null
206
- Nil
207
- } else {
208
- val membersSize = members.size
209
- if (caches == null || membersSize != caches.size)
210
- caches = Array .fill(membersSize)(new JSMethodPropDefInfoCache )
211
-
212
- for ((member, i) <- members.zipWithIndex) yield {
213
- caches(i).getInfo(member)
214
- }
215
- }
216
- }
157
+ methods.foreach { method =>
158
+ val info = prevMethodInfos(method.flags.namespace.ordinal)
159
+ .get(method.methodName)
160
+ .filter(_.version.sameVersion(method.version))
161
+ .getOrElse(Infos .generateMethodInfo(method))
217
162
218
- def cleanAfterRun (): Unit = {
219
- if (caches != null )
220
- caches.foreach(_.cleanAfterRun())
163
+ builders(method.flags.namespace.ordinal) += method.methodName -> info
221
164
}
222
- }
223
165
224
- private object JSMethodPropDefsInfosCache {
225
- def apply (): JSMethodPropDefsInfosCache =
226
- new JSMethodPropDefsInfosCache (null )
166
+ builders.map(_.result())
227
167
}
228
168
229
- private abstract class AbstractMemberInfoCache [Def <: VersionedMemberDef , Info ] {
230
- private var cacheUsed : Boolean = false
231
- private var lastVersion : Version = Version .Unversioned
232
- private var info : Info = _
233
-
234
- final def getInfo (member : Def ): Info = {
235
- update(member)
236
- info
237
- }
238
-
239
- private final def update (member : Def ): Unit = {
240
- if (! cacheUsed) {
241
- cacheUsed = true
242
- val newVersion = member.version
243
- if (! lastVersion.sameVersion(newVersion)) {
244
- info = computeInfo(member)
245
- lastVersion = newVersion
246
- }
247
- }
248
- }
249
-
250
- protected def computeInfo (member : Def ): Info
251
-
252
- /** Returns true if the cache has been used and should be kept. */
253
- final def cleanAfterRun (): Boolean = {
254
- val result = cacheUsed
255
- cacheUsed = false
256
- result
169
+ private def genJSCtorInfo (jsCtor : Option [JSConstructorDef ],
170
+ prevJSCtorInfo : Option [Infos .ReachabilityInfo ]): Option [Infos .ReachabilityInfo ] = {
171
+ jsCtor.map { ctor =>
172
+ prevJSCtorInfo
173
+ .filter(_.version.sameVersion(ctor.version))
174
+ .getOrElse(Infos .generateJSConstructorInfo(ctor))
257
175
}
258
176
}
259
177
260
- private final class MethodDefInfoCache
261
- extends AbstractMemberInfoCache [MethodDef , Infos .MethodInfo ] {
262
-
263
- protected def computeInfo (member : MethodDef ): Infos .MethodInfo =
264
- Infos .generateMethodInfo(member)
265
- }
266
-
267
- private final class JSConstructorDefInfoCache
268
- extends AbstractMemberInfoCache [JSConstructorDef , Infos .ReachabilityInfo ] {
269
-
270
- protected def computeInfo (member : JSConstructorDef ): Infos .ReachabilityInfo =
271
- Infos .generateJSConstructorInfo(member)
272
- }
273
-
274
- private final class JSMethodPropDefInfoCache
275
- extends AbstractMemberInfoCache [JSMethodPropDef , Infos .ReachabilityInfo ] {
276
-
277
- protected def computeInfo (member : JSMethodPropDef ): Infos .ReachabilityInfo = {
278
- member match {
279
- case methodDef : JSMethodDef =>
280
- Infos .generateJSMethodInfo(methodDef)
281
- case propertyDef : JSPropertyDef =>
282
- Infos .generateJSPropertyInfo(propertyDef)
178
+ private def genJSMethodPropDefInfos (jsMethodProps : List [JSMethodPropDef ],
179
+ prevJSMethodPropDefInfos : List [Infos .ReachabilityInfo ]): List [Infos .ReachabilityInfo ] = {
180
+ /* For JS method and property definitions, we use their index in the list of
181
+ * `linkedClass.exportedMembers` as their identity. We cannot use their name
182
+ * because the name itself is a `Tree`.
183
+ *
184
+ * If there is a different number of exported members than in a previous run,
185
+ * we always recompute everything. This is fine because, for any given class,
186
+ * either all JS methods and properties are reachable, or none are. So we're
187
+ * only missing opportunities for incrementality in the case where JS members
188
+ * are added or removed in the original .sjsir, which is not a big deal.
189
+ */
190
+
191
+ if (prevJSMethodPropDefInfos.size != jsMethodProps.size) {
192
+ // Regenerate everything.
193
+ jsMethodProps.map(Infos .generateJSMethodPropDefInfo(_))
194
+ } else {
195
+ for {
196
+ (prevInfo, member) <- prevJSMethodPropDefInfos.zip(jsMethodProps)
197
+ } yield {
198
+ if (prevInfo.version.sameVersion(member.version)) prevInfo
199
+ else Infos .generateJSMethodPropDefInfo(member)
283
200
}
284
201
}
285
202
}
0 commit comments