@@ -42,19 +42,32 @@ var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
4242var FN_ARG_SPLIT = / , / ;
4343var FN_ARG = / ^ \s * ( _ ? ) ( .+ ?) \1\s * $ / ;
4444var STRIP_COMMENTS = / ( ( \/ \/ .* $ ) | ( \/ \* [ \s \S ] * ?\* \/ ) ) / mg;
45- function inferInjectionArgs ( fn ) {
46- assertArgFn ( fn ) ;
47- if ( ! fn . $inject ) {
48- var args = fn . $inject = [ ] ;
49- var fnText = fn . toString ( ) . replace ( STRIP_COMMENTS , '' ) ;
50- var argDecl = fnText . match ( FN_ARGS ) ;
51- forEach ( argDecl [ 1 ] . split ( FN_ARG_SPLIT ) , function ( arg ) {
52- arg . replace ( FN_ARG , function ( all , underscore , name ) {
53- args . push ( name ) ;
45+ function annotate ( fn ) {
46+ var $inject ,
47+ fnText ,
48+ argDecl ,
49+ last ;
50+
51+ if ( typeof fn == 'function' ) {
52+ if ( ! ( $inject = fn . $inject ) ) {
53+ $inject = [ ] ;
54+ fnText = fn . toString ( ) . replace ( STRIP_COMMENTS , '' ) ;
55+ argDecl = fnText . match ( FN_ARGS ) ;
56+ forEach ( argDecl [ 1 ] . split ( FN_ARG_SPLIT ) , function ( arg ) {
57+ arg . replace ( FN_ARG , function ( all , underscore , name ) {
58+ $inject . push ( name ) ;
59+ } ) ;
5460 } ) ;
55- } ) ;
61+ fn . $inject = $inject ;
62+ }
63+ } else if ( isArray ( fn ) ) {
64+ last = fn . length - 1 ;
65+ assertArgFn ( fn [ last ] , 'fn' )
66+ $inject = fn . slice ( 0 , last ) ;
67+ } else {
68+ assertArgFn ( fn , 'fn' , true ) ;
5669 }
57- return fn . $inject ;
70+ return $inject ;
5871}
5972
6073///////////////////////////////////////
@@ -152,6 +165,87 @@ function inferInjectionArgs(fn) {
152165 * @returns {Object } new instance of `Type`.
153166 */
154167
168+ /**
169+ * @ngdoc method
170+ * @name angular.module.AUTO.$injector#annotate
171+ * @methodOf angular.module.AUTO.$injector
172+ *
173+ * @description
174+ * Returns an array of service names which the function is requesting for injection. This API is used by the injector
175+ * to determine which services need to be injected into the function when the function is invoked. There are three
176+ * ways in which the function can be annotated with the needed dependencies.
177+ *
178+ * # Argument names
179+ *
180+ * The simplest form is to extract the dependencies from the arguments of the function. This is done by converting
181+ * the function into a string using `toString()` method and extracting the argument names.
182+ * <pre>
183+ * // Given
184+ * function MyController($scope, $route) {
185+ * // ...
186+ * }
187+ *
188+ * // Then
189+ * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
190+ * </pre>
191+ *
192+ * This method does not work with code minfication / obfuscation. For this reason the following annotation strategies
193+ * are supported.
194+ *
195+ * # The `$injector` property
196+ *
197+ * If a function has an `$inject` property and its value is an array of strings, then the strings represent names of
198+ * services to be injected into the function.
199+ * <pre>
200+ * // Given
201+ * var MyController = function(obfuscatedScope, obfuscatedRoute) {
202+ * // ...
203+ * }
204+ * // Define function dependencies
205+ * MyController.$inject = ['$scope', '$route'];
206+ *
207+ * // Then
208+ * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
209+ * </pre>
210+ *
211+ * # The array notation
212+ *
213+ * It is often desirable to inline Injected functions and that's when setting the `$inject` property is very
214+ * inconvenient. In these situations using the array notation to specify the dependencies in a way that survives
215+ * minification is a better choice:
216+ *
217+ * <pre>
218+ * // We wish to write this (not minification / obfuscation safe)
219+ * injector.invoke(function($compile, $rootScope) {
220+ * // ...
221+ * });
222+ *
223+ * // We are forced to write break inlining
224+ * var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {
225+ * // ...
226+ * };
227+ * tmpFn.$inject = ['$compile', '$rootScope'];
228+ * injector.invoke(tempFn);
229+ *
230+ * // To better support inline function the inline annotation is supported
231+ * injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
232+ * // ...
233+ * }]);
234+ *
235+ * // Therefore
236+ * expect(injector.annotate(
237+ * ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])
238+ * ).toEqual(['$compile', '$rootScope']);
239+ * </pre>
240+ *
241+ * @param {function|Array.<string|Function> } fn Function for which dependent service names need to be retrieved as described
242+ * above.
243+ *
244+ * @returns {Array.<string> } The names of the services which the function requires.
245+ */
246+
247+
248+
155249
156250/**
157251 * @ngdoc object
@@ -454,30 +548,23 @@ function createInjector(modulesToLoad) {
454548
455549 function invoke ( fn , self , locals ) {
456550 var args = [ ] ,
457- $inject ,
458- length ,
551+ $inject = annotate ( fn ) ,
552+ length , i ,
459553 key ;
460554
461- if ( typeof fn == 'function' ) {
462- $inject = inferInjectionArgs ( fn ) ;
463- length = $inject . length ;
464- } else {
465- if ( isArray ( fn ) ) {
466- $inject = fn ;
467- length = $inject . length - 1 ;
468- fn = $inject [ length ] ;
469- }
470- assertArgFn ( fn , 'fn' ) ;
471- }
472-
473- for ( var i = 0 ; i < length ; i ++ ) {
555+ for ( i = 0 , length = $inject . length ; i < length ; i ++ ) {
474556 key = $inject [ i ] ;
475557 args . push (
476558 locals && locals . hasOwnProperty ( key )
477559 ? locals [ key ]
478560 : getService ( key , path )
479561 ) ;
480562 }
563+ if ( ! fn . $inject ) {
564+ // this means that we must be an array.
565+ fn = fn [ length ] ;
566+ }
567+
481568
482569 // Performance optimization: http://jsperf.com/apply-vs-call-vs-invoke
483570 switch ( self ? - 1 : args . length ) {
@@ -510,7 +597,8 @@ function createInjector(modulesToLoad) {
510597 return {
511598 invoke : invoke ,
512599 instantiate : instantiate ,
513- get : getService
600+ get : getService ,
601+ annotate : annotate
514602 } ;
515603 }
516604}
0 commit comments