diff options
Diffstat (limited to 'src/Injector.js')
| -rw-r--r-- | src/Injector.js | 270 |
1 files changed, 155 insertions, 115 deletions
diff --git a/src/Injector.js b/src/Injector.js index 8326d645..c9901513 100644 --- a/src/Injector.js +++ b/src/Injector.js @@ -10,7 +10,7 @@ * dependency injection (see {@link guide/dev_guide.di dependency injection}). * - * @param {<string, function()>} modules... A list of module functions or their aliases. See + * @param {Array.<string|Function>} modules A list of module functions or their aliases. See * {@link angular.module}. The `ng` module must be explicitly added. * @returns {function()} Injector function. See {@link angular.module.AUTO.$injector $injector}. * @@ -18,11 +18,11 @@ * Typical usage * <pre> * // create an injector - * var $injector = angular.injector('ng'); + * var $injector = angular.injector(['ng']); * * // use the injector to kick of your application * // use the type inference to auto inject arguments, or use implicit injection - * $injector.invoke(null, function($rootScope, $compile, $document){ + * $injector.invoke(function($rootScope, $compile, $document){ * $compile($document)($rootScope); * $rootScope.$digest(); * }); @@ -113,14 +113,26 @@ function inferInjectionArgs(fn) { /** * @ngdoc method + * @name angular.module.AUTO.$injector#get + * @methodOf angular.module.AUTO.$injector + * + * @description + * Return an instance of the service. + * + * @param {string} name The name of the instance to retrieve. + * @return {*} The instance. + */ + +/** + * @ngdoc method * @name angular.module.AUTO.$injector#invoke * @methodOf angular.module.AUTO.$injector * * @description * Invoke the method and supply the method arguments from the `$injector`. * - * @param {Object} self The `this` for the invoked method. - * @param {function} fn The function to invoke. The function arguments come form the function annotation. + * @param {!function} fn The function to invoke. The function arguments come form the function annotation. + * @param {Object=} self The `this` for the invoked method. * @param {Object=} locals Optional object. If preset then any argument names are read from this object first, before * the `$injector` is consulted. * @return the value returned by the invoked `fn` function. @@ -238,29 +250,35 @@ function inferInjectionArgs(fn) { function createInjector(modulesToLoad) { - var cache = {}, - providerSuffix = 'Provider', + var providerSuffix = 'Provider', path = [], - $injector, - loadedModules = new HashMap(); - - value('$injector', $injector = { - get: getService, - invoke: invoke, - instantiate: instantiate - }); - value('$provide', { - service: supportObject(service), - factory: supportObject(factory), - value: supportObject(value), - decorator: decorator - }); + loadedModules = new HashMap(), + providerCache = { + $provide: { + service: supportObject(service), + factory: supportObject(factory), + value: supportObject(value), + decorator: decorator + } + }, + providerInjector = createInternalInjector(providerCache, function() { + throw Error("Unknown provider: " + path.join(' <- ')); + }), + instanceCache = {}, + instanceInjector = (instanceCache.$injector = + createInternalInjector(instanceCache, function(servicename) { + var provider = providerInjector.get(servicename + providerSuffix); + return instanceInjector.invoke(provider.$get, provider); + })); + loadModules(modulesToLoad); - return $injector; + return instanceInjector; //////////////////////////////////// + // $provider + //////////////////////////////////// function supportObject(delegate) { return function(key, value) { @@ -274,124 +292,146 @@ function createInjector(modulesToLoad) { function service(name, provider) { if (isFunction(provider)){ - provider = instantiate(provider); + provider = providerInjector.instantiate(provider); } if (!provider.$get) { throw Error('Provider ' + name + ' must define $get factory method.'); } - cache['#' + name + providerSuffix] = provider; + providerCache[name + providerSuffix] = provider; } function factory(name, factoryFn) { service(name, { $get:factoryFn }); } function value(name, value) { factory(name, valueFn(value)); } - function decorator(name, decorFn) { - var origProvider = cache['#' + name + providerSuffix]; - if (!origProvider) throw Error("Can't find provider for: " + name); - if (cache['#' + name]) throw Error("Service " + name + " already instantiated, can't decorate!"); - var orig$get = origProvider.$get; + function decorator(serviceName, decorFn) { + var origProvider = providerInjector.get(serviceName + providerSuffix), + orig$get = origProvider.$get; + origProvider.$get = function() { - var origInstance = $injector.invoke(origProvider, orig$get); - return $injector.invoke(null, decorFn, {$delegate: origInstance}); + var origInstance = instanceInjector.invoke(orig$get, origProvider); + return instanceInjector.invoke(decorFn, null, {$delegate: origInstance}); }; } - - function getService(value) { - if (typeof value !== 'string') { - throw Error('Service name expected'); - } - var instanceKey = '#' + value, - instance = cache[instanceKey]; - if (instance !== undefined || cache.hasOwnProperty(instanceKey)) { - return instance; - } - try { - path.unshift(value); - var providerKey = instanceKey + providerSuffix, - provider = cache[providerKey]; - if (provider) { - return cache[instanceKey] = invoke(provider, provider.$get); + //////////////////////////////////// + // Module Loading + //////////////////////////////////// + function loadModules(modulesToLoad){ + forEach(modulesToLoad, function(module) { + if (loadedModules.get(module)) return; + loadedModules.put(module, true); + if (isString(module)) { + var moduleFn = angularModule(module); + loadModules(moduleFn.requires); + + try { + for(var invokeQueue = moduleFn.invokeQueue, i = 0, ii = invokeQueue.length; i < ii; i++) { + var invokeArgs = invokeQueue[i], + provider = invokeArgs[0] == '$injector' + ? providerInjector + : providerInjector.get(invokeArgs[0]); + + provider[invokeArgs[1]].apply(provider, invokeArgs[2]); + } + } catch (e) { + if (e.message) e.message += ' from ' + module; + throw e; + } + } else if (isFunction(module)) { + try { + providerInjector.invoke(module); + } catch (e) { + if (e.message) e.message += ' from ' + module; + throw e; + } + } else if (isArray(module)) { + try { + providerInjector.invoke(module); + } catch (e) { + if (e.message) e.message += ' from ' + String(module[module.length - 1]); + throw e; + } } else { - throw Error("Unknown provider for '" + path.join("' <- '") + "'."); + assertArgFn(module, 'module'); } - } finally { - path.shift(); - } + }); } - function invoke(self, fn, locals){ - var args = [], - $inject, - length, - key; - - if (typeof fn == 'function') { - $inject = inferInjectionArgs(fn); - length = $inject.length; - } else { - if (isArray(fn)) { - $inject = fn; - length = $inject.length; - fn = $inject[--length]; - } - assertArgFn(fn, 'fn'); - } + //////////////////////////////////// + // internal Injector + //////////////////////////////////// - while(length--) { - key = $inject[length]; - args.unshift( - locals && locals.hasOwnProperty(key) - ? locals[key] - : getService($inject[length], path) - ); - } + function createInternalInjector(cache, factory) { - // Performance optimization: http://jsperf.com/apply-vs-call-vs-invoke - switch (self ? -1 : args.length) { - case 0: return fn(); - case 1: return fn(args[0]); - case 2: return fn(args[0], args[1]); - case 3: return fn(args[0], args[1], args[2]); - case 4: return fn(args[0], args[1], args[2], args[3]); - case 5: return fn(args[0], args[1], args[2], args[3], args[4]); - case 6: return fn(args[0], args[1], args[2], args[3], args[4], args[5]); - case 7: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6]); - case 8: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]); - case 9: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]); - case 10: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9]); - default: return fn.apply(self, args); + function getService(serviceName) { + if (typeof serviceName !== 'string') { + throw Error('Service name expected'); + } + if (cache.hasOwnProperty(serviceName)) { + return cache[serviceName]; + } else { + try { + path.unshift(serviceName); + return cache[serviceName] = factory(serviceName); + } finally { + path.shift(); + } + } } - } - function instantiate(Type, locals){ - var Constructor = function(){}, - instance; - Constructor.prototype = Type.prototype; - instance = new Constructor(); - return invoke(instance, Type, locals) || instance; - } + function invoke(fn, self, locals){ + var args = [], + $injectAnnotation, + $injectAnnotationIndex, + key; - function loadModules(modulesToLoad){ - forEach(modulesToLoad, function(module) { - if (loadedModules.get(module)) return; - loadedModules.put(module, true); - if (isString(module)) { - module = angularModule(module); - loadModules(module.requires); + if (typeof fn == 'function') { + $injectAnnotation = inferInjectionArgs(fn); + $injectAnnotationIndex = $injectAnnotation.length; + } else { + if (isArray(fn)) { + $injectAnnotation = fn; + $injectAnnotationIndex = $injectAnnotation.length; + fn = $injectAnnotation[--$injectAnnotationIndex]; + } + assertArgFn(fn, 'fn'); + } - for(var invokeQueue = module.invokeQueue, i = 0, ii = invokeQueue.length; i < ii; i++) { - var invokeArgs = invokeQueue[i], - service = getService(invokeArgs[0]); + while($injectAnnotationIndex--) { + key = $injectAnnotation[$injectAnnotationIndex]; + args.unshift(locals && locals.hasOwnProperty(key) ? locals[key] : getService(key)); + } - service[invokeArgs[1]].apply(service, invokeArgs[2]); - } - } else if (isFunction(module) || isArray(module)) { - invoke(null, module); - } else { - assertArgFn(module, 'module'); + // Performance optimization: http://jsperf.com/apply-vs-call-vs-invoke + switch (self ? -1 : args.length) { + case 0: return fn(); + case 1: return fn(args[0]); + case 2: return fn(args[0], args[1]); + case 3: return fn(args[0], args[1], args[2]); + case 4: return fn(args[0], args[1], args[2], args[3]); + case 5: return fn(args[0], args[1], args[2], args[3], args[4]); + case 6: return fn(args[0], args[1], args[2], args[3], args[4], args[5]); + case 7: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6]); + case 8: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]); + case 9: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]); + case 10: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9]); + default: return fn.apply(self, args); } - }); + } + + function instantiate(Type, locals){ + var Constructor = function(){}, + instance; + Constructor.prototype = Type.prototype; + instance = new Constructor(); + return invoke(Type, instance, locals) || instance; + } + + return { + invoke: invoke, + instantiate: instantiate, + get: getService + }; } } |
