diff options
Diffstat (limited to 'tools/addon-sdk-1.3/packages/api-utils/lib/light-traits.js')
-rw-r--r-- | tools/addon-sdk-1.3/packages/api-utils/lib/light-traits.js | 626 |
1 files changed, 0 insertions, 626 deletions
diff --git a/tools/addon-sdk-1.3/packages/api-utils/lib/light-traits.js b/tools/addon-sdk-1.3/packages/api-utils/lib/light-traits.js deleted file mode 100644 index 1afaad5..0000000 --- a/tools/addon-sdk-1.3/packages/api-utils/lib/light-traits.js +++ /dev/null @@ -1,626 +0,0 @@ -/* vim:ts=2:sts=2:sw=2: - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Initial Developer of the Original Code is Mozilla. - * Portions created by the Initial Developer are Copyright (C) 2010 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Irakli Gozalishvili <rfobic@gmail.com> (Original author) - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -"use strict"; - -// `var` is being used in the module in order to make it reusable in -// environments in which `let` is not yet supported. - -// Shortcut to `Object.prototype.hasOwnProperty.call`. -// owns(object, name) would be the same as -// Object.prototype.hasOwnProperty.call(object, name); -var owns = Function.prototype.call.bind(Object.prototype.hasOwnProperty); - -/** - * Whether or not given property descriptors are equivalent. They are - * equivalent either if both are marked as 'conflict' or 'required' property - * or if all the properties of descriptors are equal. - * @param {Object} actual - * @param {Object} expected - */ -function equivalentDescriptors(actual, expected) { - return (actual.conflict && expected.conflict) || - (actual.required && expected.required) || - equalDescriptors(actual, expected); -} -/** - * Whether or not given property descriptors define equal properties. - */ -function equalDescriptors(actual, expected) { - return actual.get === expected.get && - actual.set === expected.set && - actual.value === expected.value && - !!actual.enumerable === !!expected.enumerable && - !!actual.configurable === !!expected.configurable && - !!actual.writable === !!expected.writable; -} - -// Utilities that throwing exceptions for a properties that are marked -// as "required" or "conflict" properties. -function throwConflictPropertyError(name) { - throw new Error("Remaining conflicting property: `" + name + "`"); -} -function throwRequiredPropertyError(name) { - throw new Error("Missing required property: `" + name + "`"); -} - -/** - * Generates custom **required** property descriptor. Descriptor contains - * non-standard property `required` that is equal to `true`. - * @param {String} name - * property name to generate descriptor for. - * @returns {Object} - * custom property descriptor - */ -function RequiredPropertyDescriptor(name) { - // Creating function by binding first argument to a property `name` on the - // `throwConflictPropertyError` function. Created function is used as a - // getter & setter of the created property descriptor. This way we ensure - // that we throw exception late (on property access) if object with - // `required` property was instantiated using built-in `Object.create`. - var accessor = throwRequiredPropertyError.bind(null, name); - return { get: accessor, set: accessor, required: true }; -} - -/** - * Generates custom **conflicting** property descriptor. Descriptor contains - * non-standard property `conflict` that is equal to `true`. - * @param {String} name - * property name to generate descriptor for. - * @returns {Object} - * custom property descriptor - */ -function ConflictPropertyDescriptor(name) { - // For details see `RequiredPropertyDescriptor` since idea is same. - var accessor = throwConflictPropertyError.bind(null, name); - return { get: accessor, set: accessor, conflict: true }; -} - -/** - * Tests if property is marked as `required` property. - */ -function isRequiredProperty(object, name) { - return !!object[name].required; -} - -/** - * Tests if property is marked as `conflict` property. - */ -function isConflictProperty(object, name) { - return !!object[name].conflict; -} - -/** - * Function tests whether or not method of the `source` object with a given - * `name` is inherited from `Object.prototype`. - */ -function isBuiltInMethod(name, source) { - var target = Object.prototype[name]; - - // If methods are equal then we know it's `true`. - return target == source || - // If `source` object comes form a different sandbox `==` will evaluate - // to `false`, in that case we check if functions names and sources match. - (String(target) === String(source) && target.name === source.name); -} - -/** - * Function overrides `toString` and `constructor` methods of a given `target` - * object with a same-named methods of a given `source` if methods of `target` - * object are inherited / copied from `Object.prototype`. - * @see create - */ -function overrideBuiltInMethods(target, source) { - if (isBuiltInMethod("toString", target.toString)) { - Object.defineProperty(target, "toString", { - value: source.toString, - configurable: true, - enumerable: false - }); - } - - if (isBuiltInMethod("constructor", target.constructor)) { - Object.defineProperty(target, "constructor", { - value: source.constructor, - configurable: true, - enumerable: false - }); - } -} - -/** - * Composes new trait with the same own properties as the original trait, - * except that all property names appearing in the first argument are replaced - * by "required" property descriptors. - * @param {String[]} keys - * Array of strings property names. - * @param {Object} trait - * A trait some properties of which should be excluded. - * @returns {Object} - * @example - * var newTrait = exclude(["name", ...], trait) - */ -function exclude(names, trait) { - var map = {}; - - Object.keys(trait).forEach(function(name) { - - // If property is not excluded (the array of names does not contain it), - // or it is a "required" property, copy it to the property descriptor `map` - // that will be used for creation of resulting trait. - if (!~names.indexOf(name) || isRequiredProperty(trait, name)) - map[name] = { value: trait[name], enumerable: true }; - - // For all the `names` in the exclude name array we create required - // property descriptors and copy them to the `map`. - else - map[name] = { value: RequiredPropertyDescriptor(name), enumerable: true }; - }); - - return Object.create(Trait.prototype, map); -} - -/** - * Composes new instance of `Trait` with a properties of a given `trait`, - * except that all properties whose name is an own property of `renames` will - * be renamed to `renames[name]` and a `"required"` property for name will be - * added instead. - * - * For each renamed property, a required property is generated. If - * the `renames` map two properties to the same name, a conflict is generated. - * If the `renames` map a property to an existing unrenamed property, a - * conflict is generated. - * - * @param {Object} renames - * An object whose own properties serve as a mapping from old names to new - * names. - * @param {Object} trait - * A new trait with renamed properties. - * @returns {Object} - * @example - * - * // Return trait with `bar` property equal to `trait.foo` and with - * // `foo` and `baz` "required" properties. - * var renamedTrait = rename({ foo: "bar", baz: null }), trait); - * - * // t1 and t2 are equivalent traits - * var t1 = rename({a: "b"}, t); - * var t2 = compose(exclude(["a"], t), { a: { required: true }, b: t[a] }); - */ -function rename(renames, trait) { - var map = {}; - - // Loop over all the properties of the given `trait` and copy them to a - // property descriptor `map` that will be used for the creation of the - // resulting trait. Also, rename properties in the `map` as specified by - // `renames`. - Object.keys(trait).forEach(function(name) { - var alias; - - // If the property is in the `renames` map, and it isn't a "required" - // property (which should never need to be aliased because "required" - // properties never conflict), then we must try to rename it. - if (owns(renames, name) && !isRequiredProperty(trait, name)) { - alias = renames[name]; - - // If the `map` already has the `alias`, and it isn't a "required" - // property, that means the `alias` conflicts with an existing name for a - // provided trait (that can happen if >=2 properties are aliased to the - // same name). In this case we mark it as a conflicting property. - // Otherwise, everything is fine, and we copy property with an `alias` - // name. - if (owns(map, alias) && !map[alias].value.required) { - map[alias] = { - value: ConflictPropertyDescriptor(alias), - enumerable: true - }; - } - else { - map[alias] = { - value: trait[name], - enumerable: true - }; - } - - // Regardless of whether or not the rename was successful, we check to - // see if the original `name` exists in the map (such a property - // could exist if previous another property was aliased to this `name`). - // If it isn't, we mark it as "required", to make sure the caller - // provides another value for the old name, which methods of the trait - // might continue to reference. - if (!owns(map, name)) { - map[name] = { - value: RequiredPropertyDescriptor(name), - enumerable: true - }; - } - } - - // Otherwise, either the property isn't in the `renames` map (thus the - // caller is not trying to rename it) or it is a "required" property. - // Either way, we don't have to alias the property, we just have to copy it - // to the map. - else { - // The property isn't in the map yet, so we copy it over. - if (!owns(map, name)) { - map[name] = { value: trait[name], enumerable: true }; - } - - // The property is already in the map (that means another property was - // aliased with this `name`, which creates a conflict if the property is - // not marked as "required"), so we have to mark it as a "conflict" - // property. - else if (!isRequiredProperty(trait, name)) { - map[name] = { - value: ConflictPropertyDescriptor(name), - enumerable: true - }; - } - } - }); - return Object.create(Trait.prototype, map); -} - -/** - * Composes new resolved trait, with all the same properties as the original - * `trait`, except that all properties whose name is an own property of - * `resolutions` will be renamed to `resolutions[name]`. - * - * If `resolutions[name]` is `null`, the value is mapped to a property - * descriptor that is marked as a "required" property. - */ -function resolve(resolutions, trait) { - var renames = {}; - var exclusions = []; - - // Go through each mapping in `resolutions` object and distribute it either - // to `renames` or `exclusions`. - Object.keys(resolutions).forEach(function(name) { - - // If `resolutions[name]` is a truthy value then it's a mapping old -> new - // so we copy it to `renames` map. - if (resolutions[name]) - renames[name] = resolutions[name]; - - // Otherwise it's not a mapping but an exclusion instead in which case we - // add it to the `exclusions` array. - else - exclusions.push(name); - }); - - // First `exclude` **then** `rename` and order is important since - // `exclude` and `rename` are not associative. - return rename(renames, exclude(exclusions, trait)); -} - -/** - * Create a Trait (a custom property descriptor map) that represents the given - * `object`'s own properties. Property descriptor map is a "custom", because it - * inherits from `Trait.prototype` and it's property descriptors may contain - * two attributes that is not part of the ES5 specification: - * - * - "required" (this property must be provided by another trait - * before an instance of this trait can be created) - * - "conflict" (when the trait is composed with another trait, - * a unique value for this property is provided by two or more traits) - * - * Data properties bound to the `Trait.required` singleton exported by - * this module will be marked as "required" properties. - * - * @param {Object} object - * Map of properties to compose trait from. - * @returns {Trait} - * Trait / Property descriptor map containing all the own properties of the - * given argument. - */ -function trait(object) { - var map; - var trait = object; - - if (!(object instanceof Trait)) { - // If the passed `object` is not already an instance of `Trait`, we create - // a property descriptor `map` containing descriptors for the own properties - // of the given `object`. `map` is then used to create a `Trait` instance - // after all properties are mapped. Note that we can't create a trait and - // then just copy properties into it since that will fail for inherited - // read-only properties. - map = {}; - - // Each own property of the given `object` is mapped to a data property - // whose value is a property descriptor. - Object.keys(object).forEach(function (name) { - - // If property of an `object` is equal to a `Trait.required`, it means - // that it was marked as "required" property, in which case we map it - // to "required" property. - if (Trait.required == - Object.getOwnPropertyDescriptor(object, name).value) { - map[name] = { - value: RequiredPropertyDescriptor(name), - enumerable: true - }; - } - // Otherwise property is mapped to it's property descriptor. - else { - map[name] = { - value: Object.getOwnPropertyDescriptor(object, name), - enumerable: true - }; - } - }); - - trait = Object.create(Trait.prototype, map); - } - return trait; -} - -/** - * Compose a property descriptor map that inherits from `Trait.prototype` and - * contains property descriptors for all the own properties of the passed - * traits. - * - * If two or more traits have own properties with the same name, the returned - * trait will contain a "conflict" property for that name. Composition is a - * commutative and associative operation, and the order of its arguments is - * irrelevant. - */ -function compose(trait1, trait2/*, ...*/) { - // Create a new property descriptor `map` to which all the own properties - // of the passed traits are copied. This map will be used to create a `Trait` - // instance that will be the result of this composition. - var map = {}; - - // Properties of each passed trait are copied to the composition. - Array.prototype.forEach.call(arguments, function(trait) { - // Copying each property of the given trait. - Object.keys(trait).forEach(function(name) { - - // If `map` already owns a property with the `name` and it is not - // marked "required". - if (owns(map, name) && !map[name].value.required) { - - // If the source trait's property with the `name` is marked as - // "required", we do nothing, as the requirement was already resolved - // by a property in the `map` (because it already contains a - // non-required property with that `name`). But if properties are just - // different, we have a name clash and we substitute it with a property - // that is marked "conflict". - if (!isRequiredProperty(trait, name) && - !equivalentDescriptors(map[name].value, trait[name]) - ) { - map[name] = { - value: ConflictPropertyDescriptor(name), - enumerable: true - }; - } - } - - // Otherwise, the `map` does not have an own property with the `name`, or - // it is marked "required". Either way, the trait's property is copied to - // the map (if the property of the `map` is marked "required", it is going - // to be resolved by the property that is being copied). - else { - map[name] = { value: trait[name], enumerable: true }; - } - }); - }); - - return Object.create(Trait.prototype, map); -} - -/** - * `defineProperties` is like `Object.defineProperties`, except that it - * ensures that: - * - An exception is thrown if any property in a given `properties` map - * is marked as "required" property and same named property is not - * found in a given `prototype`. - * - An exception is thrown if any property in a given `properties` map - * is marked as "conflict" property. - * @param {Object} object - * Object to define properties on. - * @param {Object} properties - * Properties descriptor map. - * @returns {Object} - * `object` that was passed as a first argument. - */ -function defineProperties(object, properties) { - - // Create a map into which we will copy each verified property from the given - // `properties` description map. We use it to verify that none of the - // provided properties is marked as a "conflict" property and that all - // "required" properties are resolved by a property of an `object`, so we - // can throw an exception before mutating object if that isn't the case. - var verifiedProperties = {}; - - // Coping each property from a given `properties` descriptor map to a - // verified map of property descriptors. - Object.keys(properties).forEach(function(name) { - - // If property is marked as "required" property and we don't have a same - // named property in a given `object` we throw an exception. If `object` - // has same named property just skip this property since required property - // is was inherited and there for requirement was satisfied. - if (isRequiredProperty(properties, name)) { - if (!(name in object)) - throwRequiredPropertyError(name); - } - - // If property is marked as "conflict" property we throw an exception. - else if (isConflictProperty(properties, name)) { - throwConflictPropertyError(name); - } - - // If property is not marked neither as "required" nor "conflict" property - // we copy it to verified properties map. - else { - verifiedProperties[name] = properties[name]; - } - }); - - // If no exceptions were thrown yet, we know that our verified property - // descriptor map has no properties marked as "conflict" or "required", - // so we just delegate to the built-in `Object.defineProperties`. - return Object.defineProperties(object, verifiedProperties); -} - -/** - * `create` is like `Object.create`, except that it ensures that: - * - An exception is thrown if any property in a given `properties` map - * is marked as "required" property and same named property is not - * found in a given `prototype`. - * - An exception is thrown if any property in a given `properties` map - * is marked as "conflict" property. - * @param {Object} prototype - * prototype of the composed object - * @param {Object} properties - * Properties descriptor map. - * @returns {Object} - * An object that inherits form a given `prototype` and implements all the - * properties defined by a given `properties` descriptor map. - */ -function create(prototype, properties) { - - // Creating an instance of the given `prototype`. - var object = Object.create(prototype); - - // Overriding `toString`, `constructor` methods if they are just inherited - // from `Object.prototype` with a same named methods of the `Trait.prototype` - // that will have more relevant behavior. - overrideBuiltInMethods(object, Trait.prototype); - - // Trying to define given `properties` on the `object`. We use our custom - // `defineProperties` function instead of build-in `Object.defineProperties` - // that behaves exactly the same, except that it will throw if any - // property in the given `properties` descriptor is marked as "required" or - // "conflict" property. - return defineProperties(object, properties); -} - -/** - * Composes new trait. If two or more traits have own properties with the - * same name, the new trait will contain a "conflict" property for that name. - * "compose" is a commutative and associative operation, and the order of its - * arguments is not significant. - * - * **Note:** Use `Trait.compose` instead of calling this function with more - * than one argument. The multiple-argument functionality is strictly for - * backward compatibility. - * - * @params {Object} trait - * Takes traits as an arguments - * @returns {Object} - * New trait containing the combined own properties of all the traits. - * @example - * var newTrait = compose(trait_1, trait_2, ..., trait_N) - */ -function Trait(trait1, trait2) { - - // If the function was called with one argument, the argument should be - // an object whose properties are mapped to property descriptors on a new - // instance of Trait, so we delegate to the trait function. - // If the function was called with more than one argument, those arguments - // should be instances of Trait or plain property descriptor maps - // whose properties should be mixed into a new instance of Trait, - // so we delegate to the compose function. - - return trait2 === undefined ? trait(trait1) : compose.apply(null, arguments); -} - -Object.freeze(Object.defineProperties(Trait.prototype, { - toString: { - value: function toString() { - return "[object " + this.constructor.name + "]"; - } - }, - - /** - * `create` is like `Object.create`, except that it ensures that: - * - An exception is thrown if this trait defines a property that is - * marked as required property and same named property is not - * found in a given `prototype`. - * - An exception is thrown if this trait contains property that is - * marked as "conflict" property. - * @param {Object} - * prototype of the compared object - * @returns {Object} - * An object with all of the properties described by the trait. - */ - create: { - value: function createTrait(prototype) { - return create(undefined === prototype ? Object.prototype : prototype, - this); - }, - enumerable: true - }, - - /** - * Composes a new resolved trait, with all the same properties as the original - * trait, except that all properties whose name is an own property of - * `resolutions` will be renamed to the value of `resolutions[name]`. If - * `resolutions[name]` is `null`, the property is marked as "required". - * @param {Object} resolutions - * An object whose own properties serve as a mapping from old names to new - * names, or to `null` if the property should be excluded. - * @returns {Object} - * New trait with the same own properties as the original trait but renamed. - */ - resolve: { - value: function resolveTrait(resolutions) { - return resolve(resolutions, this); - }, - enumerable: true - } -})); - -/** - * @see compose - */ -Trait.compose = Object.freeze(compose); -Object.freeze(compose.prototype); - -/** - * Constant singleton, representing placeholder for required properties. - * @type {Object} - */ -Trait.required = Object.freeze(Object.create(Object.prototype, { - toString: { - value: Object.freeze(function toString() { - return "<Trait.required>"; - }) - } -})); -Object.freeze(Trait.required.toString.prototype); - -exports.Trait = Object.freeze(Trait); |