blob: 4f952fc487bf0c04daa690f8890e0970be0429ae [file] [log] [blame]
"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;