@@ -93,7 +93,7 @@ object Infos {
93
93
)
94
94
95
95
final class ReachabilityInfo private [Infos ] (
96
- val byClass : List [ReachabilityInfoInClass ],
96
+ val byClass : Array [ReachabilityInfoInClass ],
97
97
val globalFlags : ReachabilityInfo .Flags
98
98
)
99
99
@@ -109,14 +109,12 @@ object Infos {
109
109
/** Things from a given class that are reached by one method. */
110
110
final class ReachabilityInfoInClass private [Infos ] (
111
111
val className : ClassName ,
112
- val fieldsRead : List [FieldName ],
113
- val fieldsWritten : List [FieldName ],
114
- val staticFieldsRead : List [FieldName ],
115
- val staticFieldsWritten : List [FieldName ],
116
- val methodsCalled : List [MethodName ],
117
- val methodsCalledStatically : List [NamespacedMethodName ],
118
- val methodsCalledDynamicImport : List [NamespacedMethodName ],
119
- val jsNativeMembersUsed : List [MethodName ],
112
+ /* We use a single field for all members to reduce memory consumption:
113
+ * Typically, there are very few members reached in a single
114
+ * ReachabilityInfoInClass, so the overhead of having a field per type
115
+ * becomes significant in terms of memory usage.
116
+ */
117
+ val memberInfos : Array [MemberReachabilityInfo ], // nullable!
120
118
val flags : ReachabilityInfoInClass .Flags
121
119
)
122
120
@@ -132,8 +130,41 @@ object Infos {
132
130
final val FlagInstanceTestsUsed = 1 << 2
133
131
final val FlagClassDataAccessed = 1 << 3
134
132
final val FlagStaticallyReferenced = 1 << 4
133
+ final val FlagDynamicallyReferenced = 1 << 5
134
+ }
135
+
136
+ sealed trait MemberReachabilityInfo
137
+
138
+ final case class FieldReachable private [Infos ] (
139
+ val fieldName : FieldName ,
140
+ val read : Boolean = false ,
141
+ val written : Boolean = false
142
+ ) extends MemberReachabilityInfo
143
+
144
+ final case class StaticFieldReachable private [Infos ] (
145
+ val fieldName : FieldName ,
146
+ val read : Boolean = false ,
147
+ val written : Boolean = false
148
+ ) extends MemberReachabilityInfo
149
+
150
+ final case class MethodReachable private [Infos ] (
151
+ val methodName : MethodName
152
+ ) extends MemberReachabilityInfo
153
+
154
+ final case class MethodStaticallyReachable private [Infos ] (
155
+ val namespace : MemberNamespace ,
156
+ val methodName : MethodName
157
+ ) extends MemberReachabilityInfo
158
+
159
+ object MethodStaticallyReachable {
160
+ private [Infos ] def apply (m : NamespacedMethodName ): MethodStaticallyReachable =
161
+ MethodStaticallyReachable (m.namespace, m.methodName)
135
162
}
136
163
164
+ final case class JSNativeMemberReachable private [Infos ] (
165
+ val methodName : MethodName
166
+ ) extends MemberReachabilityInfo
167
+
137
168
final class ClassInfoBuilder (
138
169
private val className : ClassName ,
139
170
private val kind : ClassKind ,
@@ -374,52 +405,63 @@ object Infos {
374
405
setFlag(ReachabilityInfo .FlagUsedExponentOperator )
375
406
376
407
def result (): ReachabilityInfo =
377
- new ReachabilityInfo (byClass.valuesIterator.map(_.result()).toList , flags)
408
+ new ReachabilityInfo (byClass.valuesIterator.map(_.result()).toArray , flags)
378
409
}
379
410
380
411
final class ReachabilityInfoInClassBuilder (val className : ClassName ) {
381
- private val fieldsRead = mutable.Set .empty[FieldName ]
382
- private val fieldsWritten = mutable.Set .empty[FieldName ]
383
- private val staticFieldsRead = mutable.Set .empty[FieldName ]
384
- private val staticFieldsWritten = mutable.Set .empty[FieldName ]
412
+ private val fieldsUsed = mutable.Map .empty[FieldName , FieldReachable ]
413
+ private val staticFieldsUsed = mutable.Map .empty[FieldName , StaticFieldReachable ]
385
414
private val methodsCalled = mutable.Set .empty[MethodName ]
386
415
private val methodsCalledStatically = mutable.Set .empty[NamespacedMethodName ]
387
- private val methodsCalledDynamicImport = mutable.Set .empty[NamespacedMethodName ]
388
416
private val jsNativeMembersUsed = mutable.Set .empty[MethodName ]
389
417
private var flags : ReachabilityInfoInClass .Flags = 0
390
418
391
419
def addFieldRead (field : FieldName ): this .type = {
392
- fieldsRead += field
420
+ fieldsUsed(field) = fieldsUsed
421
+ .getOrElse(field, FieldReachable (field))
422
+ .copy(read = true )
393
423
this
394
424
}
395
425
396
426
def addFieldWritten (field : FieldName ): this .type = {
397
- fieldsWritten += field
427
+ fieldsUsed(field) = fieldsUsed
428
+ .getOrElse(field, FieldReachable (field))
429
+ .copy(written = true )
398
430
this
399
431
}
400
432
401
433
def addStaticFieldRead (field : FieldName ): this .type = {
402
- staticFieldsRead += field
434
+ staticFieldsUsed(field) = staticFieldsUsed
435
+ .getOrElse(field, StaticFieldReachable (field))
436
+ .copy(read = true )
437
+ setStaticallyReferenced()
403
438
this
404
439
}
405
440
406
441
def addStaticFieldWritten (field : FieldName ): this .type = {
407
- staticFieldsWritten += field
442
+ staticFieldsUsed(field) = staticFieldsUsed
443
+ .getOrElse(field, StaticFieldReachable (field))
444
+ .copy(written = true )
445
+ setStaticallyReferenced()
408
446
this
409
447
}
410
448
411
449
def addMethodCalled (method : MethodName ): this .type = {
412
450
methodsCalled += method
451
+ // Do not call setStaticallyReferenced: We call these methods on the object.
413
452
this
414
453
}
415
454
416
455
def addMethodCalledStatically (method : NamespacedMethodName ): this .type = {
417
456
methodsCalledStatically += method
457
+ setStaticallyReferenced()
418
458
this
419
459
}
420
460
421
461
def addMethodCalledDynamicImport (method : NamespacedMethodName ): this .type = {
422
- methodsCalledDynamicImport += method
462
+ // In terms of reachability, a dynamic import call is just a static call.
463
+ methodsCalledStatically += method
464
+ setFlag(ReachabilityInfoInClass .FlagDynamicallyReferenced )
423
465
this
424
466
}
425
467
@@ -449,23 +491,20 @@ object Infos {
449
491
setFlag(ReachabilityInfoInClass .FlagStaticallyReferenced )
450
492
451
493
def result (): ReachabilityInfoInClass = {
452
- new ReachabilityInfoInClass (
453
- className,
454
- fieldsRead = toLikelyEmptyList(fieldsRead),
455
- fieldsWritten = toLikelyEmptyList(fieldsWritten),
456
- staticFieldsRead = toLikelyEmptyList(staticFieldsRead),
457
- staticFieldsWritten = toLikelyEmptyList(staticFieldsWritten),
458
- methodsCalled = toLikelyEmptyList(methodsCalled),
459
- methodsCalledStatically = toLikelyEmptyList(methodsCalledStatically),
460
- methodsCalledDynamicImport = toLikelyEmptyList(methodsCalledDynamicImport),
461
- jsNativeMembersUsed = toLikelyEmptyList(jsNativeMembersUsed),
462
- flags = flags
463
- )
464
- }
494
+ val memberInfos : Array [MemberReachabilityInfo ] = (
495
+ fieldsUsed.valuesIterator ++
496
+ staticFieldsUsed.valuesIterator ++
497
+ methodsCalled.iterator.map(MethodReachable (_)) ++
498
+ methodsCalledStatically.iterator.map(MethodStaticallyReachable (_)) ++
499
+ jsNativeMembersUsed.iterator.map(JSNativeMemberReachable (_))
500
+ ).toArray
501
+
502
+ val memberInfosOrNull =
503
+ if (memberInfos.isEmpty) null
504
+ else memberInfos
465
505
466
- private def toLikelyEmptyList [A ](set : mutable.Set [A ]): List [A ] =
467
- if (set.isEmpty) Nil
468
- else set.toList
506
+ new ReachabilityInfoInClass (className, memberInfosOrNull, flags)
507
+ }
469
508
}
470
509
471
510
/** Generates the [[MethodInfo ]] of a
0 commit comments