Skip to content

Commit 418d383

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

3 files changed

Lines changed: 101 additions & 81 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: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ package analyses
66
package cg
77
package xta
88

9+
import scala.collection.JavaConverters.asScalaIteratorConverter
10+
911
import org.opalj.br.ArrayType
1012
import org.opalj.br.Code
1113
import org.opalj.br.DeclaredMethod
@@ -23,6 +25,7 @@ import org.opalj.br.fpcf.properties.cg.InstantiatedTypes
2325
import org.opalj.br.instructions.CHECKCAST
2426
import org.opalj.br.instructions.INVOKESTATIC
2527
import org.opalj.collection.immutable.UIDSet
28+
import org.opalj.collection.mutable.RefArrayBuffer
2629
import org.opalj.fpcf.EPS
2730
import org.opalj.fpcf.EUBP
2831
import org.opalj.fpcf.Entity
@@ -37,8 +40,6 @@ import org.opalj.fpcf.SomeEPS
3740
import org.opalj.fpcf.SomePartialResult
3841
import org.opalj.tac.fpcf.properties.TACAI
3942

40-
import scala.collection.mutable.ListBuffer
41-
4243
/**
4344
* This analysis handles the type propagation of XTA, MTA, FTA and CTA call graph
4445
* algorithms.
@@ -69,20 +70,20 @@ final class TypePropagationAnalysis private[analyses] (
6970

7071
implicit val state: TypePropagationState =
7172
new TypePropagationState(definedMethod, typeSetEntity, tacEP, instantiatedTypesEOptP, calleesEOptP)
72-
implicit val partialResults: ListBuffer[SomePartialResult] = new ListBuffer[SomePartialResult]()
73+
implicit val partialResults: RefArrayBuffer[SomePartialResult] = RefArrayBuffer.empty[SomePartialResult]
7374

7475
if (calleesEOptP.hasUBP)
7576
processCallees(calleesEOptP.ub)
7677
processTACStatements
7778
processArrayTypes(state.ownInstantiatedTypes)
7879

79-
returnResults(partialResults)
80+
returnResults(partialResults.iterator())
8081
}
8182

8283
/**
8384
* Processes the method upon initialization. Finds field/array accesses and wires up dependencies accordingly.
8485
*/
85-
private def processTACStatements(implicit state: State, partialResults: ListBuffer[SomePartialResult]): Unit = {
86+
private def processTACStatements(implicit state: State, partialResults: RefArrayBuffer[SomePartialResult]): Unit = {
8687
val bytecode = state.method.definedMethod.body.get
8788
val tac = state.tac
8889
tac.stmts.foreach {
@@ -155,9 +156,9 @@ final class TypePropagationAnalysis private[analyses] (
155156
state: State
156157
): ProperPropertyComputationResult = {
157158
state.updateCalleeDependee(eps)
158-
implicit val partialResults: ListBuffer[SomePartialResult] = new ListBuffer[SomePartialResult]()
159+
implicit val partialResults: RefArrayBuffer[SomePartialResult] = RefArrayBuffer.empty[SomePartialResult]
159160
processCallees(eps.ub)
160-
returnResults(partialResults)
161+
returnResults(partialResults.iterator())
161162
}
162163

163164
private def handleUpdateOfOwnTypeSet(
@@ -170,8 +171,8 @@ final class TypePropagationAnalysis private[analyses] (
170171
state.updateOwnInstantiatedTypesDependee(eps)
171172
val unseenTypes = UIDSet(eps.ub.dropOldest(previouslySeenTypes).toSeq: _*)
172173

173-
implicit val partialResults: ListBuffer[SomePartialResult] = new ListBuffer[SomePartialResult]()
174-
for (fpe state.forwardPropagationEntities) {
174+
implicit val partialResults: RefArrayBuffer[SomePartialResult] = RefArrayBuffer.empty[SomePartialResult]
175+
for (fpe state.forwardPropagationEntities.iterator().asScala) {
175176
val filters = state.forwardPropagationFilters(fpe)
176177
val propagation = propagateTypes(fpe, unseenTypes, filters)
177178
if (propagation.isDefined)
@@ -180,7 +181,7 @@ final class TypePropagationAnalysis private[analyses] (
180181

181182
processArrayTypes(unseenTypes)
182183

183-
returnResults(partialResults)
184+
returnResults(partialResults.iterator())
184185
}
185186

186187
private def handleUpdateOfBackwardPropagationTypeSet(
@@ -205,7 +206,7 @@ final class TypePropagationAnalysis private[analyses] (
205206
)(
206207
implicit
207208
state: State,
208-
partialResults: ListBuffer[SomePartialResult]
209+
partialResults: RefArrayBuffer[SomePartialResult]
209210
): Unit = {
210211
for (t unseenTypes if t.isArrayType; at = t.asArrayType if at.elementType.isReferenceType) {
211212
if (state.methodWritesArrays) {
@@ -231,7 +232,7 @@ final class TypePropagationAnalysis private[analyses] (
231232
)(
232233
implicit
233234
state: State,
234-
partialResults: ListBuffer[SomePartialResult]
235+
partialResults: RefArrayBuffer[SomePartialResult]
235236
): Unit = {
236237
val bytecode = state.method.definedMethod.body.get
237238
for {
@@ -263,7 +264,7 @@ final class TypePropagationAnalysis private[analyses] (
263264
)(
264265
implicit
265266
state: State,
266-
partialResults: ListBuffer[SomePartialResult]
267+
partialResults: RefArrayBuffer[SomePartialResult]
267268
): Unit = {
268269
val params = UIDSet.newBuilder[ReferenceType]
269270

@@ -299,7 +300,7 @@ final class TypePropagationAnalysis private[analyses] (
299300
)(
300301
implicit
301302
state: State,
302-
partialResults: ListBuffer[SomePartialResult]
303+
partialResults: RefArrayBuffer[SomePartialResult]
303304
): Unit = {
304305
val returnValueIsUsed = {
305306
val tacIndex = state.tac.properStmtIndexForPC(pc)
@@ -334,7 +335,7 @@ final class TypePropagationAnalysis private[analyses] (
334335
)(
335336
implicit
336337
state: State,
337-
partialResults: ListBuffer[SomePartialResult]
338+
partialResults: RefArrayBuffer[SomePartialResult]
338339
): Unit = {
339340
// Propagation from and to the same entity can be ignored.
340341
val typeSetEntity = selectTypeSetEntity(e)
@@ -356,7 +357,7 @@ final class TypePropagationAnalysis private[analyses] (
356357
)(
357358
implicit
358359
state: State,
359-
partialResults: ListBuffer[SomePartialResult]
360+
partialResults: RefArrayBuffer[SomePartialResult]
360361
): Unit = {
361362
val typeSetEntity = selectTypeSetEntity(e)
362363
if (typeSetEntity == state.typeSetEntity) {

0 commit comments

Comments
 (0)