core.js 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. "use strict";
  2. var defineProperty = Object.defineProperty || function(object, name, property) {
  3. object[name] = property.value
  4. return object
  5. }
  6. // Shortcut for `Object.prototype.toString` for faster access.
  7. var typefy = Object.prototype.toString
  8. // Map to for jumping from typeof(value) to associated type prefix used
  9. // as a hash in the map of builtin implementations.
  10. var types = { "function": "Object", "object": "Object" }
  11. // Array is used to save method implementations for the host objects in order
  12. // to avoid extending them with non-primitive values that could cause leaks.
  13. var host = []
  14. // Hash map is used to save method implementations for builtin types in order
  15. // to avoid extending their prototypes. This also allows to share method
  16. // implementations for types across diff contexts / frames / compartments.
  17. var builtin = {}
  18. function Primitive() {}
  19. function ObjectType() {}
  20. ObjectType.prototype = new Primitive()
  21. function ErrorType() {}
  22. ErrorType.prototype = new ObjectType()
  23. var Default = builtin.Default = Primitive.prototype
  24. var Null = builtin.Null = new Primitive()
  25. var Void = builtin.Void = new Primitive()
  26. builtin.String = new Primitive()
  27. builtin.Number = new Primitive()
  28. builtin.Boolean = new Primitive()
  29. builtin.Object = ObjectType.prototype
  30. builtin.Error = ErrorType.prototype
  31. builtin.EvalError = new ErrorType()
  32. builtin.InternalError = new ErrorType()
  33. builtin.RangeError = new ErrorType()
  34. builtin.ReferenceError = new ErrorType()
  35. builtin.StopIteration = new ErrorType()
  36. builtin.SyntaxError = new ErrorType()
  37. builtin.TypeError = new ErrorType()
  38. builtin.URIError = new ErrorType()
  39. function Method(hint) {
  40. /**
  41. Private Method is a callable private name that dispatches on the first
  42. arguments same named Method:
  43. method(object, ...rest) => object[method](...rest)
  44. Optionally hint string may be provided that will be used in generated names
  45. to ease debugging.
  46. ## Example
  47. var foo = Method()
  48. // Implementation for any types
  49. foo.define(function(value, arg1, arg2) {
  50. // ...
  51. })
  52. // Implementation for a specific type
  53. foo.define(BarType, function(bar, arg1, arg2) {
  54. // ...
  55. })
  56. **/
  57. // Create an internal unique name if `hint` is provided it is used to
  58. // prefix name to ease debugging.
  59. var name = (hint || "") + "#" + Math.random().toString(32).substr(2)
  60. function dispatch(value) {
  61. // Method dispatches on type of the first argument.
  62. // If first argument is `null` or `void` associated implementation is
  63. // looked up in the `builtin` hash where implementations for built-ins
  64. // are stored.
  65. var type = null
  66. var method = value === null ? Null[name] :
  67. value === void(0) ? Void[name] :
  68. // Otherwise attempt to use method with a generated private
  69. // `name` that is supposedly in the prototype chain of the
  70. // `target`.
  71. value[name] ||
  72. // Otherwise assume it's one of the built-in type instances,
  73. // in which case implementation is stored in a `builtin` hash.
  74. // Attempt to find a implementation for the given built-in
  75. // via constructor name and method name.
  76. ((type = builtin[(value.constructor || "").name]) &&
  77. type[name]) ||
  78. // Otherwise assume it's a host object. For host objects
  79. // actual method implementations are stored in the `host`
  80. // array and only index for the implementation is stored
  81. // in the host object's prototype chain. This avoids memory
  82. // leaks that otherwise could happen when saving JS objects
  83. // on host object.
  84. host[value["!" + name] || void(0)] ||
  85. // Otherwise attempt to lookup implementation for builtins by
  86. // a type of the value. This basically makes sure that all
  87. // non primitive values will delegate to an `Object`.
  88. ((type = builtin[types[typeof(value)]]) && type[name])
  89. // If method implementation for the type is still not found then
  90. // just fallback for default implementation.
  91. method = method || Default[name]
  92. // If implementation is still not found (which also means there is no
  93. // default) just throw an error with a descriptive message.
  94. if (!method) throw TypeError("Type does not implements method: " + name)
  95. // If implementation was found then just delegate.
  96. return method.apply(method, arguments)
  97. }
  98. // Make `toString` of the dispatch return a private name, this enables
  99. // method definition without sugar:
  100. //
  101. // var method = Method()
  102. // object[method] = function() { /***/ }
  103. dispatch.toString = function toString() { return name }
  104. // Copy utility methods for convenient API.
  105. dispatch.implement = implementMethod
  106. dispatch.define = defineMethod
  107. return dispatch
  108. }
  109. // Create method shortcuts form functions.
  110. var defineMethod = function defineMethod(Type, lambda) {
  111. return define(this, Type, lambda)
  112. }
  113. var implementMethod = function implementMethod(object, lambda) {
  114. return implement(this, object, lambda)
  115. }
  116. // Define `implement` and `define` polymorphic methods to allow definitions
  117. // and implementations through them.
  118. var implement = Method("implement")
  119. var define = Method("define")
  120. function _implement(method, object, lambda) {
  121. /**
  122. Implements `Method` for the given `object` with a provided `implementation`.
  123. Calling `Method` with `object` as a first argument will dispatch on provided
  124. implementation.
  125. **/
  126. return defineProperty(object, method.toString(), {
  127. enumerable: false,
  128. configurable: false,
  129. writable: false,
  130. value: lambda
  131. })
  132. }
  133. function _define(method, Type, lambda) {
  134. /**
  135. Defines `Method` for the given `Type` with a provided `implementation`.
  136. Calling `Method` with a first argument of this `Type` will dispatch on
  137. provided `implementation`. If `Type` is a `Method` default implementation
  138. is defined. If `Type` is a `null` or `undefined` `Method` is implemented
  139. for that value type.
  140. **/
  141. // Attempt to guess a type via `Object.prototype.toString.call` hack.
  142. var type = Type && typefy.call(Type.prototype)
  143. // If only two arguments are passed then `Type` is actually an implementation
  144. // for a default type.
  145. if (!lambda) Default[method] = Type
  146. // If `Type` is `null` or `void` store implementation accordingly.
  147. else if (Type === null) Null[method] = lambda
  148. else if (Type === void(0)) Void[method] = lambda
  149. // If `type` hack indicates built-in type and type has a name us it to
  150. // store a implementation into associated hash. If hash for this type does
  151. // not exists yet create one.
  152. else if (type !== "[object Object]" && Type.name) {
  153. var Bulitin = builtin[Type.name] || (builtin[Type.name] = new ObjectType())
  154. Bulitin[method] = lambda
  155. }
  156. // If `type` hack indicates an object, that may be either object or any
  157. // JS defined "Class". If name of the constructor is `Object`, assume it's
  158. // built-in `Object` and store implementation accordingly.
  159. else if (Type.name === "Object")
  160. builtin.Object[method] = lambda
  161. // Host objects are pain!!! Every browser does some crazy stuff for them
  162. // So far all browser seem to not implement `call` method for host object
  163. // constructors. If that is a case here, assume it's a host object and
  164. // store implementation in a `host` array and store `index` in the array
  165. // in a `Type.prototype` itself. This avoids memory leaks that could be
  166. // caused by storing JS objects on a host objects.
  167. else if (Type.call === void(0)) {
  168. var index = host.indexOf(lambda)
  169. if (index < 0) index = host.push(lambda) - 1
  170. // Prefix private name with `!` so it can be dispatched from the method
  171. // without type checks.
  172. implement("!" + method, Type.prototype, index)
  173. }
  174. // If Got that far `Type` is user defined JS `Class`. Define private name
  175. // as hidden property on it's prototype.
  176. else
  177. implement(method, Type.prototype, lambda)
  178. }
  179. // And provided implementations for a polymorphic equivalents.
  180. _define(define, _define)
  181. _define(implement, _implement)
  182. // Define exports on `Method` as it's only thing being exported.
  183. Method.implement = implement
  184. Method.define = define
  185. Method.Method = Method
  186. Method.method = Method
  187. Method.builtin = builtin
  188. Method.host = host
  189. module.exports = Method