@@ -108,6 +108,36 @@ abstract class _Invocation extends _DependencyTracker
108
108
/// Used for recursive calls while this invocation is being processed.
109
109
Type get resultForRecursiveInvocation => result;
110
110
111
+ /// Use [type] as a current computed result of this invocation.
112
+ /// If this invocation was invalidated, and the invalidated result is
113
+ /// different, then invalidate all dependent invocations as well.
114
+ /// Result type may be saturated if this invocation was invalidated
115
+ /// too many times.
116
+ void setResult (TypeFlowAnalysis typeFlowAnalysis, Type type) {
117
+ assertx (type != null );
118
+ result = type;
119
+
120
+ if (invalidatedResult != null ) {
121
+ if (invalidatedResult != result) {
122
+ invalidateDependentInvocations (typeFlowAnalysis.workList);
123
+
124
+ invalidationCounter++ ;
125
+ Statistics .maxInvalidationsPerInvocation =
126
+ max (Statistics .maxInvalidationsPerInvocation, invalidationCounter);
127
+ // In rare cases, loops in dependencies and approximation of
128
+ // recursive invocations may cause infinite bouncing of result
129
+ // types. To prevent infinite looping and guarantee convergence of
130
+ // the analysis, result is saturated after invocation is invalidated
131
+ // at least [_Invocation.invalidationLimit] times.
132
+ if (invalidationCounter > _Invocation .invalidationLimit) {
133
+ result =
134
+ result.union (invalidatedResult, typeFlowAnalysis.hierarchyCache);
135
+ }
136
+ }
137
+ invalidatedResult = null ;
138
+ }
139
+ }
140
+
111
141
// Only take selector and args into account as _Invocation objects
112
142
// are cached in _InvocationsCache using selector and args as a key.
113
143
@override
@@ -261,7 +291,7 @@ class _DirectInvocation extends _Invocation {
261
291
if (summaryResult is Type &&
262
292
! typeFlowAnalysis.workList._isPending (this )) {
263
293
assertx (result == null || result == summaryResult);
264
- result = summaryResult;
294
+ setResult (typeFlowAnalysis, summaryResult) ;
265
295
}
266
296
return summary.apply (
267
297
args, typeFlowAnalysis.hierarchyCache, typeFlowAnalysis);
@@ -1220,6 +1250,7 @@ class _WorkList {
1220
1250
void invalidateInvocation (_Invocation invocation) {
1221
1251
Statistics .invocationsInvalidated++ ;
1222
1252
if (invocation.result != null ) {
1253
+ assertx (invocation.invalidatedResult == null );
1223
1254
invocation.invalidatedResult = invocation.result;
1224
1255
invocation.result = null ;
1225
1256
}
@@ -1269,45 +1300,62 @@ class _WorkList {
1269
1300
1270
1301
// Test if tracing is enabled to avoid expensive message formatting.
1271
1302
if (kPrintTrace) {
1272
- tracePrint ('PROCESSING $invocation ' , 1 );
1303
+ tracePrint (
1304
+ 'PROCESSING $invocation , invalidatedResult ${invocation .invalidatedResult }' ,
1305
+ 1 );
1273
1306
}
1274
1307
1275
1308
if (processing.add (invocation)) {
1309
+ // Do not process too many calls in the call stack as
1310
+ // it may cause stack overflow in the analysis.
1311
+ const int kMaxCallsInCallStack = 500 ;
1312
+ if (callStack.length > kMaxCallsInCallStack) {
1313
+ Statistics .deepInvocationsDeferred++ ;
1314
+ // If there is invalidatedResult, then use it.
1315
+ // When actual result is inferred it will be compared against
1316
+ // invalidatedResult and all dependent invocations will be invalidated
1317
+ // accordingly.
1318
+ //
1319
+ // Otherwise, if invocation is not invalidated yet, use empty type
1320
+ // as a result but immediately invalidate it in order to recompute.
1321
+ // Static type would be too inaccurate.
1322
+ if (invocation.invalidatedResult == null ) {
1323
+ invocation.result = const EmptyType ();
1324
+ }
1325
+ // Conservatively assume that this invocation may trigger
1326
+ // parameter type checks. This is needed because caller may not be
1327
+ // invalidated and recomputed if this invocation yields the
1328
+ // same result.
1329
+ invocation.typeChecksNeeded = true ;
1330
+ invalidateInvocation (invocation);
1331
+ assertx (invocation.result == null );
1332
+ assertx (invocation.invalidatedResult != null );
1333
+ assertx (_isPending (invocation));
1334
+ if (kPrintTrace) {
1335
+ tracePrint ("Processing deferred due to deep call stack." );
1336
+ tracePrint (
1337
+ 'END PROCESSING $invocation , RESULT ${invocation .invalidatedResult }' ,
1338
+ - 1 );
1339
+ }
1340
+ processing.remove (invocation);
1341
+ return invocation.invalidatedResult;
1342
+ }
1343
+
1276
1344
callStack.add (invocation);
1277
1345
pending.remove (invocation);
1278
1346
1279
1347
Type result = invocation.process (_typeFlowAnalysis);
1280
1348
1281
- assertx (result != null );
1282
- invocation.result = result;
1283
-
1284
- if (invocation.invalidatedResult != null ) {
1285
- if (invocation.invalidatedResult != result) {
1286
- invocation.invalidateDependentInvocations (this );
1287
-
1288
- invocation.invalidationCounter++ ;
1289
- Statistics .maxInvalidationsPerInvocation = max (
1290
- Statistics .maxInvalidationsPerInvocation,
1291
- invocation.invalidationCounter);
1292
- // In rare cases, loops in dependencies and approximation of
1293
- // recursive invocations may cause infinite bouncing of result
1294
- // types. To prevent infinite looping and guarantee convergence of
1295
- // the analysis, result is saturated after invocation is invalidated
1296
- // at least [_Invocation.invalidationLimit] times.
1297
- if (invocation.invalidationCounter > _Invocation .invalidationLimit) {
1298
- result = result.union (
1299
- invocation.invalidatedResult, _typeFlowAnalysis.hierarchyCache);
1300
- invocation.result = result;
1301
- }
1302
- }
1303
- invocation.invalidatedResult = null ;
1304
- }
1349
+ invocation.setResult (_typeFlowAnalysis, result);
1350
+
1351
+ // setResult may saturate result to ensure convergence.
1352
+ result = invocation.result;
1305
1353
1306
1354
// Invocation is still pending - it was invalidated while being processed.
1307
1355
// Move result to invalidatedResult.
1308
1356
if (_isPending (invocation)) {
1309
1357
Statistics .invocationsInvalidatedDuringProcessing++ ;
1310
- invocation.invalidatedResult = result;
1358
+ invocation.invalidatedResult = invocation. result;
1311
1359
invocation.result = null ;
1312
1360
}
1313
1361
0 commit comments