Skip to content

Commit

Permalink
fix($rootScope): TTL exception does not clear $$phase
Browse files Browse the repository at this point in the history
When $digest() throws infinite digest exception it
does not properly clear the $phase leaving the scope
in an inconsistent state.

Closes angular#979
  • Loading branch information
mhevery committed May 23, 2012
1 parent 5214c1d commit 989446e
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 12 deletions.
34 changes: 22 additions & 12 deletions src/ng/rootScope.js
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ function $RootScopeProvider(){
watchLog = [],
logIdx, logMsg;

flagPhase(target, '$digest');
beginPhase('$digest');

do {
dirty = false;
Expand Down Expand Up @@ -429,12 +429,13 @@ function $RootScopeProvider(){
} while ((current = next));

if(dirty && !(ttl--)) {
clearPhase();
throw Error(TTL + ' $digest() iterations reached. Aborting!\n' +
'Watchers fired in the last 5 iterations: ' + toJson(watchLog));
}
} while (dirty || asyncQueue.length);

this.$root.$$phase = null;
clearPhase();
},


Expand Down Expand Up @@ -469,7 +470,7 @@ function $RootScopeProvider(){
* perform any necessary cleanup.
*/
$destroy: function() {
if (this.$root == this) return; // we can't remove the root node;
if ($rootScope == this) return; // we can't remove the root node;
var parent = this.$parent;

this.$broadcast('$destroy');
Expand Down Expand Up @@ -586,13 +587,18 @@ function $RootScopeProvider(){
*/
$apply: function(expr) {
try {
flagPhase(this, '$apply');
beginPhase('$apply');
return this.$eval(expr);
} catch (e) {
$exceptionHandler(e);
} finally {
this.$root.$$phase = null;
this.$root.$digest();
clearPhase();
try {
$rootScope.$digest();
} catch (e) {
$exceptionHandler(e);
throw e;
}
}
},

Expand Down Expand Up @@ -754,18 +760,22 @@ function $RootScopeProvider(){
}
};

var $rootScope = new Scope();

return $rootScope;

function flagPhase(scope, phase) {
var root = scope.$root;

if (root.$$phase) {
throw Error(root.$$phase + ' already in progress');
function beginPhase(phase) {
if ($rootScope.$$phase) {
throw Error($rootScope.$$phase + ' already in progress');
}

root.$$phase = phase;
$rootScope.$$phase = phase;
}

return new Scope();
function clearPhase() {
$rootScope.$$phase = null;
}

function compileToFn(exp, name) {
var fn = $parse(exp);
Expand Down
23 changes: 23 additions & 0 deletions test/ng/rootScopeSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,8 @@ describe('Scope', function() {
'["a; newVal: 98; oldVal: 97","b; newVal: 99; oldVal: 98"],' +
'["a; newVal: 99; oldVal: 98","b; newVal: 100; oldVal: 99"],' +
'["a; newVal: 100; oldVal: 99","b; newVal: 101; oldVal: 100"]]');

expect($rootScope.$$phase).toBeNull();
});
});

Expand Down Expand Up @@ -492,6 +494,27 @@ describe('Scope', function() {
});


it('should log exceptions from $digest', function() {
module(function($rootScopeProvider, $exceptionHandlerProvider) {
$rootScopeProvider.digestTtl(2);
$exceptionHandlerProvider.mode('log');
});
inject(function($rootScope, $exceptionHandler) {
$rootScope.$watch('a', function() {$rootScope.b++;});
$rootScope.$watch('b', function() {$rootScope.a++;});
$rootScope.a = $rootScope.b = 0;

expect(function() {
$rootScope.$apply();
}).toThrow();

expect($exceptionHandler.errors[0]).toBeDefined();

expect($rootScope.$$phase).toBeNull();
});
});


describe('exceptions', function() {
var log;
beforeEach(module(function($exceptionHandlerProvider) {
Expand Down

0 comments on commit 989446e

Please sign in to comment.