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