Skip to content

Commit 4e4db16

Browse files
committed
Use more performant data structures for type analyses
1 parent 93e0485 commit 4e4db16

2 files changed

Lines changed: 68 additions & 56 deletions

File tree

OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/xta/InstantiatedTypesAnalysis.scala

Lines changed: 54 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import org.opalj.br.fpcf.properties.cg.NoCallers
2121
import org.opalj.br.instructions.INVOKESPECIAL
2222
import org.opalj.br.instructions.NEW
2323
import org.opalj.collection.immutable.UIDSet
24+
import org.opalj.collection.mutable.RefArrayBuffer
2425
import org.opalj.fpcf.EOptionP
2526
import org.opalj.fpcf.EPK
2627
import org.opalj.fpcf.EPS
@@ -37,9 +38,6 @@ import org.opalj.fpcf.Results
3738
import org.opalj.fpcf.SomeEPS
3839
import org.opalj.fpcf.UBP
3940

40-
import scala.collection.mutable
41-
import scala.collection.mutable.ListBuffer
42-
4341
/**
4442
* Marks types as instantiated if their constructor is invoked. Constructors invoked by subclass
4543
* constructors do not result in additional instantiated types.
@@ -106,7 +104,7 @@ class InstantiatedTypesAnalysis private[analyses] (
106104
seenCallers: Set[DeclaredMethod]
107105
): PropertyComputationResult = {
108106
var newSeenCallers = seenCallers
109-
val partialResults = new ListBuffer[PartialResult[TypeSetEntity, InstantiatedTypes]]()
107+
val partialResults = RefArrayBuffer.empty[PartialResult[TypeSetEntity, InstantiatedTypes]]
110108
for {
111109
(caller, _, _) callersUB.callers
112110
// if we already analyzed the caller, we do not need to do it twice
@@ -128,15 +126,15 @@ class InstantiatedTypesAnalysis private[analyses] (
128126
continuation(declaredMethod, declaredType, newSeenCallers)
129127
)
130128

131-
Results(reRegistration, partialResults)
129+
Results(reRegistration, partialResults.iterator())
132130
}
133131
}
134132

135133
private[this] def processSingleCaller(
136134
declaredMethod: DeclaredMethod,
137135
declaredType: ObjectType,
138136
caller: DeclaredMethod,
139-
partialResults: ListBuffer[PartialResult[TypeSetEntity, InstantiatedTypes]]
137+
partialResults: RefArrayBuffer[PartialResult[TypeSetEntity, InstantiatedTypes]]
140138
): Unit = {
141139
// a constructor is called by a non-constructor method, there will be an initialization.
142140
if (caller.name != "<init>") {
@@ -273,28 +271,29 @@ class InstantiatedTypesAnalysisScheduler(
273271
val packageIsClosed = p.get(ClosedPackagesKey)
274272
val declaredMethods = p.get(DeclaredMethodsKey)
275273
val entryPoints = p.get(InitialEntryPointsKey)
276-
val initialInstantiatedTypes = p.get(InitialInstantiatedTypesKey).toSet
274+
val initialInstantiatedTypes =
275+
UIDSet[ReferenceType](p.get(InitialInstantiatedTypesKey).toSeq: _*)
277276

278277
// While processing entry points and fields, we keep track of all array types we see, as
279278
// well as subtypes and lower-dimensional types. These types also need to be
280279
// pre-initialized. Note: This set only contains ArrayTypes whose element type is an
281280
// ObjectType. Arrays of primitive types can be ignored.
282-
val seenArrayTypes = mutable.Set[ArrayType]()
281+
val seenArrayTypes = UIDSet.newBuilder[ArrayType]
283282

284-
def initialize(setEntity: TypeSetEntity, types: Traversable[ReferenceType]): Unit = {
283+
def initialize(setEntity: TypeSetEntity, types: UIDSet[ReferenceType]): Unit = {
285284
ps.preInitialize(setEntity, InstantiatedTypes.key) {
286285
case UBP(typeSet)
287286
InterimEUBP(setEntity, typeSet.updated(types))
288287
case _: EPK[_, _]
289-
InterimEUBP(setEntity, InstantiatedTypes(UIDSet(types.toSeq: _*)))
288+
InterimEUBP(setEntity, InstantiatedTypes(types))
290289
case eps
291290
sys.error(s"unexpected property: $eps")
292291
}
293292
}
294293

295294
// Some cooperative analyses originally meant for RTA may require the global type set
296295
// to be pre-initialized. For that purpose, an empty type set is sufficient.
297-
initialize(p, Traversable.empty)
296+
initialize(p, UIDSet.empty)
298297

299298
def isRelevantArrayType(rt: Type): Boolean =
300299
rt.isArrayType && rt.asArrayType.elementType.isObjectType
@@ -305,8 +304,8 @@ class InstantiatedTypesAnalysisScheduler(
305304
ep entryPoints;
306305
dm = declaredMethods(ep)
307306
) {
308-
val typeFilters = mutable.Set[ReferenceType]()
309-
val arrayTypeAssignments = mutable.Set[ArrayType]()
307+
val typeFilters = UIDSet.newBuilder[ReferenceType]
308+
val arrayTypeAssignments = UIDSet.newBuilder[ArrayType]
310309

311310
if (!dm.definedMethod.isStatic) {
312311
typeFilters += dm.declaringClassType
@@ -330,18 +329,18 @@ class InstantiatedTypesAnalysisScheduler(
330329
}
331330

332331
// Initial assignments of ObjectTypes
333-
val objectTypeAssignments = initialInstantiatedTypes.filter(iit typeFilters.exists(tf
334-
p.classHierarchy.isSubtypeOf(iit, tf)))
332+
val objectTypeAssignments = initialInstantiatedTypes.filter(iit
333+
typeFilters.result().exists(tf p.classHierarchy.isSubtypeOf(iit, tf)))
335334

336-
val initialAssignment = arrayTypeAssignments ++ objectTypeAssignments
335+
val initialAssignment = objectTypeAssignments ++ arrayTypeAssignments.result()
337336

338337
val dmSetEntity = selectSetEntity(dm)
339338

340339
initialize(dmSetEntity, initialAssignment)
341340
}
342341

343342
// Returns true if the field's type indicates that the field should be pre-initialized.
344-
def fieldIsRelevant(f: Field): Boolean = {
343+
@inline def fieldIsRelevant(f: Field): Boolean = {
345344
// Only fields which are ArrayType or ObjectType are relevant.
346345
f.fieldType.isReferenceType &&
347346
// If the field is an ArrayType, then the array's element type must be an ObjectType.
@@ -372,25 +371,32 @@ class InstantiatedTypesAnalysisScheduler(
372371
// Assign initial types to all accessable fields.
373372
p.classFile(ot) match {
374373
case Some(cf)
375-
for (f cf.fields if fieldIsRelevant(f) && f.isNotFinal && fieldIsAccessible(f)) {
374+
for (f cf.fields if f.isNotFinal && fieldIsRelevant(f) && fieldIsAccessible(f)) {
376375
val fieldType = f.fieldType.asReferenceType
377-
378-
val initialAssignments = fieldType match {
379-
case ot: ObjectType
380-
initialInstantiatedTypes.filter(
381-
p.classHierarchy.isSubtypeOf(_, ot)
382-
)
383-
384-
case at: ArrayType
385-
seenArrayTypes += at
386-
387-
val dim = at.dimensions
388-
val et = at.elementType.asObjectType
389-
p.classHierarchy.allSubtypes(et, reflexive = true)
390-
.intersect(initialInstantiatedTypes).map(
391-
ArrayType(dim, _)
392-
)
393-
376+
import p.classHierarchy
377+
378+
val initialAssignments = if (fieldType.isObjectType) {
379+
val ot = fieldType.asObjectType
380+
initialInstantiatedTypes.foldLeft(UIDSet.newBuilder[ReferenceType]) {
381+
(assignments, iit)
382+
if (classHierarchy.isSubtypeOf(iit, ot)) {
383+
assignments += iit
384+
}
385+
assignments
386+
}.result()
387+
} else {
388+
val at = fieldType.asArrayType
389+
seenArrayTypes += at
390+
val dim = at.dimensions
391+
val et = at.elementType.asObjectType
392+
val allSubtypes = p.classHierarchy.allSubtypes(et, true)
393+
initialInstantiatedTypes.foldLeft(UIDSet.newBuilder[ReferenceType]) {
394+
(assignments, iit)
395+
if (allSubtypes.contains(iit.asObjectType)) {
396+
assignments += ArrayType(dim, iit)
397+
}
398+
assignments
399+
}.result()
394400
}
395401

396402
val fieldSetEntity = selectSetEntity(f)
@@ -407,25 +413,32 @@ class InstantiatedTypesAnalysisScheduler(
407413
// and initialize their type sets.
408414

409415
// Remember which ArrayTypes were processed, so we don't do it twice.
410-
val initializedArrayTypes = mutable.Set[ArrayType]()
416+
val initializedArrayTypes = new java.util.HashSet[ArrayType]()
411417

412418
def initializeArrayType(at: ArrayType): Unit = {
413419
// If this type has already been initialized, we skip it.
414420
if (initializedArrayTypes.contains(at)) {
415421
return ;
416422
}
417423

418-
initializedArrayTypes += at
424+
initializedArrayTypes.add(at)
419425

420426
val et = at.elementType.asObjectType
421-
val subtypes = p.classHierarchy.allSubtypes(et, reflexive = true).intersect(initialInstantiatedTypes)
427+
val allSubtypes = p.classHierarchy.allSubtypes(et, true)
428+
val subtypes =
429+
initialInstantiatedTypes.foldLeft(UIDSet.newBuilder[ReferenceType]) { (builder, iit)
430+
if (allSubtypes.contains(iit.asObjectType)) {
431+
builder += iit
432+
}
433+
builder
434+
}.result()
422435

423436
val dim = at.dimensions
424437
if (dim > 1) {
425438
// Initialize multidimensional ArrayType. E.g., if at == A[][] and A is a supertype of A1,
426439
// we need to assign A[] and A1[] to the type set of A[][].
427-
val assignedArrayTypes = subtypes.map(ArrayType(dim - 1, _))
428-
initialize(at, assignedArrayTypes)
440+
val assignedArrayTypes: UIDSet[ArrayType] = subtypes.map(ArrayType(dim - 1, _))
441+
initialize(at, assignedArrayTypes.asInstanceOf[UIDSet[ReferenceType]])
429442

430443
// After that, we also need to initialize the ArrayTypes which were just assigned. It is possible
431444
// that these were types which were not initially seen when processing entry points and fields.
@@ -436,6 +449,6 @@ class InstantiatedTypesAnalysisScheduler(
436449
}
437450
}
438451

439-
seenArrayTypes foreach initializeArrayType
452+
seenArrayTypes.result() foreach initializeArrayType
440453
}
441454
}

OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/xta/TypePropagationAnalysis.scala

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import org.opalj.br.fpcf.properties.cg.InstantiatedTypes
2323
import org.opalj.br.instructions.CHECKCAST
2424
import org.opalj.br.instructions.INVOKESTATIC
2525
import org.opalj.collection.immutable.UIDSet
26+
import org.opalj.collection.mutable.RefArrayBuffer
2627
import org.opalj.fpcf.EPS
2728
import org.opalj.fpcf.EUBP
2829
import org.opalj.fpcf.Entity
@@ -37,8 +38,6 @@ import org.opalj.fpcf.SomeEPS
3738
import org.opalj.fpcf.SomePartialResult
3839
import org.opalj.tac.fpcf.properties.TACAI
3940

40-
import scala.collection.mutable.ListBuffer
41-
4241
/**
4342
* This analysis handles the type propagation of XTA, MTA, FTA and CTA call graph
4443
* algorithms.
@@ -69,20 +68,20 @@ final class TypePropagationAnalysis private[analyses] (
6968

7069
implicit val state: TypePropagationState =
7170
new TypePropagationState(definedMethod, typeSetEntity, tacEP, instantiatedTypesEOptP, calleesEOptP)
72-
implicit val partialResults: ListBuffer[SomePartialResult] = new ListBuffer[SomePartialResult]()
71+
implicit val partialResults: RefArrayBuffer[SomePartialResult] = RefArrayBuffer.empty[SomePartialResult]
7372

7473
if (calleesEOptP.hasUBP)
7574
processCallees(calleesEOptP.ub)
7675
processTACStatements
7776
processArrayTypes(state.ownInstantiatedTypes)
7877

79-
returnResults(partialResults)
78+
returnResults(partialResults.iterator())
8079
}
8180

8281
/**
8382
* Processes the method upon initialization. Finds field/array accesses and wires up dependencies accordingly.
8483
*/
85-
private def processTACStatements(implicit state: State, partialResults: ListBuffer[SomePartialResult]): Unit = {
84+
private def processTACStatements(implicit state: State, partialResults: RefArrayBuffer[SomePartialResult]): Unit = {
8685
val bytecode = state.method.definedMethod.body.get
8786
val tac = state.tac
8887
tac.stmts.foreach {
@@ -155,9 +154,9 @@ final class TypePropagationAnalysis private[analyses] (
155154
state: State
156155
): ProperPropertyComputationResult = {
157156
state.updateCalleeDependee(eps)
158-
implicit val partialResults: ListBuffer[SomePartialResult] = new ListBuffer[SomePartialResult]()
157+
implicit val partialResults: RefArrayBuffer[SomePartialResult] = RefArrayBuffer.empty[SomePartialResult]
159158
processCallees(eps.ub)
160-
returnResults(partialResults)
159+
returnResults(partialResults.iterator())
161160
}
162161

163162
private def handleUpdateOfOwnTypeSet(
@@ -170,7 +169,7 @@ final class TypePropagationAnalysis private[analyses] (
170169
state.updateOwnInstantiatedTypesDependee(eps)
171170
val unseenTypes = UIDSet(eps.ub.dropOldest(previouslySeenTypes).toSeq: _*)
172171

173-
implicit val partialResults: ListBuffer[SomePartialResult] = new ListBuffer[SomePartialResult]()
172+
implicit val partialResults: RefArrayBuffer[SomePartialResult] = RefArrayBuffer.empty[SomePartialResult]
174173
for (fpe state.forwardPropagationEntities) {
175174
val filters = state.forwardPropagationFilters(fpe)
176175
val propagation = propagateTypes(fpe, unseenTypes, filters)
@@ -180,7 +179,7 @@ final class TypePropagationAnalysis private[analyses] (
180179

181180
processArrayTypes(unseenTypes)
182181

183-
returnResults(partialResults)
182+
returnResults(partialResults.iterator())
184183
}
185184

186185
private def handleUpdateOfBackwardPropagationTypeSet(
@@ -205,7 +204,7 @@ final class TypePropagationAnalysis private[analyses] (
205204
)(
206205
implicit
207206
state: State,
208-
partialResults: ListBuffer[SomePartialResult]
207+
partialResults: RefArrayBuffer[SomePartialResult]
209208
): Unit = {
210209
for (t unseenTypes if t.isArrayType; at = t.asArrayType if at.elementType.isReferenceType) {
211210
if (state.methodWritesArrays) {
@@ -231,7 +230,7 @@ final class TypePropagationAnalysis private[analyses] (
231230
)(
232231
implicit
233232
state: State,
234-
partialResults: ListBuffer[SomePartialResult]
233+
partialResults: RefArrayBuffer[SomePartialResult]
235234
): Unit = {
236235
val bytecode = state.method.definedMethod.body.get
237236
for {
@@ -263,7 +262,7 @@ final class TypePropagationAnalysis private[analyses] (
263262
)(
264263
implicit
265264
state: State,
266-
partialResults: ListBuffer[SomePartialResult]
265+
partialResults: RefArrayBuffer[SomePartialResult]
267266
): Unit = {
268267
val params = UIDSet.newBuilder[ReferenceType]
269268

@@ -299,7 +298,7 @@ final class TypePropagationAnalysis private[analyses] (
299298
)(
300299
implicit
301300
state: State,
302-
partialResults: ListBuffer[SomePartialResult]
301+
partialResults: RefArrayBuffer[SomePartialResult]
303302
): Unit = {
304303
val returnValueIsUsed = {
305304
val tacIndex = state.tac.properStmtIndexForPC(pc)
@@ -334,7 +333,7 @@ final class TypePropagationAnalysis private[analyses] (
334333
)(
335334
implicit
336335
state: State,
337-
partialResults: ListBuffer[SomePartialResult]
336+
partialResults: RefArrayBuffer[SomePartialResult]
338337
): Unit = {
339338
// Propagation from and to the same entity can be ignored.
340339
val typeSetEntity = selectTypeSetEntity(e)
@@ -356,7 +355,7 @@ final class TypePropagationAnalysis private[analyses] (
356355
)(
357356
implicit
358357
state: State,
359-
partialResults: ListBuffer[SomePartialResult]
358+
partialResults: RefArrayBuffer[SomePartialResult]
360359
): Unit = {
361360
val typeSetEntity = selectTypeSetEntity(e)
362361
if (typeSetEntity == state.typeSetEntity) {

0 commit comments

Comments
 (0)