diff --git a/docs/content/error/$injector/itkn.ngdoc b/docs/content/error/$injector/itkn.ngdoc
deleted file mode 100644
index dcae73b9b57b..000000000000
--- a/docs/content/error/$injector/itkn.ngdoc
+++ /dev/null
@@ -1,26 +0,0 @@
-@ngdoc error
-@name $injector:itkn
-@fullName Bad Injection Token
-@description
-
-This error occurs when using a bad token as a dependency injection annotation.
-Dependency injection annotation tokens should always be strings. Using any other
-type will cause this error to be thrown.
-
-Examples of code with bad injection tokens include:
-
-```
-var myCtrl = function ($scope, $http) { /* ... */ };
-myCtrl.$inject = ['$scope', 42];
-
-myAppModule.controller('MyCtrl', ['$scope', {}, function ($scope, $timeout) {
-  // ...
-}]);
-```
-
-The bad injection tokens are `42` in the first example and `{}` in the second.
-To avoid the error, always use string literals for dependency injection annotation
-tokens.
-
-For an explanation of what injection annotations are and how to use them, refer
-to the {@link guide/di Dependency Injection Guide}.
diff --git a/src/apis.js b/src/apis.js
index d9bd34f90de5..43273c204f99 100644
--- a/src/apis.js
+++ b/src/apis.js
@@ -36,14 +36,19 @@ function hashKey(obj, nextUidFn) {
 /**
  * HashMap which can use objects as keys
  */
-function HashMap(array, isolatedUid) {
+function HashMap(seedData, isolatedUid) {
   if (isolatedUid) {
     var uid = 0;
     this.nextUid = function() {
       return ++uid;
     };
   }
-  forEach(array, this.put, this);
+
+  if (seedData) {
+    var putFn = isArray(seedData) ?
+        this.put : reverseParams(this.put.bind(this));
+    forEach(seedData, putFn, this);
+  }
 }
 HashMap.prototype = {
   /**
@@ -63,6 +68,14 @@ HashMap.prototype = {
     return this[hashKey(key, this.nextUid)];
   },
 
+  /**
+   * @param key
+   * @returns {boolean} whether a value is stored under the specified key
+   */
+  has: function(key) {
+    return this.hasOwnProperty(hashKey(key, this.nextUid));
+  },
+
   /**
    * Remove the key/value pair
    * @param key
diff --git a/src/auto/injector.js b/src/auto/injector.js
index 352561afa4d9..b558ee1e53a0 100644
--- a/src/auto/injector.js
+++ b/src/auto/injector.js
@@ -63,6 +63,7 @@
  * Implicit module which gets automatically added to each {@link auto.$injector $injector}.
  */
 
+var PROVIDER_ID_SUFFIX = 'Provider';
 var ARROW_ARG = /^([^\(]+?)=>/;
 var FN_ARGS = /^[^\(]*\(\s*([^\)]*)\)/m;
 var FN_ARG_SPLIT = /,/;
@@ -74,7 +75,7 @@ function stringifyFn(fn) {
   // Support: Chrome 50-51 only
   // Creating a new string by adding `' '` at the end, to hack around some bug in Chrome v50/51
   // (See https://github.com/angular/angular.js/issues/14487.)
-  // TODO (gkalpak): Remove workaround when Chrome v52 is released
+  // TODO(gkalpak): Remove workaround when Chrome v52 is released
   return Function.prototype.toString.call(fn) + ' ';
 }
 
@@ -129,6 +130,14 @@ function annotate(fn, strictDi, name) {
   return $inject;
 }
 
+function getProviderId(id) {
+  return !isString(id) ? id : id + PROVIDER_ID_SUFFIX;
+}
+
+function stringifyIdForError(id, suffix) {
+  return isString(id) ? id : id + suffix;
+}
+
 ///////////////////////////////////////
 
 /**
@@ -647,36 +656,33 @@ function annotate(fn, strictDi, name) {
 function createInjector(modulesToLoad, strictDi) {
   strictDi = (strictDi === true);
   var INSTANTIATING = {},
-      providerSuffix = 'Provider',
       path = [],
-      loadedModules = new HashMap([], true),
-      providerCache = {
+      loadedModules = new HashMap(null, true),
+      providerCache = new HashMap({
         $provide: {
-            provider: supportObject(provider),
-            factory: supportObject(factory),
-            service: supportObject(service),
-            value: supportObject(value),
-            constant: supportObject(constant),
-            decorator: decorator
-          }
-      },
-      providerInjector = (providerCache.$injector =
-          createInternalInjector(providerCache, function(serviceName, caller) {
-            if (angular.isString(caller)) {
-              path.push(caller);
-            }
+          provider: supportObject(provider),
+          factory: supportObject(factory),
+          service: supportObject(service),
+          value: supportObject(value),
+          constant: supportObject(constant),
+          decorator: decorator
+        }
+      }),
+      instanceCache = new HashMap(),
+      providerInjector =
+          createInternalInjector(providerCache, function(serviceName) {
             throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
-          })),
-      instanceCache = {},
+          }, ' (provider)'),
       protoInstanceInjector =
-          createInternalInjector(instanceCache, function(serviceName, caller) {
-            var provider = providerInjector.get(serviceName + providerSuffix, caller);
-            return instanceInjector.invoke(
-                provider.$get, provider, undefined, serviceName);
+          createInternalInjector(instanceCache, function(serviceName) {
+            var provider = providerInjector.get(getProviderId(serviceName));
+            return instanceInjector.invoke(provider.$get, provider);
           }),
       instanceInjector = protoInstanceInjector;
 
-  providerCache['$injector' + providerSuffix] = { $get: valueFn(protoInstanceInjector) };
+  providerCache.put('$injector', providerInjector);
+  providerCache.put(getProviderId('$injector'), {$get: valueFn(protoInstanceInjector)});
+
   var runBlocks = loadModules(modulesToLoad);
   instanceInjector = protoInstanceInjector.get('$injector');
   instanceInjector.strictDi = strictDi;
@@ -690,7 +696,7 @@ function createInjector(modulesToLoad, strictDi) {
 
   function supportObject(delegate) {
     return function(key, value) {
-      if (isObject(key)) {
+      if ((arguments.length === 1) && isObject(key)) {
         forEach(key, reverseParams(delegate));
       } else {
         return delegate(key, value);
@@ -706,7 +712,10 @@ function createInjector(modulesToLoad, strictDi) {
     if (!provider_.$get) {
       throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);
     }
-    return (providerCache[name + providerSuffix] = provider_);
+
+    providerCache.put(getProviderId(name), provider_);
+
+    return provider_;
   }
 
   function enforceReturnValue(name, factory) {
@@ -731,16 +740,18 @@ function createInjector(modulesToLoad, strictDi) {
     }]);
   }
 
-  function value(name, val) { return factory(name, valueFn(val), false); }
+  function value(name, val) {
+    return factory(name, valueFn(val), false);
+  }
 
   function constant(name, value) {
     assertNotHasOwnProperty(name, 'constant');
-    providerCache[name] = value;
-    instanceCache[name] = value;
+    providerCache.put(name, value);
+    instanceCache.put(name, value);
   }
 
   function decorator(serviceName, decorFn) {
-    var origProvider = providerInjector.get(serviceName + providerSuffix),
+    var origProvider = providerInjector.get(getProviderId(serviceName)),
         orig$get = origProvider.$get;
 
     origProvider.$get = function() {
@@ -775,9 +786,7 @@ function createInjector(modulesToLoad, strictDi) {
           runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
           runInvokeQueue(moduleFn._invokeQueue);
           runInvokeQueue(moduleFn._configBlocks);
-        } else if (isFunction(module)) {
-            runBlocks.push(providerInjector.invoke(module));
-        } else if (isArray(module)) {
+        } else if (isFunction(module) || isArray(module)) {
             runBlocks.push(providerInjector.invoke(module));
         } else {
           assertArgFn(module, 'module');
@@ -805,29 +814,45 @@ function createInjector(modulesToLoad, strictDi) {
   // internal Injector
   ////////////////////////////////////
 
-  function createInternalInjector(cache, factory) {
+  function createInternalInjector(cache, factory, displayNameSuffix) {
+    if (!isString(displayNameSuffix)) {
+      displayNameSuffix = '';
+    }
 
     function getService(serviceName, caller) {
-      if (cache.hasOwnProperty(serviceName)) {
-        if (cache[serviceName] === INSTANTIATING) {
-          throw $injectorMinErr('cdep', 'Circular dependency found: {0}',
-                    serviceName + ' <- ' + path.join(' <- '));
-        }
-        return cache[serviceName];
-      } else {
-        try {
-          path.unshift(serviceName);
-          cache[serviceName] = INSTANTIATING;
-          cache[serviceName] = factory(serviceName, caller);
-          return cache[serviceName];
-        } catch (err) {
-          if (cache[serviceName] === INSTANTIATING) {
-            delete cache[serviceName];
+      var hasCaller = isDefined(caller);
+      var hadInstance = cache.has(serviceName);
+      var instance;
+
+      if (hasCaller) {
+        path.unshift(stringifyIdForError(caller, displayNameSuffix));
+      }
+      path.unshift(stringifyIdForError(serviceName, displayNameSuffix));
+
+      try {
+        if (hadInstance) {
+          instance = cache.get(serviceName);
+
+          if (instance === INSTANTIATING) {
+            throw $injectorMinErr('cdep', 'Circular dependency found: {0}', path.join(' <- '));
           }
-          throw err;
-        } finally {
-          path.shift();
+
+          return instance;
+        } else {
+          cache.put(serviceName, INSTANTIATING);
+
+          instance = factory(serviceName);
+          cache.put(serviceName, instance);
+
+          return instance;
         }
+      } finally {
+        if (!hadInstance && (cache.get(serviceName) === INSTANTIATING)) {
+          cache.remove(serviceName);
+        }
+
+        path.shift();
+        if (hasCaller) path.shift();
       }
     }
 
@@ -838,12 +863,8 @@ function createInjector(modulesToLoad, strictDi) {
 
       for (var i = 0, length = $inject.length; i < length; i++) {
         var key = $inject[i];
-        if (typeof key !== 'string') {
-          throw $injectorMinErr('itkn',
-                  'Incorrect injection token! Expected service name as string, got {0}', key);
-        }
-        args.push(locals && locals.hasOwnProperty(key) ? locals[key] :
-                                                         getService(key, serviceName));
+        var localsHasKey = locals && isString(key) && locals.hasOwnProperty(key);
+        args.push(localsHasKey ? locals[key] : getService(key, serviceName));
       }
       return args;
     }
@@ -901,7 +922,7 @@ function createInjector(modulesToLoad, strictDi) {
       get: getService,
       annotate: createInjector.$$annotate,
       has: function(name) {
-        return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
+        return cache.has(name) || providerCache.has(getProviderId(name));
       }
     };
   }
diff --git a/src/ng/directive/select.js b/src/ng/directive/select.js
index dc0621de60b0..6a4150806f65 100644
--- a/src/ng/directive/select.js
+++ b/src/ng/directive/select.js
@@ -563,8 +563,11 @@ var selectDirective = function() {
         // Write value now needs to set the selected property of each matching option
         selectCtrl.writeValue = function writeMultipleValue(value) {
           var items = new HashMap(value);
+          var selectValueMap = selectCtrl.selectValueMap;
+
           forEach(element.find('option'), function(option) {
-            option.selected = isDefined(items.get(option.value)) || isDefined(items.get(selectCtrl.selectValueMap[option.value]));
+            var value = option.value;
+            option.selected = items.has(value) || items.has(selectValueMap[value]);
           });
         };
 
diff --git a/test/ApiSpecs.js b/test/ApiSpecs.js
index 349dfe17e662..0fb237acc9e1 100644
--- a/test/ApiSpecs.js
+++ b/test/ApiSpecs.js
@@ -8,11 +8,16 @@ describe('api', function() {
       var key = {};
       var value1 = {};
       var value2 = {};
+
       map.put(key, value1);
       map.put(key, value2);
+
+      expect(map.has(key)).toBe(true);
+      expect(map.has({})).toBe(false);
       expect(map.get(key)).toBe(value2);
       expect(map.get({})).toBeUndefined();
       expect(map.remove(key)).toBe(value2);
+      expect(map.has(key)).toBe(false);
       expect(map.get(key)).toBeUndefined();
     });
 
@@ -23,6 +28,13 @@ describe('api', function() {
       expect(map.get('c')).toBeUndefined();
     });
 
+    it('should init from an object', function() {
+      var map = new HashMap({a: 'foo', b: 'bar'});
+      expect(map.get('a')).toBe('foo');
+      expect(map.get('b')).toBe('bar');
+      expect(map.get('c')).toBeUndefined();
+    });
+
     it('should maintain hashKey for object keys', function() {
       var map = new HashMap();
       var key = {};
diff --git a/test/auto/injectorSpec.js b/test/auto/injectorSpec.js
index c0760aecb687..ff483e25ea40 100644
--- a/test/auto/injectorSpec.js
+++ b/test/auto/injectorSpec.js
@@ -46,13 +46,13 @@ describe('injector', function() {
   it('should resolve dependency graph and instantiate all services just once', function() {
     var log = [];
 
-//          s1
-//        /  | \
-//       /  s2  \
-//      /  / | \ \
-//     /s3 < s4 > s5
-//    //
-//   s6
+    //        ____ s1 _
+    //      /      |   \
+    //     /   __ s2 __ \
+    //    /  /    |    \ \
+    //   | s3 <- s4 --> s5
+    //   | /
+    //   s6
 
 
     providers('s1', function() { log.push('s1'); return {}; }, {$inject: ['s2', 's5', 's6']});
@@ -80,14 +80,14 @@ describe('injector', function() {
   it('should provide useful message if no provider', function() {
     expect(function() {
       injector.get('idontexist');
-    }).toThrowMinErr("$injector", "unpr", "Unknown provider: idontexistProvider <- idontexist");
+    }).toThrowMinErr('$injector', 'unpr', 'Unknown provider: idontexistProvider <- idontexist\n');
   });
 
 
   it('should provide the caller name if given', function() {
     expect(function() {
       injector.get('idontexist', 'callerName');
-    }).toThrowMinErr("$injector", "unpr", "Unknown provider: idontexistProvider <- idontexist <- callerName");
+    }).toThrowMinErr('$injector', 'unpr', 'Unknown provider: idontexistProvider <- idontexist <- callerName\n');
   });
 
 
@@ -96,18 +96,18 @@ describe('injector', function() {
     var $controller = injector.get('$controller');
     expect(function() {
       $controller('myCtrl', {$scope: {}});
-    }).toThrowMinErr("$injector", "unpr", "Unknown provider: idontexistProvider <- idontexist <- myCtrl");
+    }).toThrowMinErr('$injector', 'unpr', 'Unknown provider: idontexistProvider <- idontexist <- myCtrl\n');
   });
 
 
   it('should not corrupt the cache when an object fails to get instantiated', function() {
     expect(function() {
       injector.get('idontexist');
-    }).toThrowMinErr("$injector", "unpr", "Unknown provider: idontexistProvider <- idontexist");
+    }).toThrowMinErr('$injector', 'unpr', 'Unknown provider: idontexistProvider <- idontexist\n');
 
     expect(function() {
       injector.get('idontexist');
-    }).toThrowMinErr("$injector", "unpr", "Unknown provider: idontexistProvider <- idontexist");
+    }).toThrowMinErr('$injector', 'unpr', 'Unknown provider: idontexistProvider <- idontexist\n');
   });
 
 
@@ -116,7 +116,7 @@ describe('injector', function() {
     providers('b', function(a) {return 2;});
     expect(function() {
       injector.get('b');
-    }).toThrowMinErr("$injector", "unpr", "Unknown provider: idontexistProvider <- idontexist <- a <- b");
+    }).toThrowMinErr('$injector', 'unpr', 'Unknown provider: idontexistProvider <- idontexist <- a <- b\n');
   });
 
 
@@ -287,7 +287,7 @@ describe('injector', function() {
         });
 
         // Support: Chrome 50-51 only
-        // TODO (gkalpak): Remove when Chrome v52 is released.
+        // TODO(gkalpak): Remove when Chrome v52 is released.
         // it('should be able to inject fat-arrow function', function() {
         //   inject(($injector) => {
         //     expect($injector).toBeDefined();
@@ -327,7 +327,7 @@ describe('injector', function() {
         }
 
         // Support: Chrome 50-51 only
-        // TODO (gkalpak): Remove when Chrome v52 is released.
+        // TODO(gkalpak): Remove when Chrome v52 is released.
         // it('should be able to invoke classes', function() {
         //   class Test {
         //     constructor($injector) {
@@ -800,7 +800,7 @@ describe('injector', function() {
         expect(function() {
           createInjector(['TestModule']);
         }).toThrowMinErr(
-          '$injector', 'modulerr', /Failed to instantiate module TestModule due to:\n.*\[\$injector:unpr] Unknown provider: xyzzy/
+          '$injector', 'modulerr', /Failed to instantiate module TestModule due to:\n.*\[\$injector:unpr] Unknown provider: xyzzy\n/
         );
       });
 
@@ -810,7 +810,7 @@ describe('injector', function() {
         expect(function() {
           createInjector([myModule]);
         }).toThrowMinErr(
-          '$injector', 'modulerr', /Failed to instantiate module function myModule\(xyzzy\) due to:\n.*\[\$injector:unpr] Unknown provider: xyzzy/
+          '$injector', 'modulerr', /Failed to instantiate module function myModule\(xyzzy\) due to:\n.*\[\$injector:unpr] Unknown provider: xyzzy\n/
         );
       });
 
@@ -820,7 +820,7 @@ describe('injector', function() {
         expect(function() {
           createInjector([['xyzzy', myModule]]);
         }).toThrowMinErr(
-          '$injector', 'modulerr', /Failed to instantiate module function myModule\(xyzzy\) due to:\n.*\[\$injector:unpr] Unknown provider: xyzzy/
+          '$injector', 'modulerr', /Failed to instantiate module function myModule\(xyzzy\) due to:\n.*\[\$injector:unpr] Unknown provider: xyzzy\n/
         );
       });
 
@@ -831,7 +831,7 @@ describe('injector', function() {
             $provide.factory('service', function(service) {});
             return function(service) {};
           }]);
-        }).toThrowMinErr('$injector', 'cdep', 'Circular dependency found: service <- service');
+        }).toThrowMinErr('$injector', 'cdep', 'Circular dependency found: service <- service\n');
       });
 
 
@@ -842,7 +842,7 @@ describe('injector', function() {
             $provide.factory('b', function(a) {});
             return function(a) {};
           }]);
-        }).toThrowMinErr('$injector', 'cdep', 'Circular dependency found: a <- b <- a');
+        }).toThrowMinErr('$injector', 'cdep', 'Circular dependency found: a <- b <- a\n');
       });
 
     });
@@ -884,7 +884,6 @@ describe('injector', function() {
       var $injector = createInjectorWithValue('instance', instance);
       expect($injector.invoke(function(instance) { return instance; })).toBe(instance);
     });
-
   });
 
 
@@ -1039,20 +1038,20 @@ describe('injector', function() {
 
   describe('protection modes', function() {
     it('should prevent provider lookup in app', function() {
-      var  $injector = createInjector([function($provide) {
+      var $injector = createInjector([function($provide) {
         $provide.value('name', 'angular');
       }]);
       expect(function() {
         $injector.get('nameProvider');
-      }).toThrowMinErr("$injector", "unpr", "Unknown provider: nameProviderProvider <- nameProvider");
+      }).toThrowMinErr('$injector', 'unpr', 'Unknown provider: nameProviderProvider <- nameProvider\n');
     });
 
 
     it('should prevent provider configuration in app', function() {
-      var  $injector = createInjector([]);
+      var $injector = createInjector([]);
       expect(function() {
         $injector.get('$provide').value('a', 'b');
-      }).toThrowMinErr("$injector", "unpr", "Unknown provider: $provideProvider <- $provide");
+      }).toThrowMinErr('$injector', 'unpr', 'Unknown provider: $provideProvider <- $provide\n');
     });
 
 
@@ -1062,7 +1061,7 @@ describe('injector', function() {
         createInjector([function($provide) {
           $provide.value('name', 'angular');
         }, instanceLookupInModule]);
-      }).toThrowError(/\[\$injector:unpr] Unknown provider: name/);
+      }).toThrowError(/\[\$injector:unpr] Unknown provider: name\n/);
     });
   });
 });
@@ -1072,11 +1071,18 @@ describe('strict-di injector', function() {
 
   describe('with ngMock', function() {
     it('should not throw when calling mock.module() with "magic" annotations', function() {
+      var executed = false;
+
       expect(function() {
         module(function($provide, $httpProvider, $compileProvider) {
           // Don't throw!
+          executed = true;
         });
       }).not.toThrow();
+
+      inject();
+
+      expect(executed).toBe(true);
     });
 
 
@@ -1162,3 +1168,190 @@ describe('strict-di injector', function() {
     expect($injector.strictDi).toBe(true);
   }));
 });
+
+describe('injector with non-string IDs', function() {
+  it('should support non-string service identifiers', function() {
+    var ids = [
+      null,
+      true,
+      1,
+      {},
+      [],
+      noop,
+      /./
+    ];
+
+    module(function($provide) {
+      ids.forEach(function(id, idx) { $provide.value(id, idx); });
+      $provide.factory('allDeps', ids.concat(function() { return sliceArgs(arguments); }));
+    });
+
+    inject(function(allDeps) {
+      expect(allDeps.length).toBe(ids.length);
+      expect(allDeps.every(function(dep, idx) { return dep === idx; })).toBe(true);
+    });
+  });
+
+
+  it('should support configuring services with non-string identifiers', function() {
+    /* eslint-disable no-new-wrappers */
+    var id1 = new String('same string, no problem');
+    var id2 = new String('same string, no problem');
+    /* eslint-enable */
+
+    angular.
+      module('test', []).
+      factory(id2, [id1, identity]).
+      provider(id1, function Id1Provider() {
+        var value = 'foo';
+        this.setValue = function setValue(newValue) { value = newValue; };
+        this.$get = function $get() { return value; };
+      }).
+      config([id1, function config(id1Provider) {
+        id1Provider.setValue('bar');
+      }]);
+
+    module('test');
+    inject([id2, function(dep2) {
+      expect(dep2).toBe('bar');
+    }]);
+  });
+
+
+  it('should support decorating services with non-string identifiers', function() {
+    /* eslint-disable no-new-wrappers */
+    var id1 = new String('same string, no problem');
+    var id2 = new String('same string, no problem');
+    /* eslint-enable */
+
+    angular.
+      module('test', []).
+      factory(id2, [id1, identity]).
+      value(id1, 'foo').
+      decorator(id1, function decorator($delegate) {
+        expect($delegate).toBe('foo');
+        return 'bar';
+      });
+
+    module('test');
+    inject([id2, function(dep2) {
+      expect(dep2).toBe('bar');
+    }]);
+  });
+
+
+  it('should still allow passing multiple providers as object', function() {
+    var obj = {
+      foo: 'foo',
+      bar: 'bar'
+    };
+
+    module(function($provide) {
+      $provide.value(obj);
+      $provide.value(obj, 'foo&bar');
+    });
+
+    inject(['foo', 'bar', obj, function(foo, bar, fooBar) {
+      expect(foo).toBe('foo');
+      expect(bar).toBe('bar');
+      expect(fooBar).toBe('foo&bar');
+    }]);
+  });
+
+
+  describe('should stringify non-string identifiers for error messages', function() {
+    var foo, bar, baz;
+
+    beforeEach(function() {
+      foo = {toString: valueFn('fooThingy')};
+      bar = {toString: valueFn('barThingy')};
+      baz = {toString: valueFn('bazThingy')};
+    });
+
+
+    it('(Unknown provider)', function() {
+      var executed = false;
+
+      module(function($provide) {
+        expect(function() {
+          $provide.provider('foo', ['barProvider', noop]);
+        }).toThrowMinErr('$injector', 'unpr', 'Unknown provider: barProvider\n');
+
+        expect(function() {
+          $provide.provider('foo', [{}, noop]);
+        }).toThrowMinErr('$injector', 'unpr', 'Unknown provider: [object Object] (provider)\n');
+
+        expect(function() {
+          $provide.provider('foo', [bar, noop]);
+        }).toThrowMinErr('$injector', 'unpr', 'Unknown provider: barThingy (provider)\n');
+
+        executed = true;
+      });
+
+      inject();
+
+      expect(executed).toBe(true);
+    });
+
+
+    it('(Unknown service)', function() {
+      var executed = false;
+
+      module(function($provide) {
+        $provide.provider(foo, valueFn({$get: ['bar', noop]}));
+        $provide.provider('bar', valueFn({$get: [baz, noop]}));
+
+        $provide.provider('foo', valueFn({$get: [bar, noop]}));
+        $provide.provider(bar, valueFn({$get: ['baz', noop]}));
+      });
+
+      inject(function($injector) {
+        var specs = [
+          ['bar', 'bazThingy (provider) <- bazThingy <- bar'],
+          [foo,   'bazThingy (provider) <- bazThingy <- bar <- fooThingy'],
+          [bar,   'bazProvider <- baz <- barThingy'],
+          ['foo', 'bazProvider <- baz <- barThingy <- foo']
+        ];
+
+        forEach(specs, function(spec) {
+          var serviceId = spec[0];
+          var errorPath = spec[1];
+
+          expect(function() {
+            $injector.get(serviceId);
+          }).toThrowMinErr('$injector', 'unpr', 'Unknown provider: ' + errorPath + '\n');
+        });
+
+        executed = true;
+      });
+
+      expect(executed).toBe(true);
+    });
+
+
+    it('(Circular dependency)', function() {
+      var executed = false;
+
+      module(function($provide) {
+        $provide.provider(foo, valueFn({$get: ['bar', noop]}));
+        $provide.provider('bar', valueFn({$get: [baz, noop]}));
+        $provide.provider(baz, valueFn({$get: ['foo', noop]}));
+        $provide.provider('foo', valueFn({$get: [bar, noop]}));
+        $provide.provider(bar, valueFn({$get: ['baz', noop]}));
+        $provide.provider('baz', valueFn({$get: [foo, noop]}));
+      });
+
+      inject(function($injector) {
+        var errorPath = 'fooThingy <- baz <- barThingy <- foo <- bazThingy <- bar <- fooThingy';
+
+        expect(function() {
+          $injector.get(foo);
+        }).toThrowMinErr('$injector', 'cdep', 'Circular dependency found: ' + errorPath + '\n');
+
+        executed = true;
+      });
+
+      expect(executed).toBe(true);
+    });
+  });
+});
diff --git a/test/ng/parseSpec.js b/test/ng/parseSpec.js
index ce4797d7d6b6..d76e106316d7 100644
--- a/test/ng/parseSpec.js
+++ b/test/ng/parseSpec.js
@@ -1994,7 +1994,7 @@ describe('parser', function() {
 
         expect(function() {
           scope.$eval("1|nonexistent");
-        }).toThrowMinErr('$injector', 'unpr', 'Unknown provider: nonexistentFilterProvider <- nonexistentFilter');
+        }).toThrowMinErr('$injector', 'unpr', 'Unknown provider: nonexistentFilterProvider <- nonexistentFilter\n');
 
         scope.offset =  3;
         expect(scope.$eval("'abcd'|substring:1:offset")).toEqual("bc");