1 | ;
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | exports.Slot = void 0;
|
4 | // This currentContext variable will only be used if the makeSlotClass
|
5 | // function is called, which happens only if this is the first copy of the
|
6 | // @wry/context package to be imported.
|
7 | var currentContext = null;
|
8 | // This unique internal object is used to denote the absence of a value
|
9 | // for a given Slot, and is never exposed to outside code.
|
10 | var MISSING_VALUE = {};
|
11 | var idCounter = 1;
|
12 | // Although we can't do anything about the cost of duplicated code from
|
13 | // accidentally bundling multiple copies of the @wry/context package, we can
|
14 | // avoid creating the Slot class more than once using makeSlotClass.
|
15 | var makeSlotClass = function () { return /** @class */ (function () {
|
16 | function Slot() {
|
17 | // If you have a Slot object, you can find out its slot.id, but you cannot
|
18 | // guess the slot.id of a Slot you don't have access to, thanks to the
|
19 | // randomized suffix.
|
20 | this.id = [
|
21 | "slot",
|
22 | idCounter++,
|
23 | Date.now(),
|
24 | Math.random().toString(36).slice(2),
|
25 | ].join(":");
|
26 | }
|
27 | Slot.prototype.hasValue = function () {
|
28 | for (var context_1 = currentContext; context_1; context_1 = context_1.parent) {
|
29 | // We use the Slot object iself as a key to its value, which means the
|
30 | // value cannot be obtained without a reference to the Slot object.
|
31 | if (this.id in context_1.slots) {
|
32 | var value = context_1.slots[this.id];
|
33 | if (value === MISSING_VALUE)
|
34 | break;
|
35 | if (context_1 !== currentContext) {
|
36 | // Cache the value in currentContext.slots so the next lookup will
|
37 | // be faster. This caching is safe because the tree of contexts and
|
38 | // the values of the slots are logically immutable.
|
39 | currentContext.slots[this.id] = value;
|
40 | }
|
41 | return true;
|
42 | }
|
43 | }
|
44 | if (currentContext) {
|
45 | // If a value was not found for this Slot, it's never going to be found
|
46 | // no matter how many times we look it up, so we might as well cache
|
47 | // the absence of the value, too.
|
48 | currentContext.slots[this.id] = MISSING_VALUE;
|
49 | }
|
50 | return false;
|
51 | };
|
52 | Slot.prototype.getValue = function () {
|
53 | if (this.hasValue()) {
|
54 | return currentContext.slots[this.id];
|
55 | }
|
56 | };
|
57 | Slot.prototype.withValue = function (value, callback,
|
58 | // Given the prevalence of arrow functions, specifying arguments is likely
|
59 | // to be much more common than specifying `this`, hence this ordering:
|
60 | args, thisArg) {
|
61 | var _a;
|
62 | var slots = (_a = {
|
63 | __proto__: null
|
64 | },
|
65 | _a[this.id] = value,
|
66 | _a);
|
67 | var parent = currentContext;
|
68 | currentContext = { parent: parent, slots: slots };
|
69 | try {
|
70 | // Function.prototype.apply allows the arguments array argument to be
|
71 | // omitted or undefined, so args! is fine here.
|
72 | return callback.apply(thisArg, args);
|
73 | }
|
74 | finally {
|
75 | currentContext = parent;
|
76 | }
|
77 | };
|
78 | // Capture the current context and wrap a callback function so that it
|
79 | // reestablishes the captured context when called.
|
80 | Slot.bind = function (callback) {
|
81 | var context = currentContext;
|
82 | return function () {
|
83 | var saved = currentContext;
|
84 | try {
|
85 | currentContext = context;
|
86 | return callback.apply(this, arguments);
|
87 | }
|
88 | finally {
|
89 | currentContext = saved;
|
90 | }
|
91 | };
|
92 | };
|
93 | // Immediately run a callback function without any captured context.
|
94 | Slot.noContext = function (callback,
|
95 | // Given the prevalence of arrow functions, specifying arguments is likely
|
96 | // to be much more common than specifying `this`, hence this ordering:
|
97 | args, thisArg) {
|
98 | if (currentContext) {
|
99 | var saved = currentContext;
|
100 | try {
|
101 | currentContext = null;
|
102 | // Function.prototype.apply allows the arguments array argument to be
|
103 | // omitted or undefined, so args! is fine here.
|
104 | return callback.apply(thisArg, args);
|
105 | }
|
106 | finally {
|
107 | currentContext = saved;
|
108 | }
|
109 | }
|
110 | else {
|
111 | return callback.apply(thisArg, args);
|
112 | }
|
113 | };
|
114 | return Slot;
|
115 | }()); };
|
116 | function maybe(fn) {
|
117 | try {
|
118 | return fn();
|
119 | }
|
120 | catch (ignored) { }
|
121 | }
|
122 | // We store a single global implementation of the Slot class as a permanent
|
123 | // non-enumerable property of the globalThis object. This obfuscation does
|
124 | // nothing to prevent access to the Slot class, but at least it ensures the
|
125 | // implementation (i.e. currentContext) cannot be tampered with, and all copies
|
126 | // of the @wry/context package (hopefully just one) will share the same Slot
|
127 | // implementation. Since the first copy of the @wry/context package to be
|
128 | // imported wins, this technique imposes a steep cost for any future breaking
|
129 | // changes to the Slot class.
|
130 | var globalKey = "@wry/context:Slot";
|
131 | var host =
|
132 | // Prefer globalThis when available.
|
133 | // https://github.com/benjamn/wryware/issues/347
|
134 | maybe(function () { return globalThis; }) ||
|
135 | // Fall back to global, which works in Node.js and may be converted by some
|
136 | // bundlers to the appropriate identifier (window, self, ...) depending on the
|
137 | // bundling target. https://github.com/endojs/endo/issues/576#issuecomment-1178515224
|
138 | maybe(function () { return global; }) ||
|
139 | // Otherwise, use a dummy host that's local to this module. We used to fall
|
140 | // back to using the Array constructor as a namespace, but that was flagged in
|
141 | // https://github.com/benjamn/wryware/issues/347, and can be avoided.
|
142 | Object.create(null);
|
143 | // Whichever globalHost we're using, make TypeScript happy about the additional
|
144 | // globalKey property.
|
145 | var globalHost = host;
|
146 | exports.Slot = globalHost[globalKey] ||
|
147 | // Earlier versions of this package stored the globalKey property on the Array
|
148 | // constructor, so we check there as well, to prevent Slot class duplication.
|
149 | Array[globalKey] ||
|
150 | (function (Slot) {
|
151 | try {
|
152 | Object.defineProperty(globalHost, globalKey, {
|
153 | value: Slot,
|
154 | enumerable: false,
|
155 | writable: false,
|
156 | // When it was possible for globalHost to be the Array constructor (a
|
157 | // legacy Slot dedup strategy), it was important for the property to be
|
158 | // configurable:true so it could be deleted. That does not seem to be as
|
159 | // important when globalHost is the global object, but I don't want to
|
160 | // cause similar problems again, and configurable:true seems safest.
|
161 | // https://github.com/endojs/endo/issues/576#issuecomment-1178274008
|
162 | configurable: true
|
163 | });
|
164 | }
|
165 | finally {
|
166 | return Slot;
|
167 | }
|
168 | })(makeSlotClass());
|
169 | //# sourceMappingURL=slot.js.map |
\ | No newline at end of file |