16
import { KoalaProfiler, KoalaCallsiteKey, KoalaCallsiteKeys } from "@koalaui/common"
17
import { Array_from_set, className, int32, refEqual, uint32 } from "@koalaui/compat"
18
import { Dependencies, Dependency } from "./Dependency"
19
import { Disposable, disposeContent, disposeContentBackward } from "./Disposable"
20
import { Observable, ObservableHandler } from "./Observable"
21
import { IncrementalNode } from "../tree/IncrementalNode"
22
import { ReadonlyTreeNode } from "../tree/ReadonlyTreeNode"
24
export const CONTEXT_ROOT_SCOPE = "ohos.koala.context.root.scope"
25
export const CONTEXT_ROOT_NODE = "ohos.koala.context.root.node"
31
export type Equivalent<Value> = (oldV: Value, newV: Value) => boolean
37
export function createStateManager(): StateManager {
38
return new StateManagerImpl()
47
export interface StateManager extends StateContext {
48
readonly updateNeeded: boolean
49
updateSnapshot(): uint32
50
updatableNode<Node extends IncrementalNode>(node: Node, update: (context: StateContext) => void, cleanup?: () => void): ComputableState<Node>
51
scheduleCallback(callback: () => void): void
60
export interface State<Value> {
64
readonly modified: boolean
75
export interface MutableState<Value> extends Disposable, State<Value> {
89
export interface ComputableState<Value> extends Disposable, State<Value> {
93
readonly recomputeNeeded: boolean
102
export interface StateContext {
103
readonly node: IncrementalNode | undefined
104
attach<Node extends IncrementalNode>(id: KoalaCallsiteKey, create: () => Node, update: () => void, cleanup?: () => void): void
105
compute<Value>(id: KoalaCallsiteKey, compute: () => Value, cleanup?: (value: Value | undefined) => void, once?: boolean): Value
106
computableState<Value>(compute: (context: StateContext) => Value, cleanup?: (context: StateContext, value: Value | undefined) => void): ComputableState<Value>
107
mutableState<Value>(initial: Value, global?: boolean, equivalent?: Equivalent<Value>, tracker?: ValueTracker<Value>): MutableState<Value>
108
namedState<Value>(name: string, create: () => Value, global?: boolean, equivalent?: Equivalent<Value>, tracker?: ValueTracker<Value>): MutableState<Value>
109
stateBy<Value>(name: string, global?: boolean): MutableState<Value> | undefined
110
valueBy<Value>(name: string, global?: boolean): Value
112
scope<Value>(id: KoalaCallsiteKey, paramCount?: int32, create?: () => IncrementalNode, compute?: () => Value, cleanup?: (value: Value | undefined) => void, once?: boolean): InternalScope<Value>
113
controlledScope(id: KoalaCallsiteKey, invalidate: () => void): ControlledScope
119
export interface ValueTracker<Value> {
125
onCreate(value: Value): Value
131
onUpdate(value: Value): Value
135
export interface InternalScope<Value> {
137
readonly unchanged: boolean
139
readonly cached: Value
141
recache(newValue?: Value): Value
143
param<V>(index: int32, value: V, equivalent?: Equivalent<V>, name?: string, contextLocal?: boolean): State<V>
151
export interface ControlledScope {
160
interface ManagedState extends Disposable {
165
readonly global: boolean
166
readonly modified: boolean
167
updateStateSnapshot(): void
170
interface ManagedScope extends Disposable, Dependency, ReadonlyTreeNode {
171
readonly manager: StateManagerImpl | undefined
172
readonly dependencies: Dependencies | undefined
173
readonly id: KoalaCallsiteKey
174
readonly node: IncrementalNode | undefined
175
readonly nodeRef: IncrementalNode | undefined
176
readonly once: boolean
177
readonly modified: boolean
178
readonly parent: ManagedScope | undefined
179
next: ManagedScope | undefined
180
recomputeNeeded: boolean
181
addCreatedState(state: Disposable): void
182
getNamedState<Value>(name: string): MutableState<Value> | undefined
183
setNamedState(name: string, state: Disposable): void
184
getChildScope<Value>(id: KoalaCallsiteKey, paramCount: int32, create?: () => IncrementalNode, compute?: () => Value, cleanup?: (value: Value | undefined) => void, once?: boolean): ScopeImpl<Value>
185
increment(count: uint32, skip: boolean): void
188
class StateImpl<Value> implements Observable, ManagedState, MutableState<Value> {
189
manager: StateManagerImpl | undefined = undefined
190
private dependencies: Dependencies | undefined = undefined
191
private current: Value
192
private snapshot: Value
193
private myModified = false
194
private myUpdated = true
195
readonly global: boolean
196
private equivalent: Equivalent<Value> | undefined = undefined
197
private tracker: ValueTracker<Value> | undefined = undefined
198
private name: string | undefined = undefined
207
constructor(manager: StateManagerImpl, initial: Value, global: boolean, equivalent?: Equivalent<Value>, tracker?: ValueTracker<Value>, name?: string) {
208
if (tracker !== undefined) initial = tracker.onCreate(initial)
210
this.equivalent = equivalent
211
this.tracker = tracker
213
this.manager = manager
214
this.dependencies = new Dependencies()
215
this.current = initial
216
this.snapshot = initial
217
ObservableHandler.attach(initial, this)
218
manager.addCreatedState(this)
221
get modified(): boolean {
223
return this.myModified
228
return this.manager?.frozen == true ? this.snapshot : this.current
231
set value(value: Value) {
232
if (this.setProhibited) throw new Error("prohibited to modify a state when updating a call tree")
233
if (this.tracker !== undefined) value = this.tracker!.onUpdate(value)
239
this.dependencies?.register(this.manager?.dependency)
243
this.myUpdated = false
244
if (this.manager === undefined) {
245
this.updateStateSnapshot()
247
this.manager!.updateNeeded = true
251
private get setProhibited(): boolean {
252
if (this.dependencies?.empty != false) return false
253
const scope = this.manager?.current
254
if (scope === undefined) return false
255
if (scope?.node === undefined && scope?.parent === undefined) return false
259
updateStateSnapshot(): void {
260
const isModified = ObservableHandler.dropModified(this.snapshot)
262
if (this.myUpdated) {
263
this.myModified = false
266
this.myUpdated = true
267
if (isDifferent(this.current, this.snapshot, this.equivalent)) {
268
ObservableHandler.detach(this.snapshot, this)
269
ObservableHandler.attach(this.current, this)
270
this.snapshot = this.current
271
this.myModified = true
273
this.myModified = isModified
276
this.dependencies?.updateDependencies(this.myModified)
279
get disposed(): boolean {
280
return this.manager === undefined
284
const manager = this.manager
285
if (manager === undefined) return
286
manager.checkForStateDisposing()
287
this.manager = undefined
288
this.tracker = undefined
289
this.dependencies = undefined
290
manager.removeCreatedState(this, this.name)
294
let str = this.global ? "GlobalState" : "LocalState"
295
if (this.name !== undefined) str += "(" + this.name + ")"
296
if (this.disposed) str += ",disposed"
297
if (this.myModified) str += ",modified"
298
return this.manager?.frozen == true
299
? (str + ",frozen=" + this.snapshot)
300
: (str + "=" + this.current)
304
class ParameterImpl<Value> implements MutableState<Value> {
305
private manager: StateManagerImpl | undefined = undefined
306
private dependencies: Dependencies | undefined = undefined
307
private name: string | undefined = undefined
308
private _value: Value
309
private _modified = false
316
constructor(manager: StateManagerImpl, value: Value, name?: string) {
317
this.manager = manager
318
this.dependencies = new Dependencies()
323
get modified(): boolean {
324
this.dependencies?.register(this.manager?.dependency)
325
return this._modified
329
this.dependencies?.register(this.manager?.dependency)
333
set value(value: Value) {
337
update(value: Value, equivalent?: Equivalent<Value>): void {
338
const isModified = ObservableHandler.dropModified(this._value)
339
if (isDifferent(this._value, value, equivalent)) {
341
this._modified = true
343
this._modified = isModified
345
this.dependencies?.updateDependencies(this._modified)
348
get disposed(): boolean {
349
return this.manager === undefined
353
const manager = this.manager
354
if (manager === undefined) return
355
manager.checkForStateDisposing()
356
this.manager = undefined
357
this.dependencies = undefined
361
let str = "Parameter"
362
if (this.name !== undefined) str += "(" + this.name + ")"
363
if (this.disposed) str += ",disposed"
364
if (this._modified) str += ",modified"
365
return str + "=" + this._value
369
function isDifferent<Value>(value1: Value, value2: Value, equivalent?: Equivalent<Value>): boolean {
370
return !refEqual(value1, value2) && (equivalent?.(value1, value2) != true)
373
class StateManagerImpl implements StateManager {
374
private stateCreating: string | undefined = undefined
375
private readonly statesNamed = new Map<string, Disposable>()
376
private readonly statesCreated = new Set<ManagedState>()
377
private readonly dirtyScopes = new Set<ManagedScope>()
378
current: ManagedScope | undefined = undefined
379
external: Dependency | undefined = undefined
382
private readonly callbacks = new Array<() => void>()
388
if (this.statesNamed.size > 0) {
389
disposeContent(this.statesNamed.values())
390
this.statesNamed.clear()
392
if (this.statesCreated.size > 0) {
393
disposeContent(this.statesCreated.keys())
394
this.statesCreated.clear()
396
this.dirtyScopes.clear()
397
this.callbacks.splice(0, this.callbacks.length)
398
this.updateNeeded = false
403
const scope = this.current
404
return scope !== undefined ? scope.toHierarchy() : ""
407
updateSnapshot(): uint32 {
408
KoalaProfiler.counters?.updateSnapshotEnter()
409
this.checkForStateComputing()
411
if (!this.updateNeeded) return 0
412
let modified: uint32 = 0
414
const created = this.statesCreated.size as int32
416
const it = this.statesCreated.keys()
418
const result = it.next()
419
if (result.done) break
420
result.value?.updateStateSnapshot()
421
if (result.value?.modified == true) modified++
424
KoalaProfiler.counters?.updateSnapshot(modified, created)
426
while (this.dirtyScopes.size > 0) {
427
const scopes = Array_from_set(this.dirtyScopes)
428
this.dirtyScopes.clear()
429
const length = scopes.length
430
for (let i = 0; i < length; i++) {
431
if (scopes[i].modified) modified++
434
KoalaProfiler.counters?.updateSnapshot(modified)
435
this.updateNeeded = modified > 0
436
KoalaProfiler.counters?.updateSnapshotExit()
440
updatableNode<Node extends IncrementalNode>(node: Node, update: (context: StateContext) => void, cleanup?: () => void): ComputableState<Node> {
441
this.checkForStateComputing()
442
const scope = new ScopeImpl<Node>(KoalaCallsiteKeys.empty, 0, () => {
449
scope.dependencies = new Dependencies()
450
scope.setNamedState(CONTEXT_ROOT_SCOPE, new StateImpl<ScopeImpl<Node>>(this, scope, false))
451
scope.setNamedState(CONTEXT_ROOT_NODE, new StateImpl<Node>(this, node, false))
455
computableState<Value>(compute: (context: StateContext) => Value, cleanup?: (context: StateContext, value: Value | undefined) => void): ComputableState<Value> {
456
if (this.current?.once == false) throw new Error("computable state created in memo-context without remember")
457
this.checkForStateCreating()
458
const scope = new ScopeImpl<Value>(KoalaCallsiteKeys.empty, 0, () => compute(this), cleanup ? () => cleanup(this, undefined) : undefined)
460
scope.dependencies = new Dependencies()
461
this.current?.addCreatedState(scope)
465
scheduleCallback(callback: () => void): void {
466
this.callbacks.push(callback)
469
callCallbacks(): void {
470
const length = this.callbacks.length
472
const callbacks = this.callbacks.splice(0, length)
473
for (let i = 0; i < length; i++) callbacks[i]()
477
mutableState<Value>(initial: Value, global?: boolean, equivalent?: Equivalent<Value>, tracker?: ValueTracker<Value>): StateImpl<Value> {
478
if (!global && this.current?.once == false) throw new Error("unnamed local state created in memo-context without remember")
479
if (global === undefined) global = this.current?.once != true
480
else if (!global && !this.current) throw new Error("unnamed local state created in global context")
481
return new StateImpl<Value>(this, initial, global, equivalent, tracker)
484
get node(): IncrementalNode | undefined {
485
return this.current?.nodeRef
495
get dependency(): Dependency | undefined {
496
if (this.stateCreating === undefined) {
497
const scope = this.current
498
if (scope?.once == false && (scope?.nodeRef === undefined || this.external === undefined)) {
505
scope<Value>(id: KoalaCallsiteKey, paramCount: int32 = 0, create?: () => IncrementalNode, compute?: () => Value, cleanup?: (value: Value | undefined) => void, once?: boolean): InternalScope<Value> {
506
const counters = KoalaProfiler.counters
507
if (counters !== undefined) {
508
create ? counters.build() : counters.compute()
510
const scope = this.current
511
if (scope !== undefined) return scope.getChildScope<Value>(id, paramCount, create, compute, cleanup, once)
512
throw new Error("prohibited to create scope(" + KoalaCallsiteKeys.asString(id) + ") for the top level")
515
controlledScope(id: KoalaCallsiteKey, invalidate: () => void): ControlledScope {
516
const scope = this.scope<ControlledScopeImpl>(id, 0, undefined, undefined, ControlledScopeImpl.cleanup, true)
517
return scope.unchanged ? scope.cached : scope.recache(new ControlledScopeImpl(this, invalidate))
520
attach<Node extends IncrementalNode>(id: KoalaCallsiteKey, create: () => Node, update: () => void, cleanup?: () => void): void {
521
const scope = this.scope<void>(id, 0, create, undefined, cleanup, undefined)
522
scope.unchanged ? scope.cached : scope.recache(update())
525
compute<Value>(id: KoalaCallsiteKey, compute: () => Value, cleanup?: (value: Value | undefined) => void, once: boolean = false): Value {
526
const scope = this.scope<Value>(id, 0, undefined, undefined, cleanup, once)
527
return scope.unchanged ? scope.cached : scope.recache(compute())
535
namedState<Value>(name: string, create: () => Value, global?: boolean, equivalent?: Equivalent<Value>, tracker?: ValueTracker<Value>): MutableState<Value> {
536
const scope = this.current
537
if (global === undefined) global = scope === undefined
538
let state = global ? this.getNamedState<Value>(name) : scope?.getNamedState<Value>(name)
539
if (state !== undefined) return state
540
this.checkForStateCreating()
541
this.stateCreating = name
542
let initial = create()
543
this.stateCreating = undefined
544
state = new StateImpl<Value>(this, initial, global, equivalent, tracker, name)
545
if (global) this.statesNamed.set(name, state)
546
else if (scope !== undefined) scope.setNamedState(name, state)
547
else throw new Error("local state '" + name + "' created in global context")
551
stateBy<Value>(name: string, global?: boolean): MutableState<Value> | undefined {
552
if (global == true) return this.getNamedState<Value>(name)
553
for (let scope = this.current; scope !== undefined; scope = scope!.parent) {
554
const state = scope!.getNamedState<Value>(name)
555
if (state !== undefined) return state
557
return global == false ? undefined : this.getNamedState<Value>(name)
560
valueBy<Value>(name: string, global?: boolean): Value {
561
const state = this.stateBy<Value>(name, global)
562
if (state !== undefined) return state.value
563
const scope = this.current
564
throw new Error(scope !== undefined
565
? ("state(" + name + ") is not defined in scope(" + KoalaCallsiteKeys.asString(scope.id) + ")")
566
: ("global state(" + name + ") is not defined"))
569
addDirtyScope(state: ManagedScope): void {
570
this.dirtyScopes.add(state)
573
addCreatedState(state: ManagedState): void {
574
this.statesCreated.add(state)
575
if (!state.global) this.current?.addCreatedState(state)
578
removeCreatedState(state: ManagedState, name?: string): void {
579
if (state.global && name !== undefined) this.statesNamed.delete(name)
580
this.statesCreated.delete(state)
583
getNamedState<T>(name: string): StateImpl<T> | undefined {
584
const state = this.statesNamed.get(name)
585
return state instanceof StateImpl ? state as StateImpl<T> : undefined
588
checkForStateDisposing(): void {
589
this.current?.manager !== undefined
590
? this.checkForStateComputing()
591
: this.checkForStateCreating()
594
checkForStateCreating(): void {
595
const name = this.stateCreating
596
if (name === undefined) return
597
const scope = this.current
598
throw new Error(scope !== undefined
599
? ("prohibited when creating state(" + name + ") in scope(" + KoalaCallsiteKeys.asString(scope.id) + ")")
600
: ("prohibited when creating global state(" + name + ")"))
603
private checkForStateComputing(): void {
604
this.checkForStateCreating()
605
const scope = this.current
606
if (scope !== undefined) throw new Error("prohibited when computing scope(" + KoalaCallsiteKeys.asString(scope.id) + ")")
610
class ScopeImpl<Value> implements ManagedScope, InternalScope<Value>, ComputableState<Value> {
611
recomputeNeeded = true
612
manager: StateManagerImpl | undefined = undefined
613
dependencies: Dependencies | undefined = undefined
615
private myCompute: (() => Value) | undefined = undefined
616
private myCleanup: ((value: Value | undefined) => void) | undefined = undefined
617
private myValue: Value | undefined = undefined
618
private myModified = false
619
private myComputed = false
621
private params: Array<Disposable | undefined> | undefined = undefined
622
private statesNamed: Map<string, Disposable> | undefined = undefined
623
private statesCreated: Array<Disposable> | undefined = undefined
625
private scopeInternal: ManagedScope | undefined = undefined
626
private incremental: ManagedScope | undefined = undefined
627
private child: ManagedScope | undefined = undefined
629
parent: ManagedScope | undefined = undefined
630
next: ManagedScope | undefined = undefined
632
readonly id: KoalaCallsiteKey
633
once: boolean = false
634
node: IncrementalNode | undefined = undefined
635
nodeRef: IncrementalNode | undefined = undefined
636
nodeCount: uint32 = 0
638
constructor(id: KoalaCallsiteKey, paramCount: int32, compute?: () => Value, cleanup?: (value: Value | undefined) => void) {
640
this.params = paramCount > 0 ? new Array<Disposable>(paramCount) : undefined
641
this.myCompute = compute
642
this.myCleanup = cleanup
645
addCreatedState(state: Disposable): void {
646
if (this.statesCreated === undefined) this.statesCreated = new Array<Disposable>()
647
this.statesCreated!.push(state)
650
setNamedState(name: string, state: Disposable): void {
651
if (this.statesNamed === undefined) this.statesNamed = new Map<string, Disposable>()
652
this.statesNamed!.set(name, state)
655
getNamedState<T>(name: string): MutableState<T> | undefined {
656
return this.statesNamed !== undefined ? this.statesNamed!.get(name) as MutableState<T> : undefined
659
getChildScope<Value>(id: KoalaCallsiteKey, paramCount: int32, create?: () => IncrementalNode, compute?: () => Value, cleanup?: (value: Value | undefined) => void, once: boolean = false): ScopeImpl<Value> {
660
const manager = this.manager
661
if (manager === undefined) throw new Error("prohibited to create scope(" + KoalaCallsiteKeys.asString(id) + ") within the disposed scope(" + KoalaCallsiteKeys.asString(this.id) + ")")
662
manager.checkForStateCreating()
663
const inc = this.incremental
664
const next = inc ? inc.next : this.child
665
for (let child = next; child !== undefined; child = child!.next) {
666
if (child!.id == id) {
667
this.detachChildScopes(child!)
668
this.incremental = child
669
return child as ScopeImpl<Value>
672
if (!once && this.once) throw new Error("prohibited to create scope(" + KoalaCallsiteKeys.asString(id) + ") within the remember scope(" + KoalaCallsiteKeys.asString(this.id) + ")")
673
const scope = new ScopeImpl<Value>(id, paramCount, compute, cleanup)
674
scope.manager = manager
678
manager.current = scope
679
if (this.nodeRef === undefined) throw new Error("prohibited to add nodes into computable state")
680
scope.node = create()
681
manager.current = this
683
scope.nodeRef = scope.node ?? this.nodeRef
684
scope.once = once == true
687
if (inc !== undefined) {
692
this.incremental = scope
696
private detachChildScopes(last?: ManagedScope): void {
697
const inc = this.incremental
698
let child = inc ? inc.next : this.child
699
if (child === last) return
705
const manager = this.manager
706
if (manager === undefined) throw new Error("unexpected")
707
const scope = manager.current
708
manager.current = undefined
709
while (child != last) {
710
if (child === undefined) throw new Error("unexpected")
714
manager.current = scope
717
increment(count: uint32, skip: boolean): void {
719
this.nodeCount += count
720
if (skip) this.nodeRef!.incrementalUpdateSkip(count)
725
if (this.unchanged) return this.cached
726
let value = this.myValue
728
const compute = this.myCompute
729
if (compute === undefined) throw new Error("Wrong use of Internal API")
737
get unchanged(): boolean {
738
if (this.recomputeNeeded) {
739
this.incremental = undefined
741
if (this.manager !== undefined) {
742
this.scopeInternal = this.manager!.current
743
this.manager!.current = this
747
this.parent?.increment(this.node ? 1 : this.nodeCount, true)
752
recache(newValue?: Value): Value {
753
if (this.manager !== undefined) this.manager!.current = this.scopeInternal
754
const oldValue = this.myValue
755
this.myValue = newValue
756
this.myModified = this.myComputed && !refEqual(newValue, oldValue)
757
this.myComputed = true
758
this.recomputeNeeded = false
759
this.detachChildScopes()
760
this.parent?.increment(this.node ? 1 : this.nodeCount, false)
761
this.node?.incrementalUpdateDone(this.parent?.nodeRef)
765
get cached(): Value {
766
this.dependencies?.register(this.manager?.dependency)
767
this.dependencies?.updateDependencies(this.myModified)
768
return this.myValue as Value
771
param<V>(index: int32, value: V, equivalent?: Equivalent<V>, name?: string, contextLocal: boolean = false): State<V> {
772
const manager = this.manager
773
const params = this.params
774
if (manager === undefined || params === undefined) throw new Error("Wrong use of Internal API")
775
let state = params[index] as (ParameterImpl<V> | undefined)
776
if (state !== undefined) {
777
if (contextLocal && name && state != this.getNamedState(name)) throw new Error("name was unexpectedly changed to " + name)
778
state!.update(value, equivalent)
780
params[index] = state = new ParameterImpl<V>(manager, value, name)
781
if (contextLocal && name) this.setNamedState(name, state)
786
get modified(): boolean {
787
if (this.recomputeNeeded) this.value
788
else this.dependencies?.register(this.manager?.dependency)
789
return this.myModified
792
get obsolete(): boolean {
793
return this.manager === undefined
797
const current = this.manager?.current
798
let scope: ManagedScope = this
800
if (scope === current) break
801
if (!scope.recomputeNeeded) KoalaProfiler.counters?.invalidation()
802
scope.recomputeNeeded = true
803
const parent = scope.parent
804
if (parent !== undefined) {
813
if (scope.dependencies?.empty == false) {
814
this.manager?.addDirtyScope(scope)
821
get disposed(): boolean {
822
return this.manager === undefined
826
const manager = this.manager
827
if (manager === undefined) return
828
manager.checkForStateDisposing()
829
let error: Error | undefined = undefined
830
this.manager = undefined
831
this.dependencies = undefined
832
const scope = manager.current
833
manager.current = this
835
this.myCleanup?.(this.myValue)
837
error = cause as Error
839
for (let child = this.child; child !== undefined; child = child!.next) {
842
this.child = undefined
843
this.parent = undefined
845
this.node = undefined
846
this.nodeRef = undefined
847
this.scopeInternal = undefined
848
if (this.statesCreated !== undefined) {
849
disposeContentBackward<Disposable>(this.statesCreated!)
850
this.statesCreated = undefined
852
if (this.params !== undefined) {
853
disposeContentBackward<Disposable>(this.params!)
854
this.params = undefined
856
manager.current = scope
857
this.myModified = false
858
if (error !== undefined) throw error
862
let str: string = KoalaCallsiteKeys.asString(this.id)
863
if (this.once) str += " remember..."
864
if (this.node !== undefined) str += " " + className(this.node)
865
if (this === this.manager?.current) str += " (*)"
869
toHierarchy(): string {
871
for (let node = this.parent; node !== undefined; node = node!.parent) str += " "
872
str += this.toString()
873
for (let node = this.child; node !== undefined; node = node!.next) str += "\n" + node!.toHierarchy()
878
class ControlledScopeImpl implements Dependency, ControlledScope {
879
private manager: StateManagerImpl | undefined
880
private old: Dependency | undefined = undefined
881
private readonly _invalidate: () => void
883
constructor(manager: StateManagerImpl, invalidate: () => void) {
884
this.manager = manager
885
this._invalidate = invalidate
892
static cleanup(scope?: ControlledScopeImpl): void {
893
if (scope !== undefined) scope.manager = undefined
896
get obsolete(): boolean {
897
return this.manager === undefined
901
const manager = this.manager
902
if (manager === undefined) throw new Error("ControlledScope is already disposed")
903
this.old = manager.external
904
manager.external = this
908
const manager = this.manager
909
if (manager === undefined) throw new Error("ControlledScope is already disposed")
910
if (manager.external !== this) throw new Error("ControlledScope is not valid")
911
manager.external = this.old