1
|
"use strict";
|
2
|
|
3
|
var originalObject = Object;
|
4
|
var originalDefProp = Object.defineProperty;
|
5
|
var originalCreate = Object.create;
|
6
|
|
7
|
function defProp(obj, name, value) {
|
8
|
if (originalDefProp) try {
|
9
|
originalDefProp.call(originalObject, obj, name, { value: value });
|
10
|
} catch (definePropertyIsBrokenInIE8) {
|
11
|
obj[name] = value;
|
12
|
} else {
|
13
|
obj[name] = value;
|
14
|
}
|
15
|
}
|
16
|
|
17
|
// For functions that will be invoked using .call or .apply, we need to
|
18
|
// define those methods on the function objects themselves, rather than
|
19
|
// inheriting them from Function.prototype, so that a malicious or clumsy
|
20
|
// third party cannot interfere with the functionality of this module by
|
21
|
// redefining Function.prototype.call or .apply.
|
22
|
function makeSafeToCall(fun) {
|
23
|
if (fun) {
|
24
|
defProp(fun, "call", fun.call);
|
25
|
defProp(fun, "apply", fun.apply);
|
26
|
}
|
27
|
return fun;
|
28
|
}
|
29
|
|
30
|
makeSafeToCall(originalDefProp);
|
31
|
makeSafeToCall(originalCreate);
|
32
|
|
33
|
var hasOwn = makeSafeToCall(Object.prototype.hasOwnProperty);
|
34
|
var numToStr = makeSafeToCall(Number.prototype.toString);
|
35
|
var strSlice = makeSafeToCall(String.prototype.slice);
|
36
|
|
37
|
var cloner = function(){};
|
38
|
function create(prototype) {
|
39
|
if (originalCreate) {
|
40
|
return originalCreate.call(originalObject, prototype);
|
41
|
}
|
42
|
cloner.prototype = prototype || null;
|
43
|
return new cloner;
|
44
|
}
|
45
|
|
46
|
var rand = Math.random;
|
47
|
var uniqueKeys = create(null);
|
48
|
|
49
|
function makeUniqueKey() {
|
50
|
// Collisions are highly unlikely, but this module is in the business of
|
51
|
// making guarantees rather than safe bets.
|
52
|
do var uniqueKey = internString(strSlice.call(numToStr.call(rand(), 36), 2));
|
53
|
while (hasOwn.call(uniqueKeys, uniqueKey));
|
54
|
return uniqueKeys[uniqueKey] = uniqueKey;
|
55
|
}
|
56
|
|
57
|
function internString(str) {
|
58
|
var obj = {};
|
59
|
obj[str] = true;
|
60
|
return Object.keys(obj)[0];
|
61
|
}
|
62
|
|
63
|
// External users might find this function useful, but it is not necessary
|
64
|
// for the typical use of this module.
|
65
|
exports.makeUniqueKey = makeUniqueKey;
|
66
|
|
67
|
// Object.getOwnPropertyNames is the only way to enumerate non-enumerable
|
68
|
// properties, so if we wrap it to ignore our secret keys, there should be
|
69
|
// no way (except guessing) to access those properties.
|
70
|
var originalGetOPNs = Object.getOwnPropertyNames;
|
71
|
Object.getOwnPropertyNames = function getOwnPropertyNames(object) {
|
72
|
for (var names = originalGetOPNs(object),
|
73
|
src = 0,
|
74
|
dst = 0,
|
75
|
len = names.length;
|
76
|
src < len;
|
77
|
++src) {
|
78
|
if (!hasOwn.call(uniqueKeys, names[src])) {
|
79
|
if (src > dst) {
|
80
|
names[dst] = names[src];
|
81
|
}
|
82
|
++dst;
|
83
|
}
|
84
|
}
|
85
|
names.length = dst;
|
86
|
return names;
|
87
|
};
|
88
|
|
89
|
function defaultCreatorFn(object) {
|
90
|
return create(null);
|
91
|
}
|
92
|
|
93
|
function makeAccessor(secretCreatorFn) {
|
94
|
var brand = makeUniqueKey();
|
95
|
var passkey = create(null);
|
96
|
|
97
|
secretCreatorFn = secretCreatorFn || defaultCreatorFn;
|
98
|
|
99
|
function register(object) {
|
100
|
var secret; // Created lazily.
|
101
|
|
102
|
function vault(key, forget) {
|
103
|
// Only code that has access to the passkey can retrieve (or forget)
|
104
|
// the secret object.
|
105
|
if (key === passkey) {
|
106
|
return forget
|
107
|
? secret = null
|
108
|
: secret || (secret = secretCreatorFn(object));
|
109
|
}
|
110
|
}
|
111
|
|
112
|
defProp(object, brand, vault);
|
113
|
}
|
114
|
|
115
|
function accessor(object) {
|
116
|
if (!hasOwn.call(object, brand))
|
117
|
register(object);
|
118
|
return object[brand](passkey);
|
119
|
}
|
120
|
|
121
|
accessor.forget = function(object) {
|
122
|
if (hasOwn.call(object, brand))
|
123
|
object[brand](passkey, true);
|
124
|
};
|
125
|
|
126
|
return accessor;
|
127
|
}
|
128
|
|
129
|
exports.makeAccessor = makeAccessor;
|