cfprotected 1.1.3 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -1
- package/index.mjs +54 -30
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -104,12 +104,22 @@ class Example {
|
|
|
104
104
|
As a bonus, when used on the constructor, it creates an additional property named "cla\$\$" on the prototype. This "cla\$\$" property gives instances a means to reference the class constructor even when the class itself is anonymous. This is a fill-in feature for one of the TC39 proposals offering syntax for the same.
|
|
105
105
|
|
|
106
106
|
## **abstract(klass)**
|
|
107
|
-
This method is a class wrapper that prevents instances of the class from being constructed directly. To construct an instance of the class you must extend it. This should be nearly identical to the same functionality that exists in some compiled languages.
|
|
107
|
+
This method has 2 different uses. The first is as a class wrapper that prevents instances of the class from being constructed directly. To construct an instance of the class, you must extend it. This should be nearly identical to the same functionality that exists in some compiled languages.
|
|
108
108
|
```js
|
|
109
109
|
const Example = abstract(class {
|
|
110
110
|
...
|
|
111
111
|
});
|
|
112
112
|
```
|
|
113
|
+
The second use is to mark the function as not having a valid implementation. Such functions must be overridden by derived classes.
|
|
114
|
+
```js
|
|
115
|
+
class Example {
|
|
116
|
+
...
|
|
117
|
+
#prot = share(this, Example, {
|
|
118
|
+
method = abstract("Example::method")
|
|
119
|
+
});
|
|
120
|
+
...
|
|
121
|
+
};
|
|
122
|
+
```
|
|
113
123
|
|
|
114
124
|
## **final(klass)**
|
|
115
125
|
This method is a class wrapper that prevents instances of the class from being created using descendant classes. It also attempts to prevent creation of descendant classes.
|
package/index.mjs
CHANGED
|
@@ -39,6 +39,8 @@ function bindDescriptor(desc, context) {
|
|
|
39
39
|
* @returns {Object} The fully constructed inheritance object.
|
|
40
40
|
*/
|
|
41
41
|
function share(inst, klass, members) {
|
|
42
|
+
let retval = {};
|
|
43
|
+
|
|
42
44
|
if ((typeof(inst) == "function")
|
|
43
45
|
&& klass && (typeof(klass) == "object")
|
|
44
46
|
&& (members === void 0)) {
|
|
@@ -67,7 +69,7 @@ function share(inst, klass, members) {
|
|
|
67
69
|
* }
|
|
68
70
|
*/
|
|
69
71
|
|
|
70
|
-
//Find the nearest known registered ancestor
|
|
72
|
+
//Find the nearest known registered ancestor class
|
|
71
73
|
let ancestor = Object.getPrototypeOf(klass);
|
|
72
74
|
while (ancestor && !memos.has(ancestor)) {
|
|
73
75
|
ancestor = Object.getPrototypeOf(ancestor);
|
|
@@ -76,25 +78,22 @@ function share(inst, klass, members) {
|
|
|
76
78
|
//Get the memo from that ancestor
|
|
77
79
|
let ancestorMemo = memos.get(ancestor) || new WeakMap();
|
|
78
80
|
|
|
79
|
-
//Create a memo for the current class
|
|
81
|
+
//Create a memo map for the current class
|
|
80
82
|
if (!memos.has(klass)) {
|
|
81
83
|
memos.set(klass, new WeakMap());
|
|
82
84
|
}
|
|
83
85
|
|
|
84
86
|
//Get the protected data object.
|
|
85
87
|
let ancestorKey = (inst === klass) ? ancestor : inst;
|
|
86
|
-
let memo = ancestorMemo.get(ancestorKey) || {
|
|
87
|
-
|
|
88
|
-
$uper: {},
|
|
89
|
-
};
|
|
90
|
-
let retval = {};
|
|
88
|
+
let memo = ancestorMemo.get(ancestorKey) || {data: {}, $uper: {}, inheritance: null};
|
|
89
|
+
let protData = memo.data;
|
|
91
90
|
|
|
92
91
|
//Get the details of the protected properties.
|
|
93
92
|
let mDesc = Object.getOwnPropertyDescriptors(members);
|
|
94
93
|
let mKeys = getAllOwnKeys(members);
|
|
95
94
|
|
|
96
|
-
//
|
|
97
|
-
let prototype = Object.getPrototypeOf(
|
|
95
|
+
//Add the new members to the prototype chain of protData.
|
|
96
|
+
let prototype = Object.getPrototypeOf(protData);
|
|
98
97
|
let proto = Object.create(prototype,
|
|
99
98
|
Object.fromEntries(mKeys
|
|
100
99
|
.map(k => {
|
|
@@ -108,11 +107,18 @@ function share(inst, klass, members) {
|
|
|
108
107
|
bindDescriptor(mDesc[k], inst);
|
|
109
108
|
return [k, mDesc[k]];
|
|
110
109
|
})));
|
|
111
|
-
Object.setPrototypeOf(
|
|
110
|
+
Object.setPrototypeOf(protData, proto);
|
|
112
111
|
|
|
112
|
+
//Build the accessors for this class.
|
|
113
|
+
mKeys.forEach(m => {
|
|
114
|
+
Object.defineProperty(retval, m, {
|
|
115
|
+
get() { return protData[m]; },
|
|
116
|
+
set(v) { protData[m] = v; }
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
113
120
|
//Define the "$uper" accessors
|
|
114
|
-
|
|
115
|
-
Object.defineProperty(retval, "$uper", { value: $uper });
|
|
121
|
+
Object.defineProperty(retval, "$uper", { value: {} });
|
|
116
122
|
|
|
117
123
|
//Build up the "$uper" object
|
|
118
124
|
for (let key of mKeys) {
|
|
@@ -121,23 +127,27 @@ function share(inst, klass, members) {
|
|
|
121
127
|
while (!obj.hasOwnProperty(key)) {
|
|
122
128
|
obj = Object.getPrototypeOf(obj);
|
|
123
129
|
}
|
|
124
|
-
Object.defineProperty(
|
|
130
|
+
Object.defineProperty(retval.$uper, key, Object.getOwnPropertyDescriptor(obj, key));
|
|
125
131
|
}
|
|
126
132
|
}
|
|
127
133
|
|
|
128
134
|
//Attach the super inheritance
|
|
129
|
-
Object.setPrototypeOf(
|
|
135
|
+
Object.setPrototypeOf(retval.$uper, memo.$uper);
|
|
136
|
+
|
|
137
|
+
//Inherit the inheritance
|
|
138
|
+
Object.setPrototypeOf(retval, memo.inheritance);
|
|
130
139
|
|
|
131
140
|
//Save the inheritance & protected data
|
|
132
141
|
memos.get(klass).set(inst, {
|
|
133
|
-
data:
|
|
134
|
-
|
|
142
|
+
data: protData,
|
|
143
|
+
inheritance: retval,
|
|
144
|
+
$uper: retval.$uper
|
|
135
145
|
});
|
|
136
146
|
|
|
137
147
|
return retval;
|
|
138
148
|
}
|
|
139
149
|
|
|
140
|
-
/**
|
|
150
|
+
/**
|
|
141
151
|
* Binds the class instance to itself to allow code to selectively avoid Proxy
|
|
142
152
|
* issues, especially ones involving private fields. Also binds the class
|
|
143
153
|
* constructor to the instance for static referencing as "cla$$".
|
|
@@ -173,23 +183,37 @@ function accessor(desc) {
|
|
|
173
183
|
/**
|
|
174
184
|
* A class wrapper that blocks construction of an instance if the class being
|
|
175
185
|
* instantiated is not a descendant of the current class.
|
|
176
|
-
* @param {
|
|
186
|
+
* @param {function|string} klass If a function, the constructor of the current
|
|
187
|
+
* class. If a string, the name of the function being abstracted.
|
|
188
|
+
* @returns {function} Either an extended class that denies direct construction
|
|
189
|
+
* or a function that immediately throws.
|
|
177
190
|
*/
|
|
178
191
|
function abstract(klass) {
|
|
179
|
-
let
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
192
|
+
let retval;
|
|
193
|
+
if (typeof(klass) == "function") {
|
|
194
|
+
let name = klass.name?klass.name : "";
|
|
195
|
+
retval = class extends klass {
|
|
196
|
+
constructor (...args) {
|
|
197
|
+
if (new.target === retval) {
|
|
198
|
+
throw new TypeError(`Class constructor ${name} is abstract and cannot be directly invoked with 'new'`);
|
|
199
|
+
}
|
|
200
|
+
super(...args);
|
|
184
201
|
}
|
|
185
|
-
|
|
186
|
-
}
|
|
187
|
-
};
|
|
202
|
+
};
|
|
188
203
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
204
|
+
if (memos.has(klass)) {
|
|
205
|
+
let memo = memos.get(klass);
|
|
206
|
+
memos.set(retval, memo);
|
|
207
|
+
memo.set(retval, memo.get(klass));
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
else if (typeof(klass) == "string") {
|
|
211
|
+
retval = function() {
|
|
212
|
+
throw new TypeError(`${klass}() must be overridden`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
throw new TypeError(`abstract parameter must be a function or string`)
|
|
193
217
|
}
|
|
194
218
|
|
|
195
219
|
return retval;
|