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

Commit c900f6f

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 c900f6f

File tree

4 files changed

+125
-12
lines changed

4 files changed

+125
-12
lines changed

src/ng/parse.js

+28-5
Original file line numberDiff line numberDiff line change
@@ -1746,6 +1746,7 @@ function getValueOf(value) {
17461746
function $ParseProvider() {
17471747
var cacheDefault = createMap();
17481748
var cacheExpensive = createMap();
1749+
var expensiveCheckInterceptorFunctions = [];
17491750

17501751
this.$get = ['$filter', function($filter) {
17511752
var noUnsafeEval = csp().noUnsafeEval;
@@ -1757,10 +1758,19 @@ function $ParseProvider() {
17571758
csp: noUnsafeEval,
17581759
expensiveChecks: true
17591760
};
1761+
var runningChecksEnabled = false;
17601762

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

1772+
expensiveChecks = expensiveChecks || runningChecksEnabled;
1773+
17641774
switch (typeof exp) {
17651775
case 'string':
17661776
exp = exp.trim();
@@ -1788,15 +1798,28 @@ function $ParseProvider() {
17881798
}
17891799
cache[cacheKey] = parsedExpression;
17901800
}
1791-
return addInterceptor(parsedExpression, interceptorFn);
1801+
return expensiveChecksInterceptor(addInterceptor(parsedExpression, interceptorFn), expensiveChecks);
17921802

17931803
case 'function':
1794-
return addInterceptor(exp, interceptorFn);
1804+
return expensiveChecksInterceptor(addInterceptor(exp, interceptorFn), expensiveChecks);
17951805

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

18011824
function expressionInputDirtyCheck(newValue, oldValueOfValue) {
18021825

src/ng/rootScope.js

+4-4
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

+90
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

+3-3
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)