dns: add missing exports.BADNAME
[platform/upstream/nodejs.git] / lib / events.js
1 'use strict';
2
3 var domain;
4
5 function EventEmitter() {
6   EventEmitter.init.call(this);
7 }
8 module.exports = EventEmitter;
9
10 // Backwards-compat with node 0.10.x
11 EventEmitter.EventEmitter = EventEmitter;
12
13 EventEmitter.usingDomains = false;
14
15 EventEmitter.prototype.domain = undefined;
16 EventEmitter.prototype._events = undefined;
17 EventEmitter.prototype._maxListeners = undefined;
18
19 // By default EventEmitters will print a warning if more than 10 listeners are
20 // added to it. This is a useful default which helps finding memory leaks.
21 EventEmitter.defaultMaxListeners = 10;
22
23 EventEmitter.init = function() {
24   this.domain = null;
25   if (EventEmitter.usingDomains) {
26     // if there is an active domain, then attach to it.
27     domain = domain || require('domain');
28     if (domain.active && !(this instanceof domain.Domain)) {
29       this.domain = domain.active;
30     }
31   }
32
33   if (!this._events || this._events === Object.getPrototypeOf(this)._events) {
34     this._events = {};
35     this._eventsCount = 0;
36   }
37
38   this._maxListeners = this._maxListeners || undefined;
39 };
40
41 // Obviously not all Emitters should be limited to 10. This function allows
42 // that to be increased. Set to zero for unlimited.
43 EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
44   if (typeof n !== 'number' || n < 0 || isNaN(n))
45     throw new TypeError('n must be a positive number');
46   this._maxListeners = n;
47   return this;
48 };
49
50 function $getMaxListeners(that) {
51   if (that._maxListeners === undefined)
52     return EventEmitter.defaultMaxListeners;
53   return that._maxListeners;
54 }
55
56 EventEmitter.prototype.getMaxListeners = function getMaxListeners() {
57   return $getMaxListeners(this);
58 };
59
60 // These standalone emit* functions are used to optimize calling of event
61 // handlers for fast cases because emit() itself often has a variable number of
62 // arguments and can be deoptimized because of that. These functions always have
63 // the same number of arguments and thus do not get deoptimized, so the code
64 // inside them can execute faster.
65 function emitNone(handler, isFn, self) {
66   if (isFn)
67     handler.call(self);
68   else {
69     var len = handler.length;
70     var listeners = arrayClone(handler, len);
71     for (var i = 0; i < len; ++i)
72       listeners[i].call(self);
73   }
74 }
75 function emitOne(handler, isFn, self, arg1) {
76   if (isFn)
77     handler.call(self, arg1);
78   else {
79     var len = handler.length;
80     var listeners = arrayClone(handler, len);
81     for (var i = 0; i < len; ++i)
82       listeners[i].call(self, arg1);
83   }
84 }
85 function emitTwo(handler, isFn, self, arg1, arg2) {
86   if (isFn)
87     handler.call(self, arg1, arg2);
88   else {
89     var len = handler.length;
90     var listeners = arrayClone(handler, len);
91     for (var i = 0; i < len; ++i)
92       listeners[i].call(self, arg1, arg2);
93   }
94 }
95 function emitThree(handler, isFn, self, arg1, arg2, arg3) {
96   if (isFn)
97     handler.call(self, arg1, arg2, arg3);
98   else {
99     var len = handler.length;
100     var listeners = arrayClone(handler, len);
101     for (var i = 0; i < len; ++i)
102       listeners[i].call(self, arg1, arg2, arg3);
103   }
104 }
105
106 function emitMany(handler, isFn, self, args) {
107   if (isFn)
108     handler.apply(self, args);
109   else {
110     var len = handler.length;
111     var listeners = arrayClone(handler, len);
112     for (var i = 0; i < len; ++i)
113       listeners[i].apply(self, args);
114   }
115 }
116
117 EventEmitter.prototype.emit = function emit(type) {
118   var er, handler, len, args, i, events, domain;
119   var needDomainExit = false;
120   var doError = (type === 'error');
121
122   events = this._events;
123   if (events)
124     doError = (doError && events.error == null);
125   else if (!doError)
126     return false;
127
128   domain = this.domain;
129
130   // If there is no 'error' event listener then throw.
131   if (doError) {
132     er = arguments[1];
133     if (domain) {
134       if (!er)
135         er = new Error('Uncaught, unspecified "error" event.');
136       er.domainEmitter = this;
137       er.domain = domain;
138       er.domainThrown = false;
139       domain.emit('error', er);
140     } else if (er instanceof Error) {
141       throw er; // Unhandled 'error' event
142     } else {
143       // At least give some kind of context to the user
144       var err = new Error('Uncaught, unspecified "error" event. (' + er + ')');
145       err.context = er;
146       throw err;
147     }
148     return false;
149   }
150
151   handler = events[type];
152
153   if (!handler)
154     return false;
155
156   if (domain && this !== process) {
157     domain.enter();
158     needDomainExit = true;
159   }
160
161   var isFn = typeof handler === 'function';
162   len = arguments.length;
163   switch (len) {
164     // fast cases
165     case 1:
166       emitNone(handler, isFn, this);
167       break;
168     case 2:
169       emitOne(handler, isFn, this, arguments[1]);
170       break;
171     case 3:
172       emitTwo(handler, isFn, this, arguments[1], arguments[2]);
173       break;
174     case 4:
175       emitThree(handler, isFn, this, arguments[1], arguments[2], arguments[3]);
176       break;
177     // slower
178     default:
179       args = new Array(len - 1);
180       for (i = 1; i < len; i++)
181         args[i - 1] = arguments[i];
182       emitMany(handler, isFn, this, args);
183   }
184
185   if (needDomainExit)
186     domain.exit();
187
188   return true;
189 };
190
191 EventEmitter.prototype.addListener = function addListener(type, listener) {
192   var m;
193   var events;
194   var existing;
195
196   if (typeof listener !== 'function')
197     throw new TypeError('listener must be a function');
198
199   events = this._events;
200   if (!events) {
201     events = this._events = {};
202     this._eventsCount = 0;
203   } else {
204     // To avoid recursion in the case that type === "newListener"! Before
205     // adding it to the listeners, first emit "newListener".
206     if (events.newListener) {
207       this.emit('newListener', type,
208                 listener.listener ? listener.listener : listener);
209
210       // Re-assign `events` because a newListener handler could have caused the
211       // this._events to be assigned to a new object
212       events = this._events;
213     }
214     existing = events[type];
215   }
216
217   if (!existing) {
218     // Optimize the case of one listener. Don't need the extra array object.
219     existing = events[type] = listener;
220     ++this._eventsCount;
221   } else {
222     if (typeof existing === 'function') {
223       // Adding the second element, need to change to array.
224       existing = events[type] = [existing, listener];
225     } else {
226       // If we've already got an array, just append.
227       existing.push(listener);
228     }
229
230     // Check for listener leak
231     if (!existing.warned) {
232       m = $getMaxListeners(this);
233       if (m && m > 0 && existing.length > m) {
234         existing.warned = true;
235         console.error('(node) warning: possible EventEmitter memory ' +
236                       'leak detected. %d %s listeners added. ' +
237                       'Use emitter.setMaxListeners() to increase limit.',
238                       existing.length, type);
239         console.trace();
240       }
241     }
242   }
243
244   return this;
245 };
246
247 EventEmitter.prototype.on = EventEmitter.prototype.addListener;
248
249 EventEmitter.prototype.once = function once(type, listener) {
250   if (typeof listener !== 'function')
251     throw new TypeError('listener must be a function');
252
253   var fired = false;
254
255   function g() {
256     this.removeListener(type, g);
257
258     if (!fired) {
259       fired = true;
260       listener.apply(this, arguments);
261     }
262   }
263
264   g.listener = listener;
265   this.on(type, g);
266
267   return this;
268 };
269
270 // emits a 'removeListener' event iff the listener was removed
271 EventEmitter.prototype.removeListener =
272     function removeListener(type, listener) {
273       var list, events, position, i;
274
275       if (typeof listener !== 'function')
276         throw new TypeError('listener must be a function');
277
278       events = this._events;
279       if (!events)
280         return this;
281
282       list = events[type];
283       if (!list)
284         return this;
285
286       if (list === listener || (list.listener && list.listener === listener)) {
287         if (--this._eventsCount === 0)
288           this._events = {};
289         else {
290           delete events[type];
291           if (events.removeListener)
292             this.emit('removeListener', type, listener);
293         }
294       } else if (typeof list !== 'function') {
295         position = -1;
296
297         for (i = list.length; i-- > 0;) {
298           if (list[i] === listener ||
299               (list[i].listener && list[i].listener === listener)) {
300             position = i;
301             break;
302           }
303         }
304
305         if (position < 0)
306           return this;
307
308         if (list.length === 1) {
309           list[0] = undefined;
310           if (--this._eventsCount === 0) {
311             this._events = {};
312             return this;
313           } else {
314             delete events[type];
315           }
316         } else {
317           spliceOne(list, position);
318         }
319
320         if (events.removeListener)
321           this.emit('removeListener', type, listener);
322       }
323
324       return this;
325     };
326
327 EventEmitter.prototype.removeAllListeners =
328     function removeAllListeners(type) {
329       var listeners, events;
330
331       events = this._events;
332       if (!events)
333         return this;
334
335       // not listening for removeListener, no need to emit
336       if (!events.removeListener) {
337         if (arguments.length === 0) {
338           this._events = {};
339           this._eventsCount = 0;
340         } else if (events[type]) {
341           if (--this._eventsCount === 0)
342             this._events = {};
343           else
344             delete events[type];
345         }
346         return this;
347       }
348
349       // emit removeListener for all listeners on all events
350       if (arguments.length === 0) {
351         var keys = Object.keys(events);
352         for (var i = 0, key; i < keys.length; ++i) {
353           key = keys[i];
354           if (key === 'removeListener') continue;
355           this.removeAllListeners(key);
356         }
357         this.removeAllListeners('removeListener');
358         this._events = {};
359         this._eventsCount = 0;
360         return this;
361       }
362
363       listeners = events[type];
364
365       if (typeof listeners === 'function') {
366         this.removeListener(type, listeners);
367       } else if (listeners) {
368         // LIFO order
369         do {
370           this.removeListener(type, listeners[listeners.length - 1]);
371         } while (listeners[0]);
372       }
373
374       return this;
375     };
376
377 EventEmitter.prototype.listeners = function listeners(type) {
378   var evlistener;
379   var ret;
380   var events = this._events;
381
382   if (!events)
383     ret = [];
384   else {
385     evlistener = events[type];
386     if (!evlistener)
387       ret = [];
388     else if (typeof evlistener === 'function')
389       ret = [evlistener];
390     else
391       ret = arrayClone(evlistener, evlistener.length);
392   }
393
394   return ret;
395 };
396
397 EventEmitter.listenerCount = function(emitter, type) {
398   if (typeof emitter.listenerCount === 'function') {
399     return emitter.listenerCount(type);
400   } else {
401     return listenerCount.call(emitter, type);
402   }
403 };
404
405 EventEmitter.prototype.listenerCount = listenerCount;
406 function listenerCount(type) {
407   const events = this._events;
408
409   if (events) {
410     const evlistener = events[type];
411
412     if (typeof evlistener === 'function') {
413       return 1;
414     } else if (evlistener) {
415       return evlistener.length;
416     }
417   }
418
419   return 0;
420 };
421
422 // About 1.5x faster than the two-arg version of Array#splice().
423 function spliceOne(list, index) {
424   for (var i = index, k = i + 1, n = list.length; k < n; i += 1, k += 1)
425     list[i] = list[k];
426   list.pop();
427 }
428
429 function arrayClone(arr, i) {
430   var copy = new Array(i);
431   while (i--)
432     copy[i] = arr[i];
433   return copy;
434 }