@@ -10,18 +10,9 @@ const DISPOSED = 1 << 3;
10
10
const HAS_ERROR = 1 << 4 ;
11
11
const TRACKING = 1 << 5 ;
12
12
13
- // Flags for Nodes.
14
- const NODE_FREE = 1 << 0 ;
15
- const NODE_SUBSCRIBED = 1 << 1 ;
16
-
17
13
// A linked list node used to track dependencies (sources) and dependents (targets).
18
14
// Also used to remember the source's last version number that the target saw.
19
15
type Node = {
20
- // A node may have the following flags:
21
- // NODE_FREE when it's unclear whether the source is still a dependency of the target
22
- // NODE_SUBSCRIBED when the target has subscribed to listen change notifications from the source
23
- _flags : number ;
24
-
25
16
// A source whose value the target depends on.
26
17
_source : Signal ;
27
18
_prevSource ?: Node ;
@@ -35,6 +26,7 @@ type Node = {
35
26
// The version number of the source that target has last seen. We use version numbers
36
27
// instead of storing the source value, because source values can take arbitrary amount
37
28
// of memory, and computeds could hang on to them forever because they're lazily evaluated.
29
+ // Use the special value -1 to mark potentially unused but recyclable nodes.
38
30
_version : number ;
39
31
40
32
// Used to remember & roll back the source's previous `._node` value when entering &
@@ -121,7 +113,6 @@ function addDependency(signal: Signal): Node | undefined {
121
113
// `signal` is a new dependency. Create a new node dependency node, move it
122
114
// to the front of the current context's dependency list.
123
115
node = {
124
- _flags : 0 ,
125
116
_version : 0 ,
126
117
_source : signal ,
127
118
_prevSource : undefined ,
@@ -140,10 +131,10 @@ function addDependency(signal: Signal): Node | undefined {
140
131
signal . _subscribe ( node ) ;
141
132
}
142
133
return node ;
143
- } else if ( node . _flags & NODE_FREE ) {
134
+ } else if ( node . _version === - 1 ) {
144
135
// `signal` is an existing dependency from a previous evaluation. Reuse the dependency
145
136
// node and move it to the front of the evaluation context's dependency list.
146
- node . _flags &= ~ NODE_FREE ;
137
+ node . _version = 0 ;
147
138
148
139
const head = evalContext . _sources ;
149
140
if ( node !== head ) {
@@ -174,7 +165,10 @@ declare class Signal<T = any> {
174
165
/** @internal */
175
166
_value : unknown ;
176
167
177
- /** @internal */
168
+ /** @internal
169
+ * Version numbers should always be >= 0, because the special value -1 is used
170
+ * by Nodes to signify potentially unused but recyclable notes.
171
+ */
178
172
_version : number ;
179
173
180
174
/** @internal */
@@ -219,10 +213,8 @@ Signal.prototype._refresh = function () {
219
213
} ;
220
214
221
215
Signal . prototype . _subscribe = function ( node ) {
222
- if ( ! ( node . _flags & NODE_SUBSCRIBED ) ) {
223
- node . _flags |= NODE_SUBSCRIBED ;
216
+ if ( this . _targets !== node && node . _prevTarget === undefined ) {
224
217
node . _nextTarget = this . _targets ;
225
-
226
218
if ( this . _targets !== undefined ) {
227
219
this . _targets . _prevTarget = node ;
228
220
}
@@ -231,22 +223,18 @@ Signal.prototype._subscribe = function (node) {
231
223
} ;
232
224
233
225
Signal . prototype . _unsubscribe = function ( node ) {
234
- if ( node . _flags & NODE_SUBSCRIBED ) {
235
- node . _flags &= ~ NODE_SUBSCRIBED ;
236
-
237
- const prev = node . _prevTarget ;
238
- const next = node . _nextTarget ;
239
- if ( prev !== undefined ) {
240
- prev . _nextTarget = next ;
241
- node . _prevTarget = undefined ;
242
- }
243
- if ( next !== undefined ) {
244
- next . _prevTarget = prev ;
245
- node . _nextTarget = undefined ;
246
- }
247
- if ( node === this . _targets ) {
248
- this . _targets = next ;
249
- }
226
+ const prev = node . _prevTarget ;
227
+ const next = node . _nextTarget ;
228
+ if ( prev !== undefined ) {
229
+ prev . _nextTarget = next ;
230
+ node . _prevTarget = undefined ;
231
+ }
232
+ if ( next !== undefined ) {
233
+ next . _prevTarget = prev ;
234
+ node . _nextTarget = undefined ;
235
+ }
236
+ if ( node === this . _targets ) {
237
+ this . _targets = next ;
250
238
}
251
239
} ;
252
240
@@ -318,8 +306,11 @@ function needsToRecompute(target: Computed | Effect): boolean {
318
306
// Check the dependencies for changed values. The dependency list is already
319
307
// in order of use. Therefore if multiple dependencies have changed values, only
320
308
// the first used dependency is re-evaluated at this point.
321
- let node = target . _sources ;
322
- while ( node !== undefined ) {
309
+ for (
310
+ let node = target . _sources ;
311
+ node !== undefined ;
312
+ node = node . _nextSource
313
+ ) {
323
314
// If there's a new version of the dependency before or after refreshing,
324
315
// or the dependency has something blocking it from refreshing at all (e.g. a
325
316
// dependency cycle), then we need to recompute.
@@ -328,13 +319,12 @@ function needsToRecompute(target: Computed | Effect): boolean {
328
319
! node . _source . _refresh ( ) ||
329
320
node . _source . _version !== node . _version
330
321
) {
331
- break ;
322
+ return true ;
332
323
}
333
- node = node . _nextSource ;
334
324
}
335
325
// If none of the dependencies have changed values since last recompute then the
336
326
// there's no need to recompute.
337
- return node !== undefined ;
327
+ return false ;
338
328
}
339
329
340
330
function prepareSources ( target : Computed | Effect ) {
@@ -348,7 +338,7 @@ function prepareSources(target: Computed | Effect) {
348
338
node . _rollbackNode = rollbackNode ;
349
339
}
350
340
node . _source . _node = node ;
351
- node . _flags |= NODE_FREE ;
341
+ node . _version = - 1 ;
352
342
}
353
343
}
354
344
@@ -363,7 +353,7 @@ function cleanupSources(target: Computed | Effect) {
363
353
let sources = undefined ;
364
354
while ( node !== undefined ) {
365
355
const next = node . _nextSource ;
366
- if ( node . _flags & NODE_FREE ) {
356
+ if ( node . _version === - 1 ) {
367
357
node . _source . _unsubscribe ( node ) ;
368
358
node . _nextSource = undefined ;
369
359
} else {
0 commit comments