continuation-local-storage
Advanced tools
Comparing version 2.4.4 to 2.5.0
128
context.js
@@ -6,2 +6,9 @@ 'use strict'; | ||
/* | ||
* | ||
* CONSTANTS | ||
* | ||
*/ | ||
var CONTEXTS_SYMBOL = 'cls@contexts'; | ||
// load polyfill if native support is unavailable | ||
@@ -16,3 +23,3 @@ if (!process.addAsyncListener) require('async-listener'); | ||
this.active = Object.create(null); | ||
this._stack = []; | ||
this._set = []; | ||
this.id = null; | ||
@@ -63,3 +70,3 @@ } | ||
this._stack.push(this.active); | ||
this._set.push(this.active); | ||
this.active = context; | ||
@@ -73,4 +80,4 @@ }; | ||
if (this.active === context) { | ||
assert.ok(this._stack.length, "can't remove top context"); | ||
this.active = this._stack.pop(); | ||
assert.ok(this._set.length, "can't remove top context"); | ||
this.active = this._set.pop(); | ||
return; | ||
@@ -80,3 +87,3 @@ } | ||
// Fast search in the stack using lastIndexOf | ||
var index = this._stack.lastIndexOf(context); | ||
var index = this._set.lastIndexOf(context); | ||
@@ -86,95 +93,36 @@ assert.ok(index >= 0, "context not currently entered; can't exit"); | ||
this.active = this._stack[index - 1]; | ||
this._stack.length = index - 1; | ||
this._set.splice(index, 1); | ||
}; | ||
Namespace.prototype.bindEmitter = function (source) { | ||
assert.ok(source.on && source.addListener && source.emit, "can only bind real EEs"); | ||
Namespace.prototype.bindEmitter = function (emitter) { | ||
assert.ok(emitter.on && emitter.addListener && emitter.emit, "can only bind real EEs"); | ||
var namespace = this; | ||
var contextName = 'context@' + this.name; | ||
var namespace = this; | ||
var thisSymbol = 'context@' + this.name; | ||
/** | ||
* Attach a context to a listener, and make sure that this hook stays | ||
* attached to the emitter forevermore. | ||
*/ | ||
function capturer(on) { | ||
return function captured(event, listener) { | ||
listener[contextName] = namespace.active; | ||
try { | ||
return on.call(this, event, listener); | ||
} | ||
finally { | ||
// old-style streaming overwrites .on and .addListener, so rewrap | ||
if (!this.on.__wrapped) shimmer.wrap(this, 'on', capturer); | ||
if (!this.addListener.__wrapped) shimmer.wrap(this, 'addListener', capturer); | ||
} | ||
// Capture the context active at the time the emitter is bound. | ||
function attach(listener) { | ||
if (!listener) return; | ||
if (!listener[CONTEXTS_SYMBOL]) listener[CONTEXTS_SYMBOL] = Object.create(null); | ||
listener[CONTEXTS_SYMBOL][thisSymbol] = { | ||
namespace : namespace, | ||
context : namespace.active | ||
}; | ||
} | ||
/** | ||
* Evaluate listeners within the CLS contexts in which they were originally | ||
* captured. | ||
*/ | ||
function puncher(emit) { | ||
// find all the handlers with attached contexts | ||
function prepare(unwrapped) { | ||
if (!unwrapped) return; | ||
// At emit time, bind the listener within the correct context. | ||
function bind(unwrapped) { | ||
if (!(unwrapped && unwrapped[CONTEXTS_SYMBOL])) return unwrapped; | ||
if (typeof unwrapped === 'function' && unwrapped[contextName]) { | ||
return namespace.bind(unwrapped, unwrapped[contextName]); | ||
} | ||
else if (Array.isArray(unwrapped) && unwrapped.length) { | ||
var replacements = []; | ||
for (var i = 0; i < unwrapped.length; i++) { | ||
var handler = unwrapped[i]; | ||
var context = handler[contextName]; | ||
if (context) handler = namespace.bind(handler, context); | ||
replacements[i] = handler; | ||
} | ||
return replacements; | ||
} | ||
else { | ||
return unwrapped; | ||
} | ||
} | ||
return function punched(event) { | ||
if (!this._events || !this._events[event]) return emit.apply(this, arguments); | ||
// wrap | ||
var unwrapped = this._events[event]; | ||
function releaser(removeListener) { | ||
return function unwrapRemove() { | ||
this._events[event] = unwrapped; | ||
try { | ||
return removeListener.apply(this, arguments); | ||
} | ||
finally { | ||
unwrapped = this._events[event]; | ||
this._events[event] = prepare(unwrapped); | ||
} | ||
}; | ||
} | ||
shimmer.wrap(this, 'removeListener', releaser); | ||
this._events[event] = prepare(unwrapped); | ||
try { | ||
// apply | ||
return emit.apply(this, arguments); | ||
} | ||
finally { | ||
// reset | ||
shimmer.unwrap(this, 'removeListener'); | ||
this._events[event] = unwrapped; | ||
} | ||
}; | ||
var wrapped = unwrapped; | ||
var contexts = unwrapped[CONTEXTS_SYMBOL]; | ||
Object.keys(contexts).forEach(function (name) { | ||
var thunk = contexts[name]; | ||
wrapped = thunk.namespace.bind(wrapped, thunk.context); | ||
}); | ||
return wrapped; | ||
} | ||
shimmer.wrap(source, 'addListener', capturer); | ||
shimmer.wrap(source, 'on', capturer); | ||
shimmer.wrap(source, 'emit', puncher); | ||
shimmer.wrapEmitter(emitter, attach, bind); | ||
}; | ||
@@ -191,5 +139,3 @@ | ||
namespace.id = process.addAsyncListener( | ||
function () { | ||
return namespace.active; | ||
}, | ||
function () { return namespace.active; }, | ||
{ | ||
@@ -196,0 +142,0 @@ before : function (context, domain) { namespace.enter(domain); }, |
{ | ||
"name": "continuation-local-storage", | ||
"version": "2.4.4", | ||
"version": "2.5.0", | ||
"description": "userland implementation of https://github.com/joyent/node/issues/5243", | ||
@@ -37,4 +37,4 @@ "main": "context.js", | ||
"dependencies": { | ||
"shimmer": "0.7.3" | ||
"shimmer": "~0.9" | ||
} | ||
} |
@@ -16,3 +16,3 @@ 'use strict'; | ||
test("event emitters bound to CLS context", function (t) { | ||
t.plan(10); | ||
t.plan(12); | ||
@@ -210,3 +210,4 @@ t.test("handler registered in context", function (t) { | ||
t.equal(typeof re._events.data, 'function', 'only the one data listener'); | ||
t.ok(re._events.data['context@outOnReadable'], "context is bound to listener"); | ||
t.ok(re._events.data['cls@contexts']['context@outOnReadable'], | ||
"context is bound to listener"); | ||
@@ -277,3 +278,3 @@ re.emit('data', 'blah'); | ||
var ee = new EventEmitter() | ||
, n = fresh('kaboom', this) | ||
, n = fresh('param_list', this) | ||
; | ||
@@ -291,2 +292,60 @@ | ||
}); | ||
t.test("listener that throws doesn't leave removeListener wrapped", function (t) { | ||
t.plan(4); | ||
var ee = new EventEmitter() | ||
, n = fresh('kaboom', this) | ||
; | ||
n.bindEmitter(ee); | ||
function kaboom() { | ||
throw new Error('whoops'); | ||
} | ||
n.run(function () { | ||
ee.on('bad', kaboom); | ||
t.throws(function () { ee.emit('bad'); }); | ||
t.equal(typeof ee.removeListener, 'function', 'removeListener is still there'); | ||
t.notOk(ee.removeListener.__wrapped, "removeListener got unwrapped"); | ||
t.equal(ee._events.bad, kaboom, "listener isn't still bound"); | ||
}); | ||
}); | ||
t.test("emitter bound to multiple namespaces handles them correctly", function (t) { | ||
t.plan(6); | ||
var ee = new EventEmitter() | ||
, ns1 = cls.createNamespace('1') | ||
, ns2 = cls.createNamespace('2') | ||
; | ||
// emulate an incoming data emitter | ||
setTimeout(function () { | ||
ee.emit('data', 'hi'); | ||
}, 10); | ||
ns1.set('name', 'tom1'); | ||
ns2.set('name', 'tom2'); | ||
t.doesNotThrow(function () { ns1.bindEmitter(ee); }); | ||
t.doesNotThrow(function () { ns2.bindEmitter(ee); }); | ||
ns1.run(function () { | ||
process.nextTick(function () { | ||
t.equal(ns1.get('name'), 'tom1', "ns1 value correct"); | ||
t.equal(ns2.get('name'), 'tom2', "ns2 value correct"); | ||
ns1.set('name', 'bob'); | ||
ns2.set('name', 'alice'); | ||
ee.on('data', function () { | ||
t.equal(ns1.get('name'), 'bob', "ns1 value bound onto emitter"); | ||
t.equal(ns2.get('name'), 'alice', "ns2 value bound onto emitter"); | ||
}); | ||
}); | ||
}); | ||
}); | ||
}); |
@@ -19,3 +19,3 @@ 'use strict'; | ||
t.notOk(namespace.get('inner'), "inner context should have been exited by throw"); | ||
t.equal(namespace._stack.length, 0, "should be back to global state"); | ||
t.equal(namespace._set.length, 0, "should be back to global state"); | ||
@@ -22,0 +22,0 @@ t.end(); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
80101
22
1930
+ Addedshimmer@0.9.2(transitive)
Updatedshimmer@~0.9