You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
130 lines
3.4 KiB
JavaScript
130 lines
3.4 KiB
JavaScript
"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;
|