|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 "use strict"; |
|
6 |
|
7 module.metadata = { |
|
8 "stability": "deprecated" |
|
9 }; |
|
10 |
|
11 // `var` is being used in the module in order to make it reusable in |
|
12 // environments in which `let` is not yet supported. |
|
13 |
|
14 // Shortcut to `Object.prototype.hasOwnProperty.call`. |
|
15 // owns(object, name) would be the same as |
|
16 // Object.prototype.hasOwnProperty.call(object, name); |
|
17 var owns = Function.prototype.call.bind(Object.prototype.hasOwnProperty); |
|
18 |
|
19 /** |
|
20 * Whether or not given property descriptors are equivalent. They are |
|
21 * equivalent either if both are marked as 'conflict' or 'required' property |
|
22 * or if all the properties of descriptors are equal. |
|
23 * @param {Object} actual |
|
24 * @param {Object} expected |
|
25 */ |
|
26 function equivalentDescriptors(actual, expected) { |
|
27 return (actual.conflict && expected.conflict) || |
|
28 (actual.required && expected.required) || |
|
29 equalDescriptors(actual, expected); |
|
30 } |
|
31 /** |
|
32 * Whether or not given property descriptors define equal properties. |
|
33 */ |
|
34 function equalDescriptors(actual, expected) { |
|
35 return actual.get === expected.get && |
|
36 actual.set === expected.set && |
|
37 actual.value === expected.value && |
|
38 !!actual.enumerable === !!expected.enumerable && |
|
39 !!actual.configurable === !!expected.configurable && |
|
40 !!actual.writable === !!expected.writable; |
|
41 } |
|
42 |
|
43 // Utilities that throwing exceptions for a properties that are marked |
|
44 // as "required" or "conflict" properties. |
|
45 function throwConflictPropertyError(name) { |
|
46 throw new Error("Remaining conflicting property: `" + name + "`"); |
|
47 } |
|
48 function throwRequiredPropertyError(name) { |
|
49 throw new Error("Missing required property: `" + name + "`"); |
|
50 } |
|
51 |
|
52 /** |
|
53 * Generates custom **required** property descriptor. Descriptor contains |
|
54 * non-standard property `required` that is equal to `true`. |
|
55 * @param {String} name |
|
56 * property name to generate descriptor for. |
|
57 * @returns {Object} |
|
58 * custom property descriptor |
|
59 */ |
|
60 function RequiredPropertyDescriptor(name) { |
|
61 // Creating function by binding first argument to a property `name` on the |
|
62 // `throwConflictPropertyError` function. Created function is used as a |
|
63 // getter & setter of the created property descriptor. This way we ensure |
|
64 // that we throw exception late (on property access) if object with |
|
65 // `required` property was instantiated using built-in `Object.create`. |
|
66 var accessor = throwRequiredPropertyError.bind(null, name); |
|
67 return { get: accessor, set: accessor, required: true }; |
|
68 } |
|
69 |
|
70 /** |
|
71 * Generates custom **conflicting** property descriptor. Descriptor contains |
|
72 * non-standard property `conflict` that is equal to `true`. |
|
73 * @param {String} name |
|
74 * property name to generate descriptor for. |
|
75 * @returns {Object} |
|
76 * custom property descriptor |
|
77 */ |
|
78 function ConflictPropertyDescriptor(name) { |
|
79 // For details see `RequiredPropertyDescriptor` since idea is same. |
|
80 var accessor = throwConflictPropertyError.bind(null, name); |
|
81 return { get: accessor, set: accessor, conflict: true }; |
|
82 } |
|
83 |
|
84 /** |
|
85 * Tests if property is marked as `required` property. |
|
86 */ |
|
87 function isRequiredProperty(object, name) { |
|
88 return !!object[name].required; |
|
89 } |
|
90 |
|
91 /** |
|
92 * Tests if property is marked as `conflict` property. |
|
93 */ |
|
94 function isConflictProperty(object, name) { |
|
95 return !!object[name].conflict; |
|
96 } |
|
97 |
|
98 /** |
|
99 * Function tests whether or not method of the `source` object with a given |
|
100 * `name` is inherited from `Object.prototype`. |
|
101 */ |
|
102 function isBuiltInMethod(name, source) { |
|
103 var target = Object.prototype[name]; |
|
104 |
|
105 // If methods are equal then we know it's `true`. |
|
106 return target == source || |
|
107 // If `source` object comes form a different sandbox `==` will evaluate |
|
108 // to `false`, in that case we check if functions names and sources match. |
|
109 (String(target) === String(source) && target.name === source.name); |
|
110 } |
|
111 |
|
112 /** |
|
113 * Function overrides `toString` and `constructor` methods of a given `target` |
|
114 * object with a same-named methods of a given `source` if methods of `target` |
|
115 * object are inherited / copied from `Object.prototype`. |
|
116 * @see create |
|
117 */ |
|
118 function overrideBuiltInMethods(target, source) { |
|
119 if (isBuiltInMethod("toString", target.toString)) { |
|
120 Object.defineProperty(target, "toString", { |
|
121 value: source.toString, |
|
122 configurable: true, |
|
123 enumerable: false |
|
124 }); |
|
125 } |
|
126 |
|
127 if (isBuiltInMethod("constructor", target.constructor)) { |
|
128 Object.defineProperty(target, "constructor", { |
|
129 value: source.constructor, |
|
130 configurable: true, |
|
131 enumerable: false |
|
132 }); |
|
133 } |
|
134 } |
|
135 |
|
136 /** |
|
137 * Composes new trait with the same own properties as the original trait, |
|
138 * except that all property names appearing in the first argument are replaced |
|
139 * by "required" property descriptors. |
|
140 * @param {String[]} keys |
|
141 * Array of strings property names. |
|
142 * @param {Object} trait |
|
143 * A trait some properties of which should be excluded. |
|
144 * @returns {Object} |
|
145 * @example |
|
146 * var newTrait = exclude(["name", ...], trait) |
|
147 */ |
|
148 function exclude(names, trait) { |
|
149 var map = {}; |
|
150 |
|
151 Object.keys(trait).forEach(function(name) { |
|
152 |
|
153 // If property is not excluded (the array of names does not contain it), |
|
154 // or it is a "required" property, copy it to the property descriptor `map` |
|
155 // that will be used for creation of resulting trait. |
|
156 if (!~names.indexOf(name) || isRequiredProperty(trait, name)) |
|
157 map[name] = { value: trait[name], enumerable: true }; |
|
158 |
|
159 // For all the `names` in the exclude name array we create required |
|
160 // property descriptors and copy them to the `map`. |
|
161 else |
|
162 map[name] = { value: RequiredPropertyDescriptor(name), enumerable: true }; |
|
163 }); |
|
164 |
|
165 return Object.create(Trait.prototype, map); |
|
166 } |
|
167 |
|
168 /** |
|
169 * Composes new instance of `Trait` with a properties of a given `trait`, |
|
170 * except that all properties whose name is an own property of `renames` will |
|
171 * be renamed to `renames[name]` and a `"required"` property for name will be |
|
172 * added instead. |
|
173 * |
|
174 * For each renamed property, a required property is generated. If |
|
175 * the `renames` map two properties to the same name, a conflict is generated. |
|
176 * If the `renames` map a property to an existing unrenamed property, a |
|
177 * conflict is generated. |
|
178 * |
|
179 * @param {Object} renames |
|
180 * An object whose own properties serve as a mapping from old names to new |
|
181 * names. |
|
182 * @param {Object} trait |
|
183 * A new trait with renamed properties. |
|
184 * @returns {Object} |
|
185 * @example |
|
186 * |
|
187 * // Return trait with `bar` property equal to `trait.foo` and with |
|
188 * // `foo` and `baz` "required" properties. |
|
189 * var renamedTrait = rename({ foo: "bar", baz: null }), trait); |
|
190 * |
|
191 * // t1 and t2 are equivalent traits |
|
192 * var t1 = rename({a: "b"}, t); |
|
193 * var t2 = compose(exclude(["a"], t), { a: { required: true }, b: t[a] }); |
|
194 */ |
|
195 function rename(renames, trait) { |
|
196 var map = {}; |
|
197 |
|
198 // Loop over all the properties of the given `trait` and copy them to a |
|
199 // property descriptor `map` that will be used for the creation of the |
|
200 // resulting trait. Also, rename properties in the `map` as specified by |
|
201 // `renames`. |
|
202 Object.keys(trait).forEach(function(name) { |
|
203 var alias; |
|
204 |
|
205 // If the property is in the `renames` map, and it isn't a "required" |
|
206 // property (which should never need to be aliased because "required" |
|
207 // properties never conflict), then we must try to rename it. |
|
208 if (owns(renames, name) && !isRequiredProperty(trait, name)) { |
|
209 alias = renames[name]; |
|
210 |
|
211 // If the `map` already has the `alias`, and it isn't a "required" |
|
212 // property, that means the `alias` conflicts with an existing name for a |
|
213 // provided trait (that can happen if >=2 properties are aliased to the |
|
214 // same name). In this case we mark it as a conflicting property. |
|
215 // Otherwise, everything is fine, and we copy property with an `alias` |
|
216 // name. |
|
217 if (owns(map, alias) && !map[alias].value.required) { |
|
218 map[alias] = { |
|
219 value: ConflictPropertyDescriptor(alias), |
|
220 enumerable: true |
|
221 }; |
|
222 } |
|
223 else { |
|
224 map[alias] = { |
|
225 value: trait[name], |
|
226 enumerable: true |
|
227 }; |
|
228 } |
|
229 |
|
230 // Regardless of whether or not the rename was successful, we check to |
|
231 // see if the original `name` exists in the map (such a property |
|
232 // could exist if previous another property was aliased to this `name`). |
|
233 // If it isn't, we mark it as "required", to make sure the caller |
|
234 // provides another value for the old name, which methods of the trait |
|
235 // might continue to reference. |
|
236 if (!owns(map, name)) { |
|
237 map[name] = { |
|
238 value: RequiredPropertyDescriptor(name), |
|
239 enumerable: true |
|
240 }; |
|
241 } |
|
242 } |
|
243 |
|
244 // Otherwise, either the property isn't in the `renames` map (thus the |
|
245 // caller is not trying to rename it) or it is a "required" property. |
|
246 // Either way, we don't have to alias the property, we just have to copy it |
|
247 // to the map. |
|
248 else { |
|
249 // The property isn't in the map yet, so we copy it over. |
|
250 if (!owns(map, name)) { |
|
251 map[name] = { value: trait[name], enumerable: true }; |
|
252 } |
|
253 |
|
254 // The property is already in the map (that means another property was |
|
255 // aliased with this `name`, which creates a conflict if the property is |
|
256 // not marked as "required"), so we have to mark it as a "conflict" |
|
257 // property. |
|
258 else if (!isRequiredProperty(trait, name)) { |
|
259 map[name] = { |
|
260 value: ConflictPropertyDescriptor(name), |
|
261 enumerable: true |
|
262 }; |
|
263 } |
|
264 } |
|
265 }); |
|
266 return Object.create(Trait.prototype, map); |
|
267 } |
|
268 |
|
269 /** |
|
270 * Composes new resolved trait, with all the same properties as the original |
|
271 * `trait`, except that all properties whose name is an own property of |
|
272 * `resolutions` will be renamed to `resolutions[name]`. |
|
273 * |
|
274 * If `resolutions[name]` is `null`, the value is mapped to a property |
|
275 * descriptor that is marked as a "required" property. |
|
276 */ |
|
277 function resolve(resolutions, trait) { |
|
278 var renames = {}; |
|
279 var exclusions = []; |
|
280 |
|
281 // Go through each mapping in `resolutions` object and distribute it either |
|
282 // to `renames` or `exclusions`. |
|
283 Object.keys(resolutions).forEach(function(name) { |
|
284 |
|
285 // If `resolutions[name]` is a truthy value then it's a mapping old -> new |
|
286 // so we copy it to `renames` map. |
|
287 if (resolutions[name]) |
|
288 renames[name] = resolutions[name]; |
|
289 |
|
290 // Otherwise it's not a mapping but an exclusion instead in which case we |
|
291 // add it to the `exclusions` array. |
|
292 else |
|
293 exclusions.push(name); |
|
294 }); |
|
295 |
|
296 // First `exclude` **then** `rename` and order is important since |
|
297 // `exclude` and `rename` are not associative. |
|
298 return rename(renames, exclude(exclusions, trait)); |
|
299 } |
|
300 |
|
301 /** |
|
302 * Create a Trait (a custom property descriptor map) that represents the given |
|
303 * `object`'s own properties. Property descriptor map is a "custom", because it |
|
304 * inherits from `Trait.prototype` and it's property descriptors may contain |
|
305 * two attributes that is not part of the ES5 specification: |
|
306 * |
|
307 * - "required" (this property must be provided by another trait |
|
308 * before an instance of this trait can be created) |
|
309 * - "conflict" (when the trait is composed with another trait, |
|
310 * a unique value for this property is provided by two or more traits) |
|
311 * |
|
312 * Data properties bound to the `Trait.required` singleton exported by |
|
313 * this module will be marked as "required" properties. |
|
314 * |
|
315 * @param {Object} object |
|
316 * Map of properties to compose trait from. |
|
317 * @returns {Trait} |
|
318 * Trait / Property descriptor map containing all the own properties of the |
|
319 * given argument. |
|
320 */ |
|
321 function trait(object) { |
|
322 var map; |
|
323 var trait = object; |
|
324 |
|
325 if (!(object instanceof Trait)) { |
|
326 // If the passed `object` is not already an instance of `Trait`, we create |
|
327 // a property descriptor `map` containing descriptors for the own properties |
|
328 // of the given `object`. `map` is then used to create a `Trait` instance |
|
329 // after all properties are mapped. Note that we can't create a trait and |
|
330 // then just copy properties into it since that will fail for inherited |
|
331 // read-only properties. |
|
332 map = {}; |
|
333 |
|
334 // Each own property of the given `object` is mapped to a data property |
|
335 // whose value is a property descriptor. |
|
336 Object.keys(object).forEach(function (name) { |
|
337 |
|
338 // If property of an `object` is equal to a `Trait.required`, it means |
|
339 // that it was marked as "required" property, in which case we map it |
|
340 // to "required" property. |
|
341 if (Trait.required == |
|
342 Object.getOwnPropertyDescriptor(object, name).value) { |
|
343 map[name] = { |
|
344 value: RequiredPropertyDescriptor(name), |
|
345 enumerable: true |
|
346 }; |
|
347 } |
|
348 // Otherwise property is mapped to it's property descriptor. |
|
349 else { |
|
350 map[name] = { |
|
351 value: Object.getOwnPropertyDescriptor(object, name), |
|
352 enumerable: true |
|
353 }; |
|
354 } |
|
355 }); |
|
356 |
|
357 trait = Object.create(Trait.prototype, map); |
|
358 } |
|
359 return trait; |
|
360 } |
|
361 |
|
362 /** |
|
363 * Compose a property descriptor map that inherits from `Trait.prototype` and |
|
364 * contains property descriptors for all the own properties of the passed |
|
365 * traits. |
|
366 * |
|
367 * If two or more traits have own properties with the same name, the returned |
|
368 * trait will contain a "conflict" property for that name. Composition is a |
|
369 * commutative and associative operation, and the order of its arguments is |
|
370 * irrelevant. |
|
371 */ |
|
372 function compose(trait1, trait2/*, ...*/) { |
|
373 // Create a new property descriptor `map` to which all the own properties |
|
374 // of the passed traits are copied. This map will be used to create a `Trait` |
|
375 // instance that will be the result of this composition. |
|
376 var map = {}; |
|
377 |
|
378 // Properties of each passed trait are copied to the composition. |
|
379 Array.prototype.forEach.call(arguments, function(trait) { |
|
380 // Copying each property of the given trait. |
|
381 Object.keys(trait).forEach(function(name) { |
|
382 |
|
383 // If `map` already owns a property with the `name` and it is not |
|
384 // marked "required". |
|
385 if (owns(map, name) && !map[name].value.required) { |
|
386 |
|
387 // If the source trait's property with the `name` is marked as |
|
388 // "required", we do nothing, as the requirement was already resolved |
|
389 // by a property in the `map` (because it already contains a |
|
390 // non-required property with that `name`). But if properties are just |
|
391 // different, we have a name clash and we substitute it with a property |
|
392 // that is marked "conflict". |
|
393 if (!isRequiredProperty(trait, name) && |
|
394 !equivalentDescriptors(map[name].value, trait[name]) |
|
395 ) { |
|
396 map[name] = { |
|
397 value: ConflictPropertyDescriptor(name), |
|
398 enumerable: true |
|
399 }; |
|
400 } |
|
401 } |
|
402 |
|
403 // Otherwise, the `map` does not have an own property with the `name`, or |
|
404 // it is marked "required". Either way, the trait's property is copied to |
|
405 // the map (if the property of the `map` is marked "required", it is going |
|
406 // to be resolved by the property that is being copied). |
|
407 else { |
|
408 map[name] = { value: trait[name], enumerable: true }; |
|
409 } |
|
410 }); |
|
411 }); |
|
412 |
|
413 return Object.create(Trait.prototype, map); |
|
414 } |
|
415 |
|
416 /** |
|
417 * `defineProperties` is like `Object.defineProperties`, except that it |
|
418 * ensures that: |
|
419 * - An exception is thrown if any property in a given `properties` map |
|
420 * is marked as "required" property and same named property is not |
|
421 * found in a given `prototype`. |
|
422 * - An exception is thrown if any property in a given `properties` map |
|
423 * is marked as "conflict" property. |
|
424 * @param {Object} object |
|
425 * Object to define properties on. |
|
426 * @param {Object} properties |
|
427 * Properties descriptor map. |
|
428 * @returns {Object} |
|
429 * `object` that was passed as a first argument. |
|
430 */ |
|
431 function defineProperties(object, properties) { |
|
432 |
|
433 // Create a map into which we will copy each verified property from the given |
|
434 // `properties` description map. We use it to verify that none of the |
|
435 // provided properties is marked as a "conflict" property and that all |
|
436 // "required" properties are resolved by a property of an `object`, so we |
|
437 // can throw an exception before mutating object if that isn't the case. |
|
438 var verifiedProperties = {}; |
|
439 |
|
440 // Coping each property from a given `properties` descriptor map to a |
|
441 // verified map of property descriptors. |
|
442 Object.keys(properties).forEach(function(name) { |
|
443 |
|
444 // If property is marked as "required" property and we don't have a same |
|
445 // named property in a given `object` we throw an exception. If `object` |
|
446 // has same named property just skip this property since required property |
|
447 // is was inherited and there for requirement was satisfied. |
|
448 if (isRequiredProperty(properties, name)) { |
|
449 if (!(name in object)) |
|
450 throwRequiredPropertyError(name); |
|
451 } |
|
452 |
|
453 // If property is marked as "conflict" property we throw an exception. |
|
454 else if (isConflictProperty(properties, name)) { |
|
455 throwConflictPropertyError(name); |
|
456 } |
|
457 |
|
458 // If property is not marked neither as "required" nor "conflict" property |
|
459 // we copy it to verified properties map. |
|
460 else { |
|
461 verifiedProperties[name] = properties[name]; |
|
462 } |
|
463 }); |
|
464 |
|
465 // If no exceptions were thrown yet, we know that our verified property |
|
466 // descriptor map has no properties marked as "conflict" or "required", |
|
467 // so we just delegate to the built-in `Object.defineProperties`. |
|
468 return Object.defineProperties(object, verifiedProperties); |
|
469 } |
|
470 |
|
471 /** |
|
472 * `create` is like `Object.create`, except that it ensures that: |
|
473 * - An exception is thrown if any property in a given `properties` map |
|
474 * is marked as "required" property and same named property is not |
|
475 * found in a given `prototype`. |
|
476 * - An exception is thrown if any property in a given `properties` map |
|
477 * is marked as "conflict" property. |
|
478 * @param {Object} prototype |
|
479 * prototype of the composed object |
|
480 * @param {Object} properties |
|
481 * Properties descriptor map. |
|
482 * @returns {Object} |
|
483 * An object that inherits form a given `prototype` and implements all the |
|
484 * properties defined by a given `properties` descriptor map. |
|
485 */ |
|
486 function create(prototype, properties) { |
|
487 |
|
488 // Creating an instance of the given `prototype`. |
|
489 var object = Object.create(prototype); |
|
490 |
|
491 // Overriding `toString`, `constructor` methods if they are just inherited |
|
492 // from `Object.prototype` with a same named methods of the `Trait.prototype` |
|
493 // that will have more relevant behavior. |
|
494 overrideBuiltInMethods(object, Trait.prototype); |
|
495 |
|
496 // Trying to define given `properties` on the `object`. We use our custom |
|
497 // `defineProperties` function instead of build-in `Object.defineProperties` |
|
498 // that behaves exactly the same, except that it will throw if any |
|
499 // property in the given `properties` descriptor is marked as "required" or |
|
500 // "conflict" property. |
|
501 return defineProperties(object, properties); |
|
502 } |
|
503 |
|
504 /** |
|
505 * Composes new trait. If two or more traits have own properties with the |
|
506 * same name, the new trait will contain a "conflict" property for that name. |
|
507 * "compose" is a commutative and associative operation, and the order of its |
|
508 * arguments is not significant. |
|
509 * |
|
510 * **Note:** Use `Trait.compose` instead of calling this function with more |
|
511 * than one argument. The multiple-argument functionality is strictly for |
|
512 * backward compatibility. |
|
513 * |
|
514 * @params {Object} trait |
|
515 * Takes traits as an arguments |
|
516 * @returns {Object} |
|
517 * New trait containing the combined own properties of all the traits. |
|
518 * @example |
|
519 * var newTrait = compose(trait_1, trait_2, ..., trait_N) |
|
520 */ |
|
521 function Trait(trait1, trait2) { |
|
522 |
|
523 // If the function was called with one argument, the argument should be |
|
524 // an object whose properties are mapped to property descriptors on a new |
|
525 // instance of Trait, so we delegate to the trait function. |
|
526 // If the function was called with more than one argument, those arguments |
|
527 // should be instances of Trait or plain property descriptor maps |
|
528 // whose properties should be mixed into a new instance of Trait, |
|
529 // so we delegate to the compose function. |
|
530 |
|
531 return trait2 === undefined ? trait(trait1) : compose.apply(null, arguments); |
|
532 } |
|
533 |
|
534 Object.freeze(Object.defineProperties(Trait.prototype, { |
|
535 toString: { |
|
536 value: function toString() { |
|
537 return "[object " + this.constructor.name + "]"; |
|
538 } |
|
539 }, |
|
540 |
|
541 /** |
|
542 * `create` is like `Object.create`, except that it ensures that: |
|
543 * - An exception is thrown if this trait defines a property that is |
|
544 * marked as required property and same named property is not |
|
545 * found in a given `prototype`. |
|
546 * - An exception is thrown if this trait contains property that is |
|
547 * marked as "conflict" property. |
|
548 * @param {Object} |
|
549 * prototype of the compared object |
|
550 * @returns {Object} |
|
551 * An object with all of the properties described by the trait. |
|
552 */ |
|
553 create: { |
|
554 value: function createTrait(prototype) { |
|
555 return create(undefined === prototype ? Object.prototype : prototype, |
|
556 this); |
|
557 }, |
|
558 enumerable: true |
|
559 }, |
|
560 |
|
561 /** |
|
562 * Composes a new resolved trait, with all the same properties as the original |
|
563 * trait, except that all properties whose name is an own property of |
|
564 * `resolutions` will be renamed to the value of `resolutions[name]`. If |
|
565 * `resolutions[name]` is `null`, the property is marked as "required". |
|
566 * @param {Object} resolutions |
|
567 * An object whose own properties serve as a mapping from old names to new |
|
568 * names, or to `null` if the property should be excluded. |
|
569 * @returns {Object} |
|
570 * New trait with the same own properties as the original trait but renamed. |
|
571 */ |
|
572 resolve: { |
|
573 value: function resolveTrait(resolutions) { |
|
574 return resolve(resolutions, this); |
|
575 }, |
|
576 enumerable: true |
|
577 } |
|
578 })); |
|
579 |
|
580 /** |
|
581 * @see compose |
|
582 */ |
|
583 Trait.compose = Object.freeze(compose); |
|
584 Object.freeze(compose.prototype); |
|
585 |
|
586 /** |
|
587 * Constant singleton, representing placeholder for required properties. |
|
588 * @type {Object} |
|
589 */ |
|
590 Trait.required = Object.freeze(Object.create(Object.prototype, { |
|
591 toString: { |
|
592 value: Object.freeze(function toString() { |
|
593 return "<Trait.required>"; |
|
594 }) |
|
595 } |
|
596 })); |
|
597 Object.freeze(Trait.required.toString.prototype); |
|
598 |
|
599 exports.Trait = Object.freeze(Trait); |