@@ -64,9 +64,17 @@ final class ProductionCoverageInstrumentationCallback
64
64
private final ParameterMapping parameterMapping ;
65
65
boolean visitedInstrumentCodeFile = false ;
66
66
67
- private final String FUNCTION_TYPE = "Type.FUNCTION" ;
68
- private final String BRANCH_TYPE = "Type.BRANCH" ;
69
- private final String BRANCH_DEFAULT_TYPE = "Type.BRANCH_DEFAULT" ;
67
+ private enum Type {
68
+ FUNCTION ("Type.FUNCTION" ),
69
+ BRANCH ("Type.BRANCH" ),
70
+ BRANCH_DEFAULT ("Type.BRANCH_DEFAULT" );
71
+
72
+ private String value ;
73
+
74
+ Type (String s ){
75
+ this .value = s ;
76
+ }
77
+ }
70
78
71
79
/**
72
80
* Stores a stack of function names that encapsulates the children nodes being instrumented. The
@@ -117,64 +125,74 @@ public void visit(NodeTraversal traversal, Node node, Node parent) {
117
125
118
126
String functionName = functionNameStack .peek ();
119
127
120
- if (node .isFunction ()) {
121
- // If the function node has been visited by visit() then we can be assured that all its
122
- // children nodes have been visited and properly instrumented.
123
- functionNameStack .pop ();
124
- instrumentBlockNode (node .getLastChild (), fileName , functionName , FUNCTION_TYPE );
125
- } else if (node .isIf ()) {
126
- if (node .getChildCount () == 2 ) {
127
- addDefaultBlock (node );
128
- }
129
- Node ifTrueNode = node .getSecondChild ();
130
- Node ifFalseNode = node .getLastChild ();
131
- instrumentBlockNode (ifTrueNode , sourceFileName , functionName , BRANCH_TYPE );
132
- instrumentBlockNode (ifFalseNode , sourceFileName , functionName , BRANCH_DEFAULT_TYPE );
133
- } else if (node .isSwitch ()) {
134
- boolean hasDefaultCase = false ;
135
- for (Node c = node .getSecondChild (); c != null ; c = c .getNext ()) {
136
- if (c .isDefaultCase ()) {
137
- instrumentBlockNode (c .getLastChild (), sourceFileName , functionName , BRANCH_DEFAULT_TYPE );
138
- hasDefaultCase = true ;
139
- } else {
140
- instrumentBlockNode (c .getLastChild (), sourceFileName , functionName , BRANCH_TYPE );
128
+ switch (node .getToken ()) {
129
+ case FUNCTION :
130
+ // If the function node has been visited by visit() then we can be assured that all its
131
+ // children nodes have been visited and properly instrumented.
132
+ functionNameStack .pop ();
133
+ instrumentBlockNode (node .getLastChild (), fileName , functionName , Type .FUNCTION .value );
134
+ break ;
135
+ case IF :
136
+ Node ifTrueNode = node .getSecondChild ();
137
+ instrumentBlockNode (ifTrueNode , sourceFileName , functionName , Type .BRANCH .value );
138
+ if (node .getChildCount () == 2 ) {
139
+ addElseBlock (node );
140
+ }
141
+ Node ifFalseNode = node .getLastChild ();
142
+ if (NodeUtil .isEmptyBlock (ifFalseNode )
143
+ || (ifFalseNode .getFirstChild () != null && !ifFalseNode .getFirstChild ().isIf ())) {
144
+ instrumentBlockNode (ifFalseNode , sourceFileName , functionName , Type .BRANCH_DEFAULT .value );
145
+ }
146
+ break ;
147
+ case SWITCH :
148
+ boolean hasDefaultCase = false ;
149
+ for (Node c = node .getSecondChild (); c != null ; c = c .getNext ()) {
150
+ if (c .isDefaultCase ()) {
151
+ instrumentBlockNode (
152
+ c .getLastChild (), sourceFileName , functionName , Type .BRANCH_DEFAULT .value );
153
+ hasDefaultCase = true ;
154
+ } else {
155
+ instrumentBlockNode (c .getLastChild (), sourceFileName , functionName , Type .BRANCH .value );
156
+ }
157
+ }
158
+ if (!hasDefaultCase ) {
159
+ Node defaultBlock = IR .block ();
160
+ defaultBlock .useSourceInfoIfMissingFromForTree (node );
161
+ Node defaultCase = IR .defaultCase (defaultBlock ).useSourceInfoIfMissingFromForTree (node );
162
+ node .addChildToBack (defaultCase );
163
+ instrumentBlockNode (
164
+ defaultBlock , sourceFileName , functionName , Type .BRANCH_DEFAULT .value );
165
+ }
166
+ break ;
167
+ case HOOK :
168
+ Node ifTernaryIsTrueExpression = node .getSecondChild ();
169
+ Node ifTernaryIsFalseExpression = node .getLastChild ();
170
+
171
+ addInstrumentationNodeWithComma (
172
+ ifTernaryIsTrueExpression , sourceFileName , functionName , Type .BRANCH .value );
173
+ addInstrumentationNodeWithComma (
174
+ ifTernaryIsFalseExpression , sourceFileName , functionName , Type .BRANCH .value );
175
+
176
+ compiler .reportChangeToEnclosingScope (node );
177
+ break ;
178
+ case OR :
179
+ case AND :
180
+ case COALESCE :
181
+ // Only instrument the second child of the binary operation because the first child will
182
+ // always execute, or the first child is part of a chain of binary operations and would have
183
+ // already been instrumented.
184
+ Node secondExpression = node .getLastChild ();
185
+ addInstrumentationNodeWithComma (
186
+ secondExpression , sourceFileName , functionName , Type .BRANCH .value );
187
+
188
+ compiler .reportChangeToEnclosingScope (node );
189
+ break ;
190
+ default :
191
+ if (NodeUtil .isLoopStructure (node )) {
192
+ Node blockNode = NodeUtil .getLoopCodeBlock (node );
193
+ checkNotNull (blockNode );
194
+ instrumentBlockNode (blockNode , sourceFileName , functionName , Type .BRANCH .value );
141
195
}
142
- }
143
- if (!hasDefaultCase ){
144
- Node defaultBlock = IR .block ();
145
- defaultBlock .useSourceInfoIfMissingFromForTree (node );
146
- Node defaultCase = IR .defaultCase (defaultBlock ).useSourceInfoIfMissingFromForTree (node );
147
- node .addChildToBack (defaultCase );
148
- instrumentBlockNode (defaultBlock , sourceFileName , functionName , BRANCH_DEFAULT_TYPE );
149
- }
150
- } else if (NodeUtil .isLoopStructure (node )) {
151
- Node blockNode = NodeUtil .getLoopCodeBlock (node );
152
- checkNotNull (blockNode );
153
- instrumentBlockNode (blockNode , sourceFileName , functionName , BRANCH_TYPE );
154
-
155
- Node newNode =
156
- newInstrumentationNode (blockNode , sourceFileName , functionName , BRANCH_DEFAULT_TYPE );
157
- blockNode .getGrandparent ().addChildAfter (newNode , blockNode .getParent ());
158
- compiler .reportChangeToEnclosingScope (blockNode .getParent ());
159
- } else if (node .isHook ()) {
160
- Node ifTernaryIsTrueExpression = node .getSecondChild ();
161
- Node ifTernaryIsFalseExpression = node .getLastChild ();
162
-
163
- addInstrumentationNodeWithComma (
164
- ifTernaryIsTrueExpression , sourceFileName , functionName , BRANCH_TYPE );
165
- addInstrumentationNodeWithComma (
166
- ifTernaryIsFalseExpression , sourceFileName , functionName , BRANCH_TYPE );
167
-
168
- compiler .reportChangeToEnclosingScope (node );
169
- } else if (node .isOr () || node .isAnd () || node .isNullishCoalesce ()) {
170
- // Only instrument the second child of the binary operation because the first child will
171
- // always execute, or the first child is part of a chain of binary operations and would have
172
- // already been instrumented.
173
- Node secondExpression = node .getLastChild ();
174
- addInstrumentationNodeWithComma (
175
- secondExpression , sourceFileName , functionName , BRANCH_TYPE );
176
-
177
- compiler .reportChangeToEnclosingScope (node );
178
196
}
179
197
}
180
198
@@ -185,16 +203,16 @@ public void visit(NodeTraversal traversal, Node node, Node parent) {
185
203
private void addInstrumentationNodeWithComma (
186
204
Node originalNode , String fileName , String functionName , String type ) {
187
205
Node parentNode = originalNode .getParent ();
188
- parentNode . removeChild ( originalNode );
206
+ Node cloneOfOriginal = originalNode . cloneTree ( );
189
207
Node newInstrumentationNode =
190
- newInstrumentationNode (originalNode , fileName , functionName , type );
208
+ newInstrumentationNode (cloneOfOriginal , fileName , functionName , type );
191
209
192
210
// newInstrumentationNode returns an EXPR_RESULT which cannot be a child of a COMMA node.
193
211
// Instead we use the child of of the newInstrumentatioNode which is a CALL node.
194
212
Node childOfInstrumentationNode = newInstrumentationNode .getFirstChild ().detach ();
195
213
Node infusedExp =
196
- StatementFusion .fuseExpressionIntoExpression (childOfInstrumentationNode , originalNode );
197
- parentNode .addChildToBack ( infusedExp );
214
+ StatementFusion .fuseExpressionIntoExpression (childOfInstrumentationNode , cloneOfOriginal );
215
+ parentNode .replaceChild ( originalNode , infusedExp );
198
216
}
199
217
200
218
/**
@@ -245,8 +263,8 @@ private Node newInstrumentationNode(Node node, String fileName, String fnName, S
245
263
return exprNode .useSourceInfoIfMissingFromForTree (node );
246
264
}
247
265
248
- /** Add a default block for If statements */
249
- private Node addDefaultBlock (Node node ) {
266
+ /** Add an else block for If statements if one is not already present. */
267
+ private Node addElseBlock (Node node ) {
250
268
Node defaultBlock = IR .block ();
251
269
node .addChildToBack (defaultBlock );
252
270
return defaultBlock .useSourceInfoIfMissingFromForTree (node );
0 commit comments