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

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

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

mercurial