##// END OF EJS Templates
Don't call .remove() method of objects bound to the container
cin -
r165:c446ccde5f9f v1.4.2 default
parent child
Show More
@@ -1,218 +1,216
1 import { IDestroyable, MapOf } from "../interfaces";
1 import { IDestroyable, MapOf } from "../interfaces";
2 import { argumentNotNull, isDestroyable, argumentNotEmptyString, isRemovable } from "../safe";
2 import { argumentNotNull, isDestroyable, argumentNotEmptyString, isRemovable } from "../safe";
3 import { ILifetime, ServiceContainer } from "./interfaces";
3 import { ILifetime, ServiceContainer } from "./interfaces";
4 import { ActivationContext } from "./ActivationContext";
4 import { ActivationContext } from "./ActivationContext";
5
5
6 function safeCall(item: () => void) {
6 function safeCall(item: () => void) {
7 try {
7 try {
8 item();
8 item();
9 } catch {
9 } catch {
10 // silence!
10 // silence!
11 }
11 }
12 }
12 }
13
13
14 const emptyLifetime: ILifetime = Object.freeze({
14 const emptyLifetime: ILifetime = Object.freeze({
15 has() {
15 has() {
16 return false;
16 return false;
17 },
17 },
18
18
19 initialize() {
19 initialize() {
20
20
21 },
21 },
22
22
23 get() {
23 get() {
24 throw new Error("The specified item isn't registered with this lifetime manager");
24 throw new Error("The specified item isn't registered with this lifetime manager");
25 },
25 },
26
26
27 store() {
27 store() {
28 // does nothing
28 // does nothing
29 },
29 },
30
30
31 toString() {
31 toString() {
32 return `[object EmptyLifetime]`;
32 return `[object EmptyLifetime]`;
33 }
33 }
34
34
35 });
35 });
36
36
37 const unknownLifetime: ILifetime = Object.freeze({
37 const unknownLifetime: ILifetime = Object.freeze({
38 has() {
38 has() {
39 return false;
39 return false;
40 },
40 },
41 initialize() {
41 initialize() {
42 throw new Error("Can't call initialize on the unknown lifetime object");
42 throw new Error("Can't call initialize on the unknown lifetime object");
43 },
43 },
44 get() {
44 get() {
45 throw new Error("The lifetime object isn't initialized");
45 throw new Error("The lifetime object isn't initialized");
46 },
46 },
47 store() {
47 store() {
48 throw new Error("Can't store a value in the unknown lifetime object");
48 throw new Error("Can't store a value in the unknown lifetime object");
49 },
49 },
50 toString() {
50 toString() {
51 return `[object UnknownLifetime]`;
51 return `[object UnknownLifetime]`;
52 }
52 }
53 });
53 });
54
54
55 let nextId = 0;
55 let nextId = 0;
56
56
57 const singletons: any = {};
57 const singletons: any = {};
58
58
59 export class LifetimeManager implements IDestroyable {
59 export class LifetimeManager implements IDestroyable {
60 private _cleanup: (() => void)[] = [];
60 private _cleanup: (() => void)[] = [];
61 private _cache: MapOf<any> = {};
61 private _cache: MapOf<any> = {};
62 private _destroyed = false;
62 private _destroyed = false;
63
63
64 private _pending: MapOf<boolean> = {};
64 private _pending: MapOf<boolean> = {};
65
65
66 create(): ILifetime {
66 create(): ILifetime {
67 const self = this;
67 const self = this;
68 const id = ++nextId;
68 const id = ++nextId;
69 return {
69 return {
70 has() {
70 has() {
71 return (id in self._cache);
71 return (id in self._cache);
72 },
72 },
73
73
74 get() {
74 get() {
75 const t = self._cache[id];
75 const t = self._cache[id];
76 if (t === undefined)
76 if (t === undefined)
77 throw new Error(`The item with with the key ${id} isn't found`);
77 throw new Error(`The item with with the key ${id} isn't found`);
78 return t;
78 return t;
79 },
79 },
80
80
81 initialize() {
81 initialize() {
82 if (self._pending[id])
82 if (self._pending[id])
83 throw Error(`Cyclic reference detected: the item with the key ${id} is already activating.`);
83 throw Error(`Cyclic reference detected: the item with the key ${id} is already activating.`);
84 self._pending[id] = true;
84 self._pending[id] = true;
85 },
85 },
86
86
87 store(item: any, cleanup?: (item: any) => void) {
87 store(item: any, cleanup?: (item: any) => void) {
88 argumentNotNull(id, "id");
88 argumentNotNull(id, "id");
89 argumentNotNull(item, "item");
89 argumentNotNull(item, "item");
90
90
91 if (this.has())
91 if (this.has())
92 throw new Error(`The item with with the key ${id} already registered with this lifetime manager`);
92 throw new Error(`The item with with the key ${id} already registered with this lifetime manager`);
93 delete self._pending[id];
93 delete self._pending[id];
94
94
95 self._cache[id] = item;
95 self._cache[id] = item;
96
96
97 if (self._destroyed)
97 if (self._destroyed)
98 throw new Error("Lifetime manager is destroyed");
98 throw new Error("Lifetime manager is destroyed");
99 if (cleanup) {
99 if (cleanup) {
100 self._cleanup.push(() => cleanup(item));
100 self._cleanup.push(() => cleanup(item));
101 } else if (isDestroyable(item)) {
101 } else if (isDestroyable(item)) {
102 self._cleanup.push(() => item.destroy());
102 self._cleanup.push(() => item.destroy());
103 } else if (isRemovable(item)) {
104 self._cleanup.push(() => item.remove());
105 }
103 }
106 }
104 }
107 };
105 };
108 }
106 }
109
107
110 destroy() {
108 destroy() {
111 if (!this._destroyed) {
109 if (!this._destroyed) {
112 this._destroyed = true;
110 this._destroyed = true;
113 this._cleanup.forEach(safeCall);
111 this._cleanup.forEach(safeCall);
114 this._cleanup.length = 0;
112 this._cleanup.length = 0;
115 }
113 }
116 }
114 }
117
115
118 static empty(): ILifetime {
116 static empty(): ILifetime {
119 return emptyLifetime;
117 return emptyLifetime;
120 }
118 }
121
119
122 static hierarchyLifetime() {
120 static hierarchyLifetime() {
123 let _lifetime = unknownLifetime;
121 let _lifetime = unknownLifetime;
124 return {
122 return {
125 initialize(context: ActivationContext<any>) {
123 initialize(context: ActivationContext<any>) {
126 if (_lifetime !== unknownLifetime)
124 if (_lifetime !== unknownLifetime)
127 throw new Error("Cyclic reference activation detected");
125 throw new Error("Cyclic reference activation detected");
128
126
129 _lifetime = context.getContainer().getLifetimeManager().create();
127 _lifetime = context.getContainer().getLifetimeManager().create();
130 },
128 },
131 get() {
129 get() {
132 return _lifetime.get();
130 return _lifetime.get();
133 },
131 },
134 has() {
132 has() {
135 return _lifetime.has();
133 return _lifetime.has();
136 },
134 },
137 store(item: any, cleanup?: (item: any) => void) {
135 store(item: any, cleanup?: (item: any) => void) {
138 return _lifetime.store(item, cleanup);
136 return _lifetime.store(item, cleanup);
139 },
137 },
140 toString() {
138 toString() {
141 return `[object HierarchyLifetime, has=${this.has()}]`;
139 return `[object HierarchyLifetime, has=${this.has()}]`;
142 }
140 }
143 };
141 };
144 }
142 }
145
143
146 static contextLifetime() {
144 static contextLifetime() {
147 let _lifetime = unknownLifetime;
145 let _lifetime = unknownLifetime;
148 return {
146 return {
149 initialize(context: ActivationContext<any>) {
147 initialize(context: ActivationContext<any>) {
150 if (_lifetime !== unknownLifetime)
148 if (_lifetime !== unknownLifetime)
151 throw new Error("Cyclic reference detected");
149 throw new Error("Cyclic reference detected");
152 _lifetime = context.createLifetime();
150 _lifetime = context.createLifetime();
153 },
151 },
154 get() {
152 get() {
155 return _lifetime.get();
153 return _lifetime.get();
156 },
154 },
157 has() {
155 has() {
158 return _lifetime.has();
156 return _lifetime.has();
159 },
157 },
160 store(item: any) {
158 store(item: any) {
161 _lifetime.store(item);
159 _lifetime.store(item);
162 },
160 },
163 toString() {
161 toString() {
164 return `[object ContextLifetime, has=${this.has()}]`;
162 return `[object ContextLifetime, has=${this.has()}]`;
165 }
163 }
166 };
164 };
167 }
165 }
168
166
169 static singletonLifetime(typeId: string) {
167 static singletonLifetime(typeId: string) {
170 argumentNotEmptyString(typeId, "typeId");
168 argumentNotEmptyString(typeId, "typeId");
171 let pending = false;
169 let pending = false;
172 return {
170 return {
173 has() {
171 has() {
174 return typeId in singletons;
172 return typeId in singletons;
175 },
173 },
176 get() {
174 get() {
177 if (!this.has())
175 if (!this.has())
178 throw new Error(`The instance ${typeId} doesn't exists`);
176 throw new Error(`The instance ${typeId} doesn't exists`);
179 return singletons[typeId];
177 return singletons[typeId];
180 },
178 },
181 initialize() {
179 initialize() {
182 if (pending)
180 if (pending)
183 throw new Error("Cyclic reference detected");
181 throw new Error("Cyclic reference detected");
184 pending = true;
182 pending = true;
185 },
183 },
186 store(item: any) {
184 store(item: any) {
187 singletons[typeId] = item;
185 singletons[typeId] = item;
188 pending = false;
186 pending = false;
189 },
187 },
190 toString() {
188 toString() {
191 return `[object SingletonLifetime, has=${this.has()}, typeId=${typeId}]`;
189 return `[object SingletonLifetime, has=${this.has()}, typeId=${typeId}]`;
192 }
190 }
193 };
191 };
194 }
192 }
195
193
196 static containerLifetime(container: ServiceContainer<any>) {
194 static containerLifetime(container: ServiceContainer<any>) {
197 let _lifetime = unknownLifetime;
195 let _lifetime = unknownLifetime;
198 return {
196 return {
199 initialize(context: ActivationContext<any>) {
197 initialize(context: ActivationContext<any>) {
200 if (_lifetime !== unknownLifetime)
198 if (_lifetime !== unknownLifetime)
201 throw new Error("Cyclic reference detected");
199 throw new Error("Cyclic reference detected");
202 _lifetime = container.getLifetimeManager().create();
200 _lifetime = container.getLifetimeManager().create();
203 },
201 },
204 get() {
202 get() {
205 return _lifetime.get();
203 return _lifetime.get();
206 },
204 },
207 has() {
205 has() {
208 return _lifetime.has();
206 return _lifetime.has();
209 },
207 },
210 store(item: any) {
208 store(item: any) {
211 _lifetime.store(item);
209 _lifetime.store(item);
212 },
210 },
213 toString() {
211 toString() {
214 return `[object ContainerLifetime, has=${_lifetime.has()}]`
212 return `[object ContainerLifetime, has=${_lifetime.has()}]`
215 }
213 }
216 };
214 };
217 }
215 }
218 }
216 }
General Comments 0
You need to be logged in to leave comments. Login now