perf($parse): remove getterFn wrapper for internal use

Closes #8901
This commit is contained in:
Jason Bedard
2014-09-02 22:13:51 -07:00
committed by Igor Minar
parent 456dcb020b
commit b3b476db7d
2 changed files with 43 additions and 29 deletions

View File

@@ -80,12 +80,21 @@ function ensureSafeFunction(obj, fullExpression) {
} }
} }
//Keyword constants
var CONSTANTS = createMap();
forEach({
'null': function() { return null; },
'true': function() { return true; },
'false': function() { return false; },
'undefined': function() {}
}, function(constantGetter, name) {
constantGetter.constant = constantGetter.literal = constantGetter.sharedGetter = true;
CONSTANTS[name] = constantGetter;
});
//Operators - will be wrapped by binaryFn/unaryFn/assignment/filter
var OPERATORS = extend(createMap(), { var OPERATORS = extend(createMap(), {
/* jshint bitwise : false */ /* jshint bitwise : false */
'null':function(){return null;},
'true':function(){return true;},
'false':function(){return false;},
undefined:noop,
'+':function(self, locals, a,b){ '+':function(self, locals, a,b){
a=a(self, locals); b=b(self, locals); a=a(self, locals); b=b(self, locals);
if (isDefined(a)) { if (isDefined(a)) {
@@ -305,30 +314,11 @@ Lexer.prototype = {
} }
} }
this.tokens.push({
var token = {
index: start, index: start,
text: ident text: ident,
}; fn: CONSTANTS[ident] || getterFn(ident, this.options, expression)
});
var fn = OPERATORS[ident];
if (fn) {
token.fn = fn;
token.constant = true;
} else {
var getter = getterFn(ident, this.options, expression);
// TODO(perf): consider exposing the getter reference
token.fn = extend(function $parsePathGetter(self, locals) {
return getter(self, locals);
}, {
assign: function(self, value) {
return setter(self, ident, value, expression);
}
});
}
this.tokens.push(token);
if (methodName) { if (methodName) {
this.tokens.push({ this.tokens.push({
@@ -397,6 +387,7 @@ var Parser = function (lexer, $filter, options) {
Parser.ZERO = extend(function () { Parser.ZERO = extend(function () {
return 0; return 0;
}, { }, {
sharedGetter: true,
constant: true constant: true
}); });
@@ -935,9 +926,14 @@ function getterFn(path, options, fullExp) {
var evaledFnGetter = new Function('s', 'l', code); // s=scope, l=locals var evaledFnGetter = new Function('s', 'l', code); // s=scope, l=locals
/* jshint +W054 */ /* jshint +W054 */
evaledFnGetter.toString = valueFn(code); evaledFnGetter.toString = valueFn(code);
evaledFnGetter.assign = function(self, value) {
return setter(self, path, value, path);
};
fn = evaledFnGetter; fn = evaledFnGetter;
} }
fn.sharedGetter = true;
getterFnCache[path] = fn; getterFnCache[path] = fn;
return fn; return fn;
} }
@@ -1004,6 +1000,21 @@ function $ParseProvider() {
this.$get = ['$filter', '$sniffer', function($filter, $sniffer) { this.$get = ['$filter', '$sniffer', function($filter, $sniffer) {
$parseOptions.csp = $sniffer.csp; $parseOptions.csp = $sniffer.csp;
function wrapSharedExpression(exp) {
var wrapped = exp;
if (exp.sharedGetter) {
wrapped = function $parseWrapper(self, locals) {
return exp(self, locals);
};
wrapped.literal = exp.literal;
wrapped.constant = exp.constant;
wrapped.assign = exp.assign;
}
return wrapped;
}
return function $parse(exp, interceptorFn) { return function $parse(exp, interceptorFn) {
var parsedExpression, oneTime, cacheKey; var parsedExpression, oneTime, cacheKey;
@@ -1026,6 +1037,9 @@ function $ParseProvider() {
if (parsedExpression.constant) { if (parsedExpression.constant) {
parsedExpression.$$watchDelegate = constantWatchDelegate; parsedExpression.$$watchDelegate = constantWatchDelegate;
} else if (oneTime) { } else if (oneTime) {
//oneTime is not part of the exp passed to the Parser so we may have to
//wrap the parsedExpression before adding a $$watchDelegate
parsedExpression = wrapSharedExpression(parsedExpression);
parsedExpression.$$watchDelegate = parsedExpression.literal ? parsedExpression.$$watchDelegate = parsedExpression.literal ?
oneTimeLiteralWatchDelegate : oneTimeWatchDelegate; oneTimeLiteralWatchDelegate : oneTimeWatchDelegate;
} }

View File

@@ -722,7 +722,7 @@ describe('parser', function() {
scope.$eval('a.toString.constructor = 1', scope); scope.$eval('a.toString.constructor = 1', scope);
}).toThrowMinErr( }).toThrowMinErr(
'$parse', 'isecfn', 'Referencing Function in Angular expressions is disallowed! ' + '$parse', 'isecfn', 'Referencing Function in Angular expressions is disallowed! ' +
'Expression: a.toString.constructor = 1'); 'Expression: a.toString.constructor');
}); });
it('should disallow traversing the Function object in a setter: E02', function() { it('should disallow traversing the Function object in a setter: E02', function() {
@@ -733,7 +733,7 @@ describe('parser', function() {
scope.$eval('hasOwnProperty.constructor.prototype.valueOf = 1'); scope.$eval('hasOwnProperty.constructor.prototype.valueOf = 1');
}).toThrowMinErr( }).toThrowMinErr(
'$parse', 'isecfn', 'Referencing Function in Angular expressions is disallowed! ' + '$parse', 'isecfn', 'Referencing Function in Angular expressions is disallowed! ' +
'Expression: hasOwnProperty.constructor.prototype.valueOf = 1'); 'Expression: hasOwnProperty.constructor.prototype.valueOf');
}); });
it('should disallow passing the Function object as a parameter: E03', function() { it('should disallow passing the Function object as a parameter: E03', function() {