@@ -9,6 +9,7 @@ import {CompilerError} from '../CompilerError';
9
9
import { DependencyPath , Identifier , ReactiveScopeDependency } from '../HIR' ;
10
10
import { printIdentifier } from '../HIR/PrintHIR' ;
11
11
import { assertExhaustive } from '../Utils/utils' ;
12
+ import { printDependency } from './PrintReactiveFunction' ;
12
13
13
14
/*
14
15
* We need to understand optional member expressions only when determining
@@ -60,13 +61,14 @@ export class ReactiveScopeDependencyTree {
60
61
const { path} = dep ;
61
62
let currNode = this . #getOrCreateRoot( dep . identifier ) ;
62
63
63
- const accessType = inConditional
64
- ? PropertyAccessType . ConditionalAccess
65
- : PropertyAccessType . UnconditionalAccess ;
66
-
67
64
for ( const item of path ) {
68
65
// all properties read 'on the way' to a dependency are marked as 'access'
69
66
let currChild = getOrMakeProperty ( currNode , item . property ) ;
67
+ const accessType = inConditional
68
+ ? PropertyAccessType . ConditionalAccess
69
+ : item . optional
70
+ ? PropertyAccessType . OptionalAccess
71
+ : PropertyAccessType . UnconditionalAccess ;
70
72
currChild . accessType = merge ( currChild . accessType , accessType ) ;
71
73
currNode = currChild ;
72
74
}
@@ -77,18 +79,22 @@ export class ReactiveScopeDependencyTree {
77
79
*/
78
80
const depType = inConditional
79
81
? PropertyAccessType . ConditionalDependency
80
- : PropertyAccessType . UnconditionalDependency ;
82
+ : isOptional ( currNode . accessType )
83
+ ? PropertyAccessType . OptionalDependency
84
+ : PropertyAccessType . UnconditionalDependency ;
81
85
82
86
currNode . accessType = merge ( currNode . accessType , depType ) ;
83
87
}
84
88
85
89
deriveMinimalDependencies ( ) : Set < ReactiveScopeDependency > {
86
90
const results = new Set < ReactiveScopeDependency > ( ) ;
87
91
for ( const [ rootId , rootNode ] of this . #roots. entries ( ) ) {
88
- const deps = deriveMinimalDependenciesInSubtree ( rootNode ) ;
92
+ const deps = deriveMinimalDependenciesInSubtree ( rootNode , null ) ;
89
93
CompilerError . invariant (
90
94
deps . every (
91
- dep => dep . accessType === PropertyAccessType . UnconditionalDependency ,
95
+ dep =>
96
+ dep . accessType === PropertyAccessType . UnconditionalDependency ||
97
+ dep . accessType == PropertyAccessType . OptionalDependency ,
92
98
) ,
93
99
{
94
100
reason :
@@ -173,6 +179,27 @@ export class ReactiveScopeDependencyTree {
173
179
}
174
180
return res . flat ( ) . join ( '\n' ) ;
175
181
}
182
+
183
+ debug ( ) : string {
184
+ const buf : Array < string > = [ `tree() [` ] ;
185
+ for ( const [ rootId , rootNode ] of this . #roots) {
186
+ buf . push ( `${ printIdentifier ( rootId ) } (${ rootNode . accessType } ):` ) ;
187
+ this . #debugImpl( buf , rootNode , 1 ) ;
188
+ }
189
+ buf . push ( ']' ) ;
190
+ return buf . length > 2 ? buf . join ( '\n' ) : buf . join ( '' ) ;
191
+ }
192
+
193
+ #debugImpl(
194
+ buf : Array < string > ,
195
+ node : DependencyNode ,
196
+ depth : number = 0 ,
197
+ ) : void {
198
+ for ( const [ property , childNode ] of node . properties ) {
199
+ buf . push ( `${ ' ' . repeat ( depth ) } .${ property } (${ childNode . accessType } ):` ) ;
200
+ this . #debugImpl( buf , childNode , depth + 1 ) ;
201
+ }
202
+ }
176
203
}
177
204
178
205
/*
@@ -196,8 +223,10 @@ export class ReactiveScopeDependencyTree {
196
223
*/
197
224
enum PropertyAccessType {
198
225
ConditionalAccess = 'ConditionalAccess' ,
226
+ OptionalAccess = 'OptionalAccess' ,
199
227
UnconditionalAccess = 'UnconditionalAccess' ,
200
228
ConditionalDependency = 'ConditionalDependency' ,
229
+ OptionalDependency = 'OptionalDependency' ,
201
230
UnconditionalDependency = 'UnconditionalDependency' ,
202
231
}
203
232
@@ -211,9 +240,16 @@ function isUnconditional(access: PropertyAccessType): boolean {
211
240
function isDependency ( access : PropertyAccessType ) : boolean {
212
241
return (
213
242
access === PropertyAccessType . ConditionalDependency ||
243
+ access === PropertyAccessType . OptionalDependency ||
214
244
access === PropertyAccessType . UnconditionalDependency
215
245
) ;
216
246
}
247
+ function isOptional ( access : PropertyAccessType ) : boolean {
248
+ return (
249
+ access === PropertyAccessType . OptionalAccess ||
250
+ access === PropertyAccessType . OptionalDependency
251
+ ) ;
252
+ }
217
253
218
254
function merge (
219
255
access1 : PropertyAccessType ,
@@ -222,6 +258,7 @@ function merge(
222
258
const resultIsUnconditional =
223
259
isUnconditional ( access1 ) || isUnconditional ( access2 ) ;
224
260
const resultIsDependency = isDependency ( access1 ) || isDependency ( access2 ) ;
261
+ const resultIsOptional = isOptional ( access1 ) || isOptional ( access2 ) ;
225
262
226
263
/*
227
264
* Straightforward merge.
@@ -237,6 +274,12 @@ function merge(
237
274
} else {
238
275
return PropertyAccessType . UnconditionalAccess ;
239
276
}
277
+ } else if ( resultIsOptional ) {
278
+ if ( resultIsDependency ) {
279
+ return PropertyAccessType . OptionalDependency ;
280
+ } else {
281
+ return PropertyAccessType . OptionalAccess ;
282
+ }
240
283
} else {
241
284
if ( resultIsDependency ) {
242
285
return PropertyAccessType . ConditionalDependency ;
@@ -256,19 +299,34 @@ type ReduceResultNode = {
256
299
accessType : PropertyAccessType ;
257
300
} ;
258
301
259
- const promoteUncondResult = [
260
- {
302
+ function promoteResult (
303
+ accessType : PropertyAccessType ,
304
+ path : { property : string ; optional : boolean } | null ,
305
+ ) : Array < ReduceResultNode > {
306
+ const result : ReduceResultNode = {
261
307
relativePath : [ ] ,
262
- accessType : PropertyAccessType . UnconditionalDependency ,
263
- } ,
264
- ] ;
308
+ accessType,
309
+ } ;
310
+ if ( path !== null ) {
311
+ result . relativePath . push ( path ) ;
312
+ }
313
+ return [ result ] ;
314
+ }
265
315
266
- const promoteCondResult = [
267
- {
268
- relativePath : [ ] ,
269
- accessType : PropertyAccessType . ConditionalDependency ,
270
- } ,
271
- ] ;
316
+ function prependPath (
317
+ results : Array < ReduceResultNode > ,
318
+ path : { property : string ; optional : boolean } | null ,
319
+ ) : Array < ReduceResultNode > {
320
+ if ( path === null ) {
321
+ return results ;
322
+ }
323
+ return results . map ( result => {
324
+ return {
325
+ accessType : result . accessType ,
326
+ relativePath : [ path , ...result . relativePath ] ,
327
+ } ;
328
+ } ) ;
329
+ }
272
330
273
331
/*
274
332
* Recursively calculates minimal dependencies in a subtree.
@@ -277,42 +335,76 @@ const promoteCondResult = [
277
335
*/
278
336
function deriveMinimalDependenciesInSubtree (
279
337
dep : DependencyNode ,
338
+ property : string | null ,
280
339
) : Array < ReduceResultNode > {
281
340
const results : Array < ReduceResultNode > = [ ] ;
282
341
for ( const [ childName , childNode ] of dep . properties ) {
283
- const childResult = deriveMinimalDependenciesInSubtree ( childNode ) . map (
284
- ( { relativePath, accessType} ) => {
285
- return {
286
- relativePath : [
287
- { property : childName , optional : false } ,
288
- ...relativePath ,
289
- ] ,
290
- accessType,
291
- } ;
292
- } ,
342
+ const childResult = deriveMinimalDependenciesInSubtree (
343
+ childNode ,
344
+ childName ,
293
345
) ;
294
346
results . push ( ...childResult ) ;
295
347
}
296
348
297
349
switch ( dep . accessType ) {
298
350
case PropertyAccessType . UnconditionalDependency : {
299
- return promoteUncondResult ;
351
+ return promoteResult (
352
+ PropertyAccessType . UnconditionalDependency ,
353
+ property !== null ? { property, optional : false } : null ,
354
+ ) ;
300
355
}
301
356
case PropertyAccessType . UnconditionalAccess : {
302
357
if (
303
358
results . every (
304
359
( { accessType} ) =>
305
- accessType === PropertyAccessType . UnconditionalDependency ,
360
+ accessType === PropertyAccessType . UnconditionalDependency ||
361
+ accessType === PropertyAccessType . OptionalDependency ,
306
362
)
307
363
) {
308
364
// all children are unconditional dependencies, return them to preserve granularity
309
- return results ;
365
+ return prependPath (
366
+ results ,
367
+ property !== null ? { property, optional : false } : null ,
368
+ ) ;
310
369
} else {
311
370
/*
312
371
* at least one child is accessed conditionally, so this node needs to be promoted to
313
372
* unconditional dependency
314
373
*/
315
- return promoteUncondResult ;
374
+ return promoteResult (
375
+ PropertyAccessType . UnconditionalDependency ,
376
+ property !== null ? { property, optional : false } : null ,
377
+ ) ;
378
+ }
379
+ }
380
+ case PropertyAccessType . OptionalDependency : {
381
+ return promoteResult (
382
+ PropertyAccessType . OptionalDependency ,
383
+ property !== null ? { property, optional : true } : null ,
384
+ ) ;
385
+ }
386
+ case PropertyAccessType . OptionalAccess : {
387
+ if (
388
+ results . every (
389
+ ( { accessType} ) =>
390
+ accessType === PropertyAccessType . UnconditionalDependency ||
391
+ accessType === PropertyAccessType . OptionalDependency ,
392
+ )
393
+ ) {
394
+ // all children are unconditional dependencies, return them to preserve granularity
395
+ return prependPath (
396
+ results ,
397
+ property !== null ? { property, optional : true } : null ,
398
+ ) ;
399
+ } else {
400
+ /*
401
+ * at least one child is accessed conditionally, so this node needs to be promoted to
402
+ * unconditional dependency
403
+ */
404
+ return promoteResult (
405
+ PropertyAccessType . OptionalDependency ,
406
+ property !== null ? { property, optional : true } : null ,
407
+ ) ;
316
408
}
317
409
}
318
410
case PropertyAccessType . ConditionalAccess :
@@ -328,13 +420,19 @@ function deriveMinimalDependenciesInSubtree(
328
420
* unconditional access.
329
421
* Truncate results of child nodes here, since we shouldn't access them anyways
330
422
*/
331
- return promoteCondResult ;
423
+ return promoteResult (
424
+ PropertyAccessType . ConditionalDependency ,
425
+ property !== null ? { property, optional : true } : null ,
426
+ ) ;
332
427
} else {
333
428
/*
334
429
* at least one child is accessed unconditionally, so this node can be promoted to
335
430
* unconditional dependency
336
431
*/
337
- return promoteUncondResult ;
432
+ return promoteResult (
433
+ PropertyAccessType . UnconditionalDependency ,
434
+ property !== null ? { property, optional : true } : null ,
435
+ ) ;
338
436
}
339
437
}
340
438
default : {
0 commit comments