| "use strict"; |
| |
| var originalObject = Object; |
| var originalDefProp = Object.defineProperty; |
| var originalCreate = Object.create; |
| |
| function defProp(obj, name, value) { |
| if (originalDefProp) try { |
| originalDefProp.call(originalObject, obj, name, { value: value }); |
| } catch (definePropertyIsBrokenInIE8) { |
| obj[name] = value; |
| } else { |
| obj[name] = value; |
| } |
| } |
| |
| // For functions that will be invoked using .call or .apply, we need to |
| // define those methods on the function objects themselves, rather than |
| // inheriting them from Function.prototype, so that a malicious or clumsy |
| // third party cannot interfere with the functionality of this module by |
| // redefining Function.prototype.call or .apply. |
| function makeSafeToCall(fun) { |
| if (fun) { |
| defProp(fun, "call", fun.call); |
| defProp(fun, "apply", fun.apply); |
| } |
| return fun; |
| } |
| |
| makeSafeToCall(originalDefProp); |
| makeSafeToCall(originalCreate); |
| |
| var hasOwn = makeSafeToCall(Object.prototype.hasOwnProperty); |
| var numToStr = makeSafeToCall(Number.prototype.toString); |
| var strSlice = makeSafeToCall(String.prototype.slice); |
| |
| var cloner = function(){}; |
| function create(prototype) { |
| if (originalCreate) { |
| return originalCreate.call(originalObject, prototype); |
| } |
| cloner.prototype = prototype || null; |
| return new cloner; |
| } |
| |
| var rand = Math.random; |
| var uniqueKeys = create(null); |
| |
| function makeUniqueKey() { |
| // Collisions are highly unlikely, but this module is in the business of |
| // making guarantees rather than safe bets. |
| do var uniqueKey = internString(strSlice.call(numToStr.call(rand(), 36), 2)); |
| while (hasOwn.call(uniqueKeys, uniqueKey)); |
| return uniqueKeys[uniqueKey] = uniqueKey; |
| } |
| |
| function internString(str) { |
| var obj = {}; |
| obj[str] = true; |
| return Object.keys(obj)[0]; |
| } |
| |
| // External users might find this function useful, but it is not necessary |
| // for the typical use of this module. |
| exports.makeUniqueKey = makeUniqueKey; |
| |
| // Object.getOwnPropertyNames is the only way to enumerate non-enumerable |
| // properties, so if we wrap it to ignore our secret keys, there should be |
| // no way (except guessing) to access those properties. |
| var originalGetOPNs = Object.getOwnPropertyNames; |
| Object.getOwnPropertyNames = function getOwnPropertyNames(object) { |
| for (var names = originalGetOPNs(object), |
| src = 0, |
| dst = 0, |
| len = names.length; |
| src < len; |
| ++src) { |
| if (!hasOwn.call(uniqueKeys, names[src])) { |
| if (src > dst) { |
| names[dst] = names[src]; |
| } |
| ++dst; |
| } |
| } |
| names.length = dst; |
| return names; |
| }; |
| |
| function defaultCreatorFn(object) { |
| return create(null); |
| } |
| |
| function makeAccessor(secretCreatorFn) { |
| var brand = makeUniqueKey(); |
| var passkey = create(null); |
| |
| secretCreatorFn = secretCreatorFn || defaultCreatorFn; |
| |
| function register(object) { |
| var secret; // Created lazily. |
| |
| function vault(key, forget) { |
| // Only code that has access to the passkey can retrieve (or forget) |
| // the secret object. |
| if (key === passkey) { |
| return forget |
| ? secret = null |
| : secret || (secret = secretCreatorFn(object)); |
| } |
| } |
| |
| defProp(object, brand, vault); |
| } |
| |
| function accessor(object) { |
| if (!hasOwn.call(object, brand)) |
| register(object); |
| return object[brand](passkey); |
| } |
| |
| accessor.forget = function(object) { |
| if (hasOwn.call(object, brand)) |
| object[brand](passkey, true); |
| }; |
| |
| return accessor; |
| } |
| |
| exports.makeAccessor = makeAccessor; |