cfprotected 1.1.1 → 1.1.2
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 +3 -0
- package/index.mjs +46 -42
- package/package.json +1 -1
- package/test/test.mjs +18 -3
package/README.md
CHANGED
|
@@ -119,6 +119,9 @@ const Example = final(class {
|
|
|
119
119
|
});
|
|
120
120
|
```
|
|
121
121
|
|
|
122
|
+
### Notes:
|
|
123
|
+
In order to ensure the functionality of final would not interfere with the ability to access static members, final is implemented using a Proxy. It is therefore very important to use `saveSelf(...)` and the resulting property (or a similar approach) to ensure that access to static private properties is not interrupted.
|
|
124
|
+
|
|
122
125
|
## Other features
|
|
123
126
|
There will be occasions when a shared function that shadows an ancestor function needs to call the ancestor's function. Unfortunately, `super` cannot give you access to these. There is a similar problem when accessing accessors and data properties. To satisfy this need, the class-specific accessor option is given an additional property: `$uper`. Using this property, it is possible to reach the ancestor version of any shared member.
|
|
124
127
|
|
package/index.mjs
CHANGED
|
@@ -62,7 +62,8 @@ function share(inst, klass, members) {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
//Get the protected data object.
|
|
65
|
-
let
|
|
65
|
+
let ancestorKey = (inst === klass) ? ancestor : inst;
|
|
66
|
+
let memo = ancestorMemo.get(ancestorKey) || {data: {}, $uper: {}, inheritance: null};
|
|
66
67
|
let protData = memo.data;
|
|
67
68
|
|
|
68
69
|
//Get the details of the protected properties.
|
|
@@ -169,9 +170,11 @@ function abstract(klass) {
|
|
|
169
170
|
};
|
|
170
171
|
|
|
171
172
|
if (memos.has(klass)) {
|
|
172
|
-
|
|
173
|
+
let memo = memos.get(klass);
|
|
174
|
+
memos.set(retval, memo);
|
|
175
|
+
memo.set(retval, memo.get(klass));
|
|
173
176
|
}
|
|
174
|
-
|
|
177
|
+
|
|
175
178
|
return retval;
|
|
176
179
|
};
|
|
177
180
|
|
|
@@ -182,51 +185,52 @@ function abstract(klass) {
|
|
|
182
185
|
* @param {Function} klass The constructor of the current class.
|
|
183
186
|
*/
|
|
184
187
|
function final(klass) {
|
|
185
|
-
let
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
retval.prototype = void 0;
|
|
195
|
-
}
|
|
196
|
-
let inst = new klass(...args);
|
|
197
|
-
let proto = Object.create(klass.prototype, {
|
|
198
|
-
constructor: {
|
|
199
|
-
enumerable: true,
|
|
200
|
-
configurable: true,
|
|
201
|
-
writable: true,
|
|
202
|
-
value: retval
|
|
188
|
+
let retval = new Proxy(function() {}, {
|
|
189
|
+
handleDefault(fname, args) {
|
|
190
|
+
args.shift();
|
|
191
|
+
args.unshift(klass);
|
|
192
|
+
return Reflect[fname](...args);
|
|
193
|
+
},
|
|
194
|
+
construct(_, args, newTarget) {
|
|
195
|
+
if (newTarget !== retval) {
|
|
196
|
+
throw new TypeError("Cannot create an instance of a descendant of a final class");
|
|
203
197
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
value() { return klass.toString(); }
|
|
198
|
+
let inst = Reflect.construct(klass, args, newTarget);
|
|
199
|
+
let proto = Object.create(klass.prototype, {
|
|
200
|
+
constructor: {
|
|
201
|
+
enumerable: true,
|
|
202
|
+
configurable: true,
|
|
203
|
+
writable: true,
|
|
204
|
+
value: retval
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
Object.setPrototypeOf(inst, proto);
|
|
208
|
+
return inst;
|
|
216
209
|
},
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
210
|
+
get(_, prop, receiver) {
|
|
211
|
+
return (prop == "prototype")
|
|
212
|
+
? void 0
|
|
213
|
+
: Reflect.get(klass, prop, receiver);
|
|
220
214
|
},
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
}
|
|
224
|
-
|
|
215
|
+
set(...args) { return this.handleDefault("set", args); },
|
|
216
|
+
apply(...args) { return this.handleDefault("apply", args); },
|
|
217
|
+
defineProperty(...args) { return this.handleDefault("defineProperty", args); },
|
|
218
|
+
deleteProperty(...args) { return this.handleDefault("deleteProperty", args); },
|
|
219
|
+
getOwnPropertyDescriptor(...args) { return this.handleDefault("getOwnPropertyDescriptor", args); },
|
|
220
|
+
getPrototypeOf(...args) { return this.handleDefault("getPrototypeOf", args); },
|
|
221
|
+
has(...args) { return this.handleDefault("has", args); },
|
|
222
|
+
isExtensible(...args) { return this.handleDefault("isExtensible", args); },
|
|
223
|
+
ownKeys(...args) { return this.handleDefault("ownKeys", args); },
|
|
224
|
+
preventExtensions(...args) { return this.handleDefault("preventExtensions", args); },
|
|
225
|
+
setPrototypeOf(...args) { return this.handleDefault("setPrototypeOf", args); }
|
|
226
|
+
});
|
|
225
227
|
|
|
226
228
|
if (memos.has(klass)) {
|
|
227
|
-
|
|
229
|
+
let memo = memos.get(klass);
|
|
230
|
+
memos.set(retval, memo);
|
|
231
|
+
memo.set(retval, memo.get(klass));
|
|
228
232
|
}
|
|
229
|
-
|
|
233
|
+
|
|
230
234
|
return retval;
|
|
231
235
|
};
|
|
232
236
|
|
package/package.json
CHANGED
package/test/test.mjs
CHANGED
|
@@ -140,13 +140,18 @@ describe(`Testing that $uper works in all cases`, () => {
|
|
|
140
140
|
describe(`Testing that abstract classes function as expected`, () => {
|
|
141
141
|
const key = Symbol();
|
|
142
142
|
const ATest = abstract(class ATest {
|
|
143
|
+
static #sshared = share(this, {
|
|
144
|
+
[key]: true
|
|
145
|
+
});
|
|
143
146
|
#shared = share(this, ATest, {
|
|
144
147
|
[key]: true
|
|
145
148
|
});
|
|
146
149
|
});
|
|
147
150
|
class DTest extends ATest {
|
|
151
|
+
static #sshared = share(this, {});
|
|
148
152
|
#shared = share(this, DTest, {});
|
|
149
153
|
run() { return this.#shared[key]; }
|
|
154
|
+
static run() { return this.#sshared[key]; }
|
|
150
155
|
};
|
|
151
156
|
|
|
152
157
|
test(`Should not be able to instantiate directly`, () => {
|
|
@@ -158,20 +163,27 @@ describe(`Testing that abstract classes function as expected`, () => {
|
|
|
158
163
|
test(`Should see shared members from constructed instance`, () => {
|
|
159
164
|
expect((new DTest).run()).toBe(true);
|
|
160
165
|
});
|
|
166
|
+
test(`Should see static shared members from constructed instance`, () => {
|
|
167
|
+
expect(DTest.run()).toBe(true);
|
|
168
|
+
});
|
|
161
169
|
});
|
|
162
170
|
|
|
163
171
|
describe(`Testing that final classes function as expected`, () => {
|
|
164
172
|
const key = Symbol();
|
|
165
173
|
class TestBase {
|
|
174
|
+
static #sshared = share(this, {
|
|
175
|
+
[key]: true
|
|
176
|
+
});
|
|
166
177
|
#shared = share(this, TestBase, {
|
|
167
178
|
[key]: true
|
|
168
179
|
});
|
|
169
180
|
}
|
|
170
181
|
const FTest = final(class FTest extends TestBase {
|
|
182
|
+
static { saveSelf(this, "pvt"); }
|
|
183
|
+
static #sshared = share(this, {});
|
|
171
184
|
#shared = share(this, FTest, {});
|
|
172
|
-
run() {
|
|
173
|
-
|
|
174
|
-
}
|
|
185
|
+
run() { return this.#shared[key]; }
|
|
186
|
+
static run() { return this.pvt.#sshared[key]; }
|
|
175
187
|
});
|
|
176
188
|
|
|
177
189
|
test(`Should be able to instantiate an instance directly`, () => {
|
|
@@ -190,4 +202,7 @@ describe(`Testing that final classes function as expected`, () => {
|
|
|
190
202
|
test(`Should see shared members from constructed instance`, () => {
|
|
191
203
|
expect((new FTest).run()).toBe(true);
|
|
192
204
|
});
|
|
205
|
+
test(`Should see static shared members from constructed instance`, () => {
|
|
206
|
+
expect(FTest.run()).toBe(true);
|
|
207
|
+
});
|
|
193
208
|
});
|