-
Notifications
You must be signed in to change notification settings - Fork 22
/
privatize.js
156 lines (138 loc) · 4.97 KB
/
privatize.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/**
* ensure private property/method stays private
*
* @namespace Strong typing for javascript
*/
var Privatize = {};
// include dependancies
var Stacktrace = Stacktrace || require('./stacktrace.js');
var QGetterSetter = QGetterSetter|| require('./qgettersetter.js')
// export the namespace in node.js - if running in node.js
if( typeof(window) === 'undefined' ) module.exports = Privatize;
//////////////////////////////////////////////////////////////////////////////////
// Handle PrivateOKFn //
//////////////////////////////////////////////////////////////////////////////////
/**
* determine which function is considered private for klass
*
* @param {function} klass the constructor of the class
* @param {function} privateFn private function to add
*/
Privatize.pushPrivateOkFn = function(instance, privateFn){
// init if needed
Privatize.init(instance);
// honor .__betterjsOriginalFn
var callerFn = privateFn.__betterjsOriginalFn || privateFn
// actually add the function
instance._privateOkFn.push(callerFn)
}
Privatize.init = function(instance){
// create the storage value if needed - with non enumerable
if( instance._privateOkFn === undefined ){
Object.defineProperty(instance, '_privateOkFn', {
enumerable : false,
writable : true,
value : [],
})
}
}
//////////////////////////////////////////////////////////////////////////////////
// core //
//////////////////////////////////////////////////////////////////////////////////
/**
* define a private property on a given instance of a object class
* @param {Object} instance the object instance
* @param {String} property the property name
* @return {undefined} nothing
*/
Privatize.property = function(instance, property){
// init if needed
Privatize.init(instance);
// check private in the getter
QGetterSetter.defineGetter(instance, property, function aFunction(value, caller, property){
// if caller not privateOK, notify the caller
if( instance._privateOkFn.indexOf(caller) === -1 ){
// get stackFrame for the originId of the user
var stackFrame = Stacktrace.parse()[2]
// log the event
console.assert(false, 'access to private property "'+property+'" from '+stackFrame)
}
// actually return the value
return value;
});
// check private in the setter
QGetterSetter.defineSetter(instance, property, function aFunction(value, caller, property){
// if caller not privateOK, notify the caller
if( instance._privateOkFn.indexOf(caller) === -1 ){
// get stackFrame for the originId of the user
var stackFrame = Stacktrace.parse()[2]
// log the event
console.assert(false, 'access to private property "'+property+'" from '+stackFrame)
}
// actually return the value
return value;
});
};
/**
* define a private function
* @param {Object} instance the object instance
* @param {Function} fn the function to overload
* @return {Function} the overloaded function
*/
Privatize.function = function(instance, fn){
var functionName= fn.name || 'anonymous'
return function _checkFunction(){
// get caller
var caller = _checkFunction.caller;
// if caller not privateOK, notify the caller
// console.log('check function', functionName)
if( instance._privateOkFn.indexOf(caller) === -1 ){
// get stackFrame for the originId of the user
var stackFrame = Stacktrace.parse()[1]
// log the event
console.assert(false, 'access to private function "'+functionName+'" from '+stackFrame)
}
// forward the call to the original function
return fn.apply(this, arguments);
};
};
//////////////////////////////////////////////////////////////////////////////////
// Helpers //
//////////////////////////////////////////////////////////////////////////////////
/**
* get all the functions of the instance, and declare them privateOK
*
* @param {Object} instance [description]
*/
Privatize.prepare = function(instance){
// init if needed
Privatize.init(instance);
// populate the ._privateOkFn with the .prototype function which start by '_'
for(var property in instance){
// TODO should i do a .hasOwnProperty on a .prototype ?
if( typeof(instance[property]) !== 'function') continue;
// console.log('PrivateOKFn', property)
Privatize.pushPrivateOkFn(instance, instance[property])
}
}
/**
* declare any property/functions starting with '_' as private
*
* @param {object} instance the instance of the object
*/
Privatize.privatize = function(instance, selectorRegexp){
selectorRegexp = selectorRegexp || /^_.*/
// init if needed
Privatize.init(instance);
// declare any property/functions starting with '_' as private
for(var property in instance){
if( property.match(selectorRegexp) === null ) continue;
if( typeof(instance[property]) === 'function' ){
// console.log('declare', property, 'as private function')
instance[property] = Privatize.function(instance, instance[property])
}else{
// console.log('declare', property, 'as private property')
Privatize.property(instance, property);
}
}
};