Skip to content

Commit 89f2e2d

Browse files
committed
CFG: Have the basic if/then case managed
1 parent 867bd54 commit 89f2e2d

6 files changed

Lines changed: 90 additions & 34 deletions

src/FAST-Python-Tools-Tests/FASTPythonCFGTest.class.st

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -34,24 +34,28 @@ FASTPythonCFGTest >> setUp [
3434
{ #category : 'tests' }
3535
FASTPythonCFGTest >> testFunctionWithIf [
3636

37-
| nextBlock |
38-
self skip.
39-
self flag: #todo. "Finish"
37+
| thenBlock nullBlock |
4038
self buildCFGFor: 'def f(i):
4139
if i > 3:
4240
print(i)'.
4341

4442
self assert: startBlock isConditional.
45-
self assert: startBlock isFinal. "If the condition is false then the block is indeed final"
43+
self deny: startBlock isExit.
4644
self assert: startBlock statements size equals: 1.
47-
self assert: (startBlock statements first class isOfType: FASTPyComparisonOperator).
45+
self assert: (startBlock firstStatement class isOfType: FASTPyComparisonOperator).
4846
self assert: startBlock nextBlocks size equals: 2.
49-
self assert: (startBlock nextBlock select: #isNullBlock) size equals: 1.
50-
51-
nextBlock := startBlock nextBlockForValue: true.
52-
self assert: nextBlock isConditional not.
53-
self assert: nextBlock isFinal.
54-
self assertEmpty: nextBlock statements
47+
self assert: (startBlock nextBlocks select: #isNullBlock) size equals: 1.
48+
49+
thenBlock := startBlock nextTrueBlock.
50+
self assert: thenBlock isConditional not.
51+
self deny: thenBlock isExit.
52+
self assert: thenBlock statements size equals: 1.
53+
self assert: (thenBlock firstStatement isOfType: FASTPyCall).
54+
55+
nullBlock := startBlock nextFalseBlock.
56+
self assert: nullBlock isNullBlock.
57+
self assert: nullBlock identicalTo: thenBlock nextBlock.
58+
self assert: nullBlock isExit
5559
]
5660

5761
{ #category : 'tests' }
@@ -61,8 +65,9 @@ FASTPythonCFGTest >> testFunctionWithOneStatement [
6165
print("Hello")'.
6266

6367
self assert: startBlock isStart.
64-
self assert: startBlock nextBlock isNullBlock.
65-
self assert: startBlock statements size equals: 1
68+
self assert: startBlock isFinal.
69+
self assert: startBlock statements size equals: 1.
70+
self assertEmpty: startBlock nextBlocks
6671
]
6772

6873
{ #category : 'tests' }
@@ -75,8 +80,9 @@ FASTPythonCFGTest >> testFunctionWithSimpleReturn [
7580
return i'.
7681

7782
self assert: startBlock isStart.
78-
self assert: startBlock nextBlock isNullBlock.
79-
self assert: startBlock statements size equals: 4
83+
self assert: startBlock isFinal.
84+
self assert: startBlock statements size equals: 4.
85+
self assertEmpty: startBlock nextBlocks
8086
]
8187

8288
{ #category : 'tests' }
@@ -88,6 +94,7 @@ FASTPythonCFGTest >> testFunctionWithSimpleStatements [
8894
print(i)'.
8995

9096
self assert: startBlock isStart.
91-
self assert: startBlock nextBlock isNullBlock.
92-
self assert: startBlock statements size equals: 3
97+
self assert: startBlock isFinal.
98+
self assert: startBlock statements size equals: 3.
99+
self assertEmpty: startBlock nextBlocks
93100
]

src/FAST-Python-Tools/FASTAbstractBasicBlock.extension.st

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@ FASTAbstractBasicBlock >> inspectionVisualizationContext: aContext [
1515
aContext active: self isStart
1616
]
1717

18+
{ #category : '*FAST-Python-Tools' }
19+
FASTAbstractBasicBlock >> isExit [
20+
21+
self flag: #todo. "I implemented this because #isFinal is currently not right. This should be removed once we fix the FAST CFG model."
22+
^ self nextBlocks isEmpty
23+
]
24+
1825
{ #category : '*FAST-Python-Tools' }
1926
FASTAbstractBasicBlock >> withAllFollowingBlocks [
2027

src/FAST-Python-Tools/FASTCFGVisualizationBuilder.class.st

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,10 @@ FASTCFGVisualizationBuilder >> basicBlockShape: aBasicBlock [
3333

3434
| shape |
3535
shape := aBasicBlock isStart
36-
ifTrue: [
37-
aBasicBlock isFinal
38-
ifTrue: [ self startAndFinalBlockShape ]
39-
ifFalse: [ self startBlockShape ] ]
40-
ifFalse: [
41-
aBasicBlock isFinal
42-
ifTrue: [ self finalBlockShape ]
43-
ifFalse: [ self normalBlockShape ] ].
36+
ifTrue: [ aBasicBlock nextBlocks ifEmpty: [ self startAndFinalBlockShape ] ifNotEmpty: [ self startBlockShape ] ]
37+
ifFalse: [ aBasicBlock nextBlocks ifEmpty: [ self finalBlockShape ] ifNotEmpty: [ self normalBlockShape ] ].
4438
aBasicBlock isConditional ifTrue: [ shape color: self conditionalBlockColor ].
39+
aBasicBlock isNullBlock ifTrue: [ shape color: self nullBlockColor ].
4540
^ shape
4641
]
4742

@@ -58,6 +53,7 @@ FASTCFGVisualizationBuilder >> buildLegend [
5853
legend text: 'start & final block' withShape: self startAndFinalBlockShape.
5954
legend text: 'Normal block' withShape: self normalBlockShape.
6055
legend text: 'Conditional block' withShape: (self normalBlockShape color: self conditionalBlockColor).
56+
legend text: 'Null block' withShape: (self normalBlockShape color: self nullBlockColor).
6157
legend build
6258
]
6359

@@ -110,6 +106,12 @@ FASTCFGVisualizationBuilder >> normalBlockShape [
110106
yourself
111107
]
112108

109+
{ #category : 'hooks' }
110+
FASTCFGVisualizationBuilder >> nullBlockColor [
111+
112+
^ Color green
113+
]
114+
113115
{ #category : 'instance creation' }
114116
FASTCFGVisualizationBuilder >> openOn: aCFGNode [
115117

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
Extension { #name : 'FASTConditionalBasicBlock' }
2+
3+
{ #category : '*FAST-Python-Tools' }
4+
FASTConditionalBasicBlock >> nextFalseBlock [
5+
6+
self flag: #todo. "This should move to FAST."
7+
^ self nextBlockForValue: false
8+
]
9+
10+
{ #category : '*FAST-Python-Tools' }
11+
FASTConditionalBasicBlock >> nextTrueBlock [
12+
13+
self flag: #todo. "This should move to FAST."
14+
^ self nextBlockForValue: true
15+
]

src/FAST-Python-Tools/FASTPythonCFGBuilder.class.st

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"
22
I am a class taking a FAST Python entity and returning a Control Flow Graph.
3+
4+
If we have multiple end blocks in a method, we create a virtual `FASTNullBlock` to have only one end point. In the case of Python, this node represents a **`return None`** that is the implicite return of functions in Python.
35
"
46
Class {
57
#name : 'FASTPythonCFGBuilder',

src/FAST-Python-Tools/FASTPythonCFGVisitor.class.st

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,29 +18,44 @@ FASTPythonCFGVisitor >> visitFASTPyExpression: aFASTPyExpression [
1818
{ #category : 'visiting' }
1919
FASTPythonCFGVisitor >> visitFASTPyFunctionDefinition: aFunction [
2020
"We do not manage the function in itself only its content"
21+
2122
(aFunction containedEntities sorted: #endPos ascending) do: [ :child | child accept: self ].
2223

2324
cfgBuilder basicBlocks first isStart: true.
2425

25-
"case where method has no return instruction (void signature)"
26-
cfgBuilder currentBlock ifNotNil: [ cfgBuilder chainPendingBlocksTo: FASTNullBlock new ]
26+
"If an if ends without having an else part and without having further statements, we add a null block."
27+
cfgBuilder basicBlocks
28+
detect: [ :block | block isConditional and: [ block nextFalseBlock isNil ] ]
29+
ifFound: [ cfgBuilder newBasicBlock: FASTNullBlock ]
2730
]
2831

2932
{ #category : 'visiting' }
30-
FASTPythonCFGVisitor >> visitFASTPyReturnStatement: aReturn [
33+
FASTPythonCFGVisitor >> visitFASTPyIfStatement: anIfStatement [
34+
"We do not want to apply the visit statement in the case of the if"
35+
36+
| conditionalBlock |
37+
self visitFASTTConditionalStatement: anIfStatement.
38+
conditionalBlock := cfgBuilder currentBlock.
39+
40+
self visitEntity: anIfStatement thenClause.
3141

32-
super visitFASTPyReturnStatement: aReturn.
42+
"adding a new pending action, either for the new else block or following code if no elsePart"
43+
cfgBuilder addPendingNextBlockAction: [ :elseOrNextBlock | conditionalBlock nextBlock: elseOrNextBlock onValue: false ].
3344

34-
"We know that a return mark the end of a graph branch"
35-
cfgBuilder currentBlock nextBlock: FASTNullBlock new
45+
self visitCollection: anIfStatement elifClauses.
46+
47+
self visitFASTPyTWithElseClause: anIfStatement
3648
]
3749

3850
{ #category : 'visiting' }
39-
FASTPythonCFGVisitor >> visitFASTTConditionalStatement: aStatement [
51+
FASTPythonCFGVisitor >> visitFASTPyThenClause: aThenClause [
4052

41-
cfgBuilder newConditionalBlock: aStatement.
53+
| conditionalBlock |
54+
conditionalBlock := cfgBuilder currentBlock.
55+
cfgBuilder addPendingNextBlockAction: [ :thenBlock | conditionalBlock nextBlock: thenBlock onValue: true ].
56+
cfgBuilder closeCurrentBlock.
4257

43-
super visitFASTTConditionalStatement: aStatement
58+
super visitFASTPyThenClause: aThenClause
4459
]
4560

4661
{ #category : 'visiting' }
@@ -66,3 +81,11 @@ FASTPythonCFGVisitor >> visitFASTTStatementBlock: aFASTTStatementBlock [
6681

6782
self visitCollection: aFASTTStatementBlock statements
6883
]
84+
85+
{ #category : 'visiting' }
86+
FASTPythonCFGVisitor >> visitFASTTWithCondition: aStatement [
87+
88+
cfgBuilder newConditionalBlock: aStatement condition.
89+
90+
super visitFASTTWithCondition: aStatement
91+
]

0 commit comments

Comments
 (0)