Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 157e9e7

Browse files
committed
fix($parse): Preserve expensive checks when runnning $eval inside an expression
When running an expression with expensive checks, there is a call to `$eval` or `$evalAsync` then that expression is also evaluated using expensive checks
1 parent fabc6ab commit 157e9e7

File tree

4 files changed

+124
-12
lines changed

4 files changed

+124
-12
lines changed

src/ng/parse.js

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1757,10 +1757,19 @@ function $ParseProvider() {
17571757
csp: noUnsafeEval,
17581758
expensiveChecks: true
17591759
};
1760+
var runningChecksEnabled = false;
17601761

1761-
return function $parse(exp, interceptorFn, expensiveChecks) {
1762+
$parse.$$runningExpensiveChecks = function() {
1763+
return runningChecksEnabled;
1764+
};
1765+
1766+
return $parse;
1767+
1768+
function $parse(exp, interceptorFn, expensiveChecks) {
17621769
var parsedExpression, oneTime, cacheKey;
17631770

1771+
expensiveChecks = expensiveChecks || runningChecksEnabled;
1772+
17641773
switch (typeof exp) {
17651774
case 'string':
17661775
exp = exp.trim();
@@ -1788,15 +1797,28 @@ function $ParseProvider() {
17881797
}
17891798
cache[cacheKey] = parsedExpression;
17901799
}
1791-
return addInterceptor(parsedExpression, interceptorFn);
1800+
return expensiveChecksInterceptor(addInterceptor(parsedExpression, interceptorFn), expensiveChecks);
17921801

17931802
case 'function':
1794-
return addInterceptor(exp, interceptorFn);
1803+
return expensiveChecksInterceptor(addInterceptor(exp, interceptorFn), expensiveChecks);
17951804

17961805
default:
1797-
return addInterceptor(noop, interceptorFn);
1806+
return expensiveChecksInterceptor(addInterceptor(noop, interceptorFn), expensiveChecks);
17981807
}
1799-
};
1808+
}
1809+
1810+
function expensiveChecksInterceptor(fn, expensiveChecks) {
1811+
if (!expensiveChecks) return fn;
1812+
return function expensiveCheckFn(scope, locals, assign, inputs) {
1813+
var expensiveCheckOldValue = runningChecksEnabled;
1814+
runningChecksEnabled = true;
1815+
try {
1816+
fn(scope, locals, assign, inputs);
1817+
} finally {
1818+
runningChecksEnabled = expensiveCheckOldValue;
1819+
}
1820+
};
1821+
}
18001822

18011823
function expressionInputDirtyCheck(newValue, oldValueOfValue) {
18021824

src/ng/rootScope.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -772,7 +772,7 @@ function $RootScopeProvider() {
772772
while (asyncQueue.length) {
773773
try {
774774
asyncTask = asyncQueue.shift();
775-
asyncTask.scope.$eval(asyncTask.expression, asyncTask.locals);
775+
asyncTask.scope.$eval(asyncTask.expression, asyncTask.locals, asyncTask.expensiveChecks);
776776
} catch (e) {
777777
$exceptionHandler(e);
778778
}
@@ -953,8 +953,8 @@ function $RootScopeProvider() {
953953
* @param {(object)=} locals Local variables object, useful for overriding values in scope.
954954
* @returns {*} The result of evaluating the expression.
955955
*/
956-
$eval: function(expr, locals) {
957-
return $parse(expr)(this, locals);
956+
$eval: function(expr, locals, expensiveChecks) {
957+
return $parse(expr, null, expensiveChecks)(this, locals);
958958
},
959959

960960
/**
@@ -998,7 +998,7 @@ function $RootScopeProvider() {
998998
});
999999
}
10001000

1001-
asyncQueue.push({scope: this, expression: expr, locals: locals});
1001+
asyncQueue.push({scope: this, expression: expr, locals: locals, expensiveChecks: $parse.$$runningExpensiveChecks()});
10021002
},
10031003

10041004
$$postDigest: function(fn) {

test/ng/parseSpec.js

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2400,6 +2400,96 @@ describe('parser', function() {
24002400
'$parse', 'isecwindow', 'Referencing the Window in Angular expressions is disallowed! ' +
24012401
'Expression: foo.w = 1');
24022402
}));
2403+
2404+
it('should propagate expensive checks when calling `$eval` and `$evalAsync`', inject(function($parse, $window) {
2405+
scope.foo = {
2406+
w: $window,
2407+
bar: 'bar',
2408+
t: function() { throw new Error(); },
2409+
e: function() { scope.$eval("foo.w"); }
2410+
};
2411+
expect($parse.$$runningExpensiveChecks()).toEqual(false);
2412+
expect(function() {
2413+
scope.$eval($parse('foo.w', null, true));
2414+
}).toThrow();
2415+
expect(function() {
2416+
scope.$eval($parse('$eval("foo.w && true")', null, true));
2417+
}).toThrow();
2418+
expect(function() {
2419+
scope.$eval($parse('this["$eval"]("foo.w && true")', null, true));
2420+
}).toThrow();
2421+
expect(function() {
2422+
scope.$eval($parse('bar;$eval("foo.w && true")', null, true));
2423+
}).toThrow();
2424+
expect(function() {
2425+
scope.$eval($parse('$eval("foo.w && true");bar', null, true));
2426+
}).toThrow();
2427+
expect(function() {
2428+
scope.$eval($parse('$eval("foo.w && true", null, false)', null, true));
2429+
}).toThrow();
2430+
expect(function() {
2431+
scope.$eval($parse('$eval("foo");$eval("foo.w && true")', null, true));
2432+
}).toThrow();
2433+
expect(function() {
2434+
scope.$eval($parse('$eval("$eval(\"foo.w && true\")")', null, true));
2435+
}).toThrow();
2436+
expect(function() {
2437+
scope.$eval($parse('$eval("foo.t()")', null, true));
2438+
}).toThrow();
2439+
expect(function() {
2440+
scope.$eval($parse('$eval("foo.e()")', null, true));
2441+
}).toThrow();
2442+
expect($parse.$$runningExpensiveChecks()).toEqual(false);
2443+
2444+
expect(function() {
2445+
scope.$eval($parse('$evalAsync("foo.w && true")', null, true));
2446+
scope.$digest();
2447+
}).toThrow();
2448+
expect(function() {
2449+
scope.$eval($parse('this["$evalAsync"]("foo.w && true")', null, true));
2450+
scope.$digest();
2451+
}).toThrow();
2452+
expect(function() {
2453+
scope.$eval($parse('bar;$evalAsync("foo.w && true")', null, true));
2454+
scope.$digest();
2455+
}).toThrow();
2456+
expect(function() {
2457+
scope.$eval($parse('$evalAsync("foo.w && true");bar', null, true));
2458+
scope.$digest();
2459+
}).toThrow();
2460+
expect(function() {
2461+
scope.$eval($parse('$evalAsync("foo.w && true", null, false)', null, true));
2462+
scope.$digest();
2463+
}).toThrow();
2464+
expect(function() {
2465+
scope.$eval($parse('$evalAsync("foo");$evalAsync("foo.w && true")', null, true));
2466+
scope.$digest();
2467+
}).toThrow();
2468+
expect(function() {
2469+
scope.$eval($parse('$evalAsync("$evalAsync(\"foo.w && true\")")', null, true));
2470+
scope.$digest();
2471+
}).toThrow();
2472+
expect(function() {
2473+
scope.$eval($parse('$evalAsync("foo.t()", {foo: foo})', null, true));
2474+
scope.$digest();
2475+
}).toThrow();
2476+
expect(function() {
2477+
scope.$eval($parse('$evalAsync("foo.e()")', null, true));
2478+
scope.$digest();
2479+
}).toThrow();
2480+
expect($parse.$$runningExpensiveChecks()).toEqual(false);
2481+
2482+
2483+
expect(function() {
2484+
scope.$eval($parse('$evalAsync("$eval(\"foo.w && true\")")', null, true));
2485+
scope.$digest();
2486+
}).toThrow();
2487+
expect(function() {
2488+
scope.$eval($parse('$eval("$evalAsync(\"foo.w && true\")")', null, true));
2489+
scope.$digest();
2490+
}).toThrow();
2491+
expect($parse.$$runningExpensiveChecks()).toEqual(false);
2492+
}));
24032493
});
24042494
});
24052495

test/ng/rootScopeSpec.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1398,9 +1398,9 @@ describe('Scope', function() {
13981398
expect(childScope.$$asyncQueue).toBe($rootScope.$$asyncQueue);
13991399
expect(isolateScope.$$asyncQueue).toBeUndefined();
14001400
expect($rootScope.$$asyncQueue).toEqual([
1401-
{scope: $rootScope, expression: 'rootExpression'},
1402-
{scope: childScope, expression: 'childExpression'},
1403-
{scope: isolateScope, expression: 'isolateExpression'}
1401+
{scope: $rootScope, expression: 'rootExpression', expensiveChecks: false},
1402+
{scope: childScope, expression: 'childExpression', expensiveChecks: false},
1403+
{scope: isolateScope, expression: 'isolateExpression', expensiveChecks: false}
14041404
]);
14051405
}));
14061406

0 commit comments

Comments
 (0)