Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ Or, using the moose name of the entity:
(FamixJavaCHABuilder entryPoint: (tagcollections31 entityNamed: #'org.apache.commons.collections.map.HashedMap.readObject(ObjectInputStream)')) build.
```

Some caches are created during the building. If you wish to save space, you can flush them executing:

```smalltalk
model resetCallGraphCaches
```

### More Documentation

You can find more documentation here: [documentation](resources/documentation/UserDocumentation.md)
7 changes: 7 additions & 0 deletions resources/documentation/UserDocumentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ Examples:
(FamixJavaCHABuilder entryPoint: (tagcollections31 entityNamed: #'org.apache.commons.collections.map.HashedMap.readObject(ObjectInputStream)')) build.
```

The algo is building some caches in the caches of the entities. Those caches are kept after building a call graph in case multiple needs to be built.
If you wish to flush the caches to same some memory once the call graph is built you can execute:

```smalltalk
model resetCallGraphCaches
```

## CallGraph result

Once a CallGraph algorithm was applied, one will get an instance of `FamixCallGraph` as a result.
Expand Down
22 changes: 16 additions & 6 deletions src/Famix-CallGraphs/FamixJavaCHABuilder.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@ For example:
build
```

# Caching

The algo is building some caches in the caches of the entities. Those caches are kept after building a call graph in case multiple needs to be built.

If you wish to flush the caches to same some memory once the call graph is built you can execute:

```st
model resetCallGraphCaches
```

# Implementation detail

Originaly the implementation was using an `OrderedCollection` for the worklist and a `Set` for the visited list. But to check if we already have a node created for a method and kind it was taking a lot of time.
Expand Down Expand Up @@ -92,15 +102,15 @@ FamixJavaCHABuilder >> addErrorTo: aNode signature: signature kind: type excepti

{ #category : 'building' }
FamixJavaCHABuilder >> build [

self entryPoints do: [ :aNode | worklist at: aNode hash put: aNode ].

[ worklist isEmpty ] whileFalse: [
| node |
node := worklist anyOne.
worklist removeKey: node hash.
visited at: node hash put: node.
self computeCallGraphOf: node ].
"Usually we would treat only one node at a time but this is more costly than doing by batch"
| toVisit |
toVisit := worklist values.
worklist removeAll.
toVisit do: [ :node | visited at: node hash put: node ]. "If we do both things in the same loop sone nodes will be missing in `visited` and could lead to errors later."
toVisit do: [ :node | self computeCallGraphOf: node ] ].

"If errors occured, inspect them."
graph errorNodes ifNotEmpty: [ :collection | collection inspect ].
Expand Down
26 changes: 9 additions & 17 deletions src/Famix-CallGraphs/FamixJavaMethod.extension.st
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
Extension { #name : 'FamixJavaMethod' }

{ #category : '*Famix-CallGraphs' }
FamixJavaMethod >> isEquivalentTo: aMethod [
"A method is equivalent if their name are the same and parameters are the same.
FamixJavaMethod >> isImplementationOf: aMethod [
"Return true if I am an implemented method equivalent to the method as parameter.

There is a tricky case with parametrics because in case a type is a type parameter, we need to find its concretization to have the real type in the context."
A method is equivalent if their name are the same and parameters are the same. We already verified the name before getting here.

self name size = aMethod name size ifFalse: [ ^ false ]. "This might seems useless since we compare the name right after, but we pass so much in this method that it has an impact on perfs. It is faster to compare integer than strings"
self name = aMethod name ifFalse: [ ^ false ].
There is a tricky case with parametrics because in case a type is a type parameter, we need to find its concretization to have the real type in the context."

self isMethodDefinition ifFalse: [ ^ false ]. "We reject method declarations"

self == aMethod ifTrue: [ ^ true ]. "fastest case"

self parameters size = aMethod parameters size ifFalse: [ ^ false ].

"We need to ensure the type of each parameter is the same after applying the concretizations to the type parameters"
self orederedParameters with: aMethod orederedParameters do: [ :param1 :param2 |
self orderedParameters with: aMethod orderedParameters do: [ :param1 :param2 |
| realType1 realType2 |

realType1 := param1 typing declaredType.
Expand All @@ -36,14 +39,3 @@ FamixJavaMethod >> isEquivalentTo: aMethod [

^ true
]

{ #category : '*Famix-CallGraphs' }
FamixJavaMethod >> isImplementationOf: aMethod [
"Return true if I am an implemented method equivalent to the method as parameter."

self isMethodDefinition ifFalse: [ ^false ]. "Return false if I am a declaration and not an implementation."

self == aMethod ifTrue: [ ^ true ]. "fastest case"

^ self isEquivalentTo: aMethod
]
4 changes: 2 additions & 2 deletions src/Famix-CallGraphs/FamixTMethod.extension.st
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ FamixTMethod >> asCallGraphNode [
]

{ #category : '*Famix-CallGraphs' }
FamixTMethod >> orederedParameters [
FamixTMethod >> orderedParameters [

^ self parameters sorted: [ :method | method sourceAnchor startPos ] ascending
^ self cacheAt: #orderedParameters ifAbsentPut: [ self parameters sorted: [ :method | method sourceAnchor startPos ] ascending ]
]
18 changes: 14 additions & 4 deletions src/Famix-CallGraphs/FamixTType.extension.st
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,20 @@ Extension { #name : 'FamixTType' }
{ #category : '*Famix-CallGraphs' }
FamixTType >> implementesMethodEquivalentTo: aMethod [

^ self methods
detect: [ :method | method isImplementationOf: aMethod ]
ifFound: [ :method | true ]
ifNone: [ false ]
^ (self methodsNamed: aMethod name) anySatisfy: [ :method | method isImplementationOf: aMethod ]
]

{ #category : '*Famix-CallGraphs' }
FamixTType >> methodsNamed: aName [
"We use a cache for speed reasons. We will probably get multiple time through here and we need to have a fast way to get the methods of the right name.

Checking the size before the name for speed reasons."

| nameSize |
nameSize := aName size.
^ (self cacheAt: #methodsMap ifAbsentPut: [ IdentityDictionary new ])
at: aName
ifAbsentPut: [ self methods select: [ :method | method name size = nameSize and: [ method name = aName ] ] ]
]

{ #category : '*Famix-CallGraphs' }
Expand Down
15 changes: 8 additions & 7 deletions src/Famix-CallGraphs/FamixTWithInheritances.extension.st
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ Extension { #name : 'FamixTWithInheritances' }
{ #category : '*Famix-CallGraphs' }
FamixTWithInheritances >> applicableConcretizations [

| concretizations |
concretizations := Set new.
self withSuperclassesDo: [ :class |
class superInheritances
select: [ :inheritance | inheritance isParametricAssociation ]
thenDo: [ :inheritance | concretizations addAll: inheritance concretizations ] ].
^ concretizations
^ self cacheAt: #applicableConcretizations ifAbsentPut: [
| concretizations |
concretizations := Set new.
self withSuperclassesDo: [ :class |
class superInheritances
select: [ :inheritance | inheritance isParametricAssociation ]
thenDo: [ :inheritance | concretizations addAll: inheritance concretizations ] ].
concretizations ]
]
10 changes: 10 additions & 0 deletions src/Famix-CallGraphs/MooseModel.extension.st
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Extension { #name : 'MooseModel' }

{ #category : '*Famix-CallGraphs' }
MooseModel >> resetCallGraphCaches [
"Flushes the caches used by callgraph builders."

(self allUsing: FamixTType) do: [ :type | type removeCache: #methodsMap ].
(self allUsing: FamixTMethod) do: [ :type | type removeCache: #orderedParameters ].
(self allUsing: FamixTWithInheritances) do: [ :type | type removeCache: #applicableConcretizations ]
]