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

Commit b3b476d

Browse files
jbedardIgorMinar
authored andcommitted
perf($parse): remove getterFn wrapper for internal use
Closes #8901
1 parent 456dcb0 commit b3b476d

File tree

2 files changed

+43
-29
lines changed

2 files changed

+43
-29
lines changed

src/ng/parse.js

+41-27
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,21 @@ function ensureSafeFunction(obj, fullExpression) {
8080
}
8181
}
8282

83+
//Keyword constants
84+
var CONSTANTS = createMap();
85+
forEach({
86+
'null': function() { return null; },
87+
'true': function() { return true; },
88+
'false': function() { return false; },
89+
'undefined': function() {}
90+
}, function(constantGetter, name) {
91+
constantGetter.constant = constantGetter.literal = constantGetter.sharedGetter = true;
92+
CONSTANTS[name] = constantGetter;
93+
});
94+
95+
//Operators - will be wrapped by binaryFn/unaryFn/assignment/filter
8396
var OPERATORS = extend(createMap(), {
8497
/* jshint bitwise : false */
85-
'null':function(){return null;},
86-
'true':function(){return true;},
87-
'false':function(){return false;},
88-
undefined:noop,
8998
'+':function(self, locals, a,b){
9099
a=a(self, locals); b=b(self, locals);
91100
if (isDefined(a)) {
@@ -305,30 +314,11 @@ Lexer.prototype = {
305314
}
306315
}
307316

308-
309-
var token = {
317+
this.tokens.push({
310318
index: start,
311-
text: ident
312-
};
313-
314-
var fn = OPERATORS[ident];
315-
316-
if (fn) {
317-
token.fn = fn;
318-
token.constant = true;
319-
} else {
320-
var getter = getterFn(ident, this.options, expression);
321-
// TODO(perf): consider exposing the getter reference
322-
token.fn = extend(function $parsePathGetter(self, locals) {
323-
return getter(self, locals);
324-
}, {
325-
assign: function(self, value) {
326-
return setter(self, ident, value, expression);
327-
}
328-
});
329-
}
330-
331-
this.tokens.push(token);
319+
text: ident,
320+
fn: CONSTANTS[ident] || getterFn(ident, this.options, expression)
321+
});
332322

333323
if (methodName) {
334324
this.tokens.push({
@@ -397,6 +387,7 @@ var Parser = function (lexer, $filter, options) {
397387
Parser.ZERO = extend(function () {
398388
return 0;
399389
}, {
390+
sharedGetter: true,
400391
constant: true
401392
});
402393

@@ -935,9 +926,14 @@ function getterFn(path, options, fullExp) {
935926
var evaledFnGetter = new Function('s', 'l', code); // s=scope, l=locals
936927
/* jshint +W054 */
937928
evaledFnGetter.toString = valueFn(code);
929+
evaledFnGetter.assign = function(self, value) {
930+
return setter(self, path, value, path);
931+
};
932+
938933
fn = evaledFnGetter;
939934
}
940935

936+
fn.sharedGetter = true;
941937
getterFnCache[path] = fn;
942938
return fn;
943939
}
@@ -1004,6 +1000,21 @@ function $ParseProvider() {
10041000
this.$get = ['$filter', '$sniffer', function($filter, $sniffer) {
10051001
$parseOptions.csp = $sniffer.csp;
10061002

1003+
function wrapSharedExpression(exp) {
1004+
var wrapped = exp;
1005+
1006+
if (exp.sharedGetter) {
1007+
wrapped = function $parseWrapper(self, locals) {
1008+
return exp(self, locals);
1009+
};
1010+
wrapped.literal = exp.literal;
1011+
wrapped.constant = exp.constant;
1012+
wrapped.assign = exp.assign;
1013+
}
1014+
1015+
return wrapped;
1016+
}
1017+
10071018
return function $parse(exp, interceptorFn) {
10081019
var parsedExpression, oneTime, cacheKey;
10091020

@@ -1026,6 +1037,9 @@ function $ParseProvider() {
10261037
if (parsedExpression.constant) {
10271038
parsedExpression.$$watchDelegate = constantWatchDelegate;
10281039
} else if (oneTime) {
1040+
//oneTime is not part of the exp passed to the Parser so we may have to
1041+
//wrap the parsedExpression before adding a $$watchDelegate
1042+
parsedExpression = wrapSharedExpression(parsedExpression);
10291043
parsedExpression.$$watchDelegate = parsedExpression.literal ?
10301044
oneTimeLiteralWatchDelegate : oneTimeWatchDelegate;
10311045
}

test/ng/parseSpec.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -722,7 +722,7 @@ describe('parser', function() {
722722
scope.$eval('a.toString.constructor = 1', scope);
723723
}).toThrowMinErr(
724724
'$parse', 'isecfn', 'Referencing Function in Angular expressions is disallowed! ' +
725-
'Expression: a.toString.constructor = 1');
725+
'Expression: a.toString.constructor');
726726
});
727727

728728
it('should disallow traversing the Function object in a setter: E02', function() {
@@ -733,7 +733,7 @@ describe('parser', function() {
733733
scope.$eval('hasOwnProperty.constructor.prototype.valueOf = 1');
734734
}).toThrowMinErr(
735735
'$parse', 'isecfn', 'Referencing Function in Angular expressions is disallowed! ' +
736-
'Expression: hasOwnProperty.constructor.prototype.valueOf = 1');
736+
'Expression: hasOwnProperty.constructor.prototype.valueOf');
737737
});
738738

739739
it('should disallow passing the Function object as a parameter: E03', function() {

0 commit comments

Comments
 (0)