addon-sdk/source/lib/method/core.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/addon-sdk/source/lib/method/core.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,225 @@
     1.4 +"use strict";
     1.5 +
     1.6 +var defineProperty = Object.defineProperty || function(object, name, property) {
     1.7 +  object[name] = property.value
     1.8 +  return object
     1.9 +}
    1.10 +
    1.11 +// Shortcut for `Object.prototype.toString` for faster access.
    1.12 +var typefy = Object.prototype.toString
    1.13 +
    1.14 +// Map to for jumping from typeof(value) to associated type prefix used
    1.15 +// as a hash in the map of builtin implementations.
    1.16 +var types = { "function": "Object", "object": "Object" }
    1.17 +
    1.18 +// Array is used to save method implementations for the host objects in order
    1.19 +// to avoid extending them with non-primitive values that could cause leaks.
    1.20 +var host = []
    1.21 +// Hash map is used to save method implementations for builtin types in order
    1.22 +// to avoid extending their prototypes. This also allows to share method
    1.23 +// implementations for types across diff contexts / frames / compartments.
    1.24 +var builtin = {}
    1.25 +
    1.26 +function Primitive() {}
    1.27 +function ObjectType() {}
    1.28 +ObjectType.prototype = new Primitive()
    1.29 +function ErrorType() {}
    1.30 +ErrorType.prototype = new ObjectType()
    1.31 +
    1.32 +var Default = builtin.Default = Primitive.prototype
    1.33 +var Null = builtin.Null = new Primitive()
    1.34 +var Void = builtin.Void = new Primitive()
    1.35 +builtin.String = new Primitive()
    1.36 +builtin.Number = new Primitive()
    1.37 +builtin.Boolean = new Primitive()
    1.38 +
    1.39 +builtin.Object = ObjectType.prototype
    1.40 +builtin.Error = ErrorType.prototype
    1.41 +
    1.42 +builtin.EvalError = new ErrorType()
    1.43 +builtin.InternalError = new ErrorType()
    1.44 +builtin.RangeError = new ErrorType()
    1.45 +builtin.ReferenceError = new ErrorType()
    1.46 +builtin.StopIteration = new ErrorType()
    1.47 +builtin.SyntaxError = new ErrorType()
    1.48 +builtin.TypeError = new ErrorType()
    1.49 +builtin.URIError = new ErrorType()
    1.50 +
    1.51 +
    1.52 +function Method(hint) {
    1.53 +  /**
    1.54 +  Private Method is a callable private name that dispatches on the first
    1.55 +  arguments same named Method:
    1.56 +
    1.57 +      method(object, ...rest) => object[method](...rest)
    1.58 +
    1.59 +  Optionally hint string may be provided that will be used in generated names
    1.60 +  to ease debugging.
    1.61 +
    1.62 +  ## Example
    1.63 +
    1.64 +      var foo = Method()
    1.65 +
    1.66 +      // Implementation for any types
    1.67 +      foo.define(function(value, arg1, arg2) {
    1.68 +        // ...
    1.69 +      })
    1.70 +
    1.71 +      // Implementation for a specific type
    1.72 +      foo.define(BarType, function(bar, arg1, arg2) {
    1.73 +        // ...
    1.74 +      })
    1.75 +  **/
    1.76 +
    1.77 +  // Create an internal unique name if `hint` is provided it is used to
    1.78 +  // prefix name to ease debugging.
    1.79 +  var name = (hint || "") + "#" + Math.random().toString(32).substr(2)
    1.80 +
    1.81 +  function dispatch(value) {
    1.82 +    // Method dispatches on type of the first argument.
    1.83 +    // If first argument is `null` or `void` associated implementation is
    1.84 +    // looked up in the `builtin` hash where implementations for built-ins
    1.85 +    // are stored.
    1.86 +    var type = null
    1.87 +    var method = value === null ? Null[name] :
    1.88 +                 value === void(0) ? Void[name] :
    1.89 +                 // Otherwise attempt to use method with a generated private
    1.90 +                 // `name` that is supposedly in the prototype chain of the
    1.91 +                 // `target`.
    1.92 +                 value[name] ||
    1.93 +                 // Otherwise assume it's one of the built-in type instances,
    1.94 +                 // in which case implementation is stored in a `builtin` hash.
    1.95 +                 // Attempt to find a implementation for the given built-in
    1.96 +                 // via constructor name and method name.
    1.97 +                 ((type = builtin[(value.constructor || "").name]) &&
    1.98 +                  type[name]) ||
    1.99 +                 // Otherwise assume it's a host object. For host objects
   1.100 +                 // actual method implementations are stored in the `host`
   1.101 +                 // array and only index for the implementation is stored
   1.102 +                 // in the host object's prototype chain. This avoids memory
   1.103 +                 // leaks that otherwise could happen when saving JS objects
   1.104 +                 // on host object.
   1.105 +                 host[value["!" + name] || void(0)] ||
   1.106 +                 // Otherwise attempt to lookup implementation for builtins by
   1.107 +                 // a type of the value. This basically makes sure that all
   1.108 +                 // non primitive values will delegate to an `Object`.
   1.109 +                 ((type = builtin[types[typeof(value)]]) && type[name])
   1.110 +
   1.111 +
   1.112 +    // If method implementation for the type is still not found then
   1.113 +    // just fallback for default implementation.
   1.114 +    method = method || Default[name]
   1.115 +
   1.116 +
   1.117 +    // If implementation is still not found (which also means there is no
   1.118 +    // default) just throw an error with a descriptive message.
   1.119 +    if (!method) throw TypeError("Type does not implements method: " + name)
   1.120 +
   1.121 +    // If implementation was found then just delegate.
   1.122 +    return method.apply(method, arguments)
   1.123 +  }
   1.124 +
   1.125 +  // Make `toString` of the dispatch return a private name, this enables
   1.126 +  // method definition without sugar:
   1.127 +  //
   1.128 +  //    var method = Method()
   1.129 +  //    object[method] = function() { /***/ }
   1.130 +  dispatch.toString = function toString() { return name }
   1.131 +
   1.132 +  // Copy utility methods for convenient API.
   1.133 +  dispatch.implement = implementMethod
   1.134 +  dispatch.define = defineMethod
   1.135 +
   1.136 +  return dispatch
   1.137 +}
   1.138 +
   1.139 +// Create method shortcuts form functions.
   1.140 +var defineMethod = function defineMethod(Type, lambda) {
   1.141 +  return define(this, Type, lambda)
   1.142 +}
   1.143 +var implementMethod = function implementMethod(object, lambda) {
   1.144 +  return implement(this, object, lambda)
   1.145 +}
   1.146 +
   1.147 +// Define `implement` and `define` polymorphic methods to allow definitions
   1.148 +// and implementations through them.
   1.149 +var implement = Method("implement")
   1.150 +var define = Method("define")
   1.151 +
   1.152 +
   1.153 +function _implement(method, object, lambda) {
   1.154 +  /**
   1.155 +  Implements `Method` for the given `object` with a provided `implementation`.
   1.156 +  Calling `Method` with `object` as a first argument will dispatch on provided
   1.157 +  implementation.
   1.158 +  **/
   1.159 +  return defineProperty(object, method.toString(), {
   1.160 +    enumerable: false,
   1.161 +    configurable: false,
   1.162 +    writable: false,
   1.163 +    value: lambda
   1.164 +  })
   1.165 +}
   1.166 +
   1.167 +function _define(method, Type, lambda) {
   1.168 +  /**
   1.169 +  Defines `Method` for the given `Type` with a provided `implementation`.
   1.170 +  Calling `Method` with a first argument of this `Type` will dispatch on
   1.171 +  provided `implementation`. If `Type` is a `Method` default implementation
   1.172 +  is defined. If `Type` is a `null` or `undefined` `Method` is implemented
   1.173 +  for that value type.
   1.174 +  **/
   1.175 +
   1.176 +  // Attempt to guess a type via `Object.prototype.toString.call` hack.
   1.177 +  var type = Type && typefy.call(Type.prototype)
   1.178 +
   1.179 +  // If only two arguments are passed then `Type` is actually an implementation
   1.180 +  // for a default type.
   1.181 +  if (!lambda) Default[method] = Type
   1.182 +  // If `Type` is `null` or `void` store implementation accordingly.
   1.183 +  else if (Type === null) Null[method] = lambda
   1.184 +  else if (Type === void(0)) Void[method] = lambda
   1.185 +  // If `type` hack indicates built-in type and type has a name us it to
   1.186 +  // store a implementation into associated hash. If hash for this type does
   1.187 +  // not exists yet create one.
   1.188 +  else if (type !== "[object Object]" && Type.name) {
   1.189 +    var Bulitin = builtin[Type.name] || (builtin[Type.name] = new ObjectType())
   1.190 +    Bulitin[method] = lambda
   1.191 +  }
   1.192 +  // If `type` hack indicates an object, that may be either object or any
   1.193 +  // JS defined "Class". If name of the constructor is `Object`, assume it's
   1.194 +  // built-in `Object` and store implementation accordingly.
   1.195 +  else if (Type.name === "Object")
   1.196 +    builtin.Object[method] = lambda
   1.197 +  // Host objects are pain!!! Every browser does some crazy stuff for them
   1.198 +  // So far all browser seem to not implement `call` method for host object
   1.199 +  // constructors. If that is a case here, assume it's a host object and
   1.200 +  // store implementation in a `host` array and store `index` in the array
   1.201 +  // in a `Type.prototype` itself. This avoids memory leaks that could be
   1.202 +  // caused by storing JS objects on a host objects.
   1.203 +  else if (Type.call === void(0)) {
   1.204 +    var index = host.indexOf(lambda)
   1.205 +    if (index < 0) index = host.push(lambda) - 1
   1.206 +    // Prefix private name with `!` so it can be dispatched from the method
   1.207 +    // without type checks.
   1.208 +    implement("!" + method, Type.prototype, index)
   1.209 +  }
   1.210 +  // If Got that far `Type` is user defined JS `Class`. Define private name
   1.211 +  // as hidden property on it's prototype.
   1.212 +  else
   1.213 +    implement(method, Type.prototype, lambda)
   1.214 +}
   1.215 +
   1.216 +// And provided implementations for a polymorphic equivalents.
   1.217 +_define(define, _define)
   1.218 +_define(implement, _implement)
   1.219 +
   1.220 +// Define exports on `Method` as it's only thing being exported.
   1.221 +Method.implement = implement
   1.222 +Method.define = define
   1.223 +Method.Method = Method
   1.224 +Method.method = Method
   1.225 +Method.builtin = builtin
   1.226 +Method.host = host
   1.227 +
   1.228 +module.exports = Method

mercurial