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(), {
/* jshint bitwise : false */
'null':function(){return null;},
'true':function(){return true;},
'false':function(){return false;},
undefined:noop,
'+':function(self, locals, a,b){
a=a(self, locals); b=b(self, locals);
if (isDefined(a)) {
@@ -305,30 +314,11 @@ Lexer.prototype = {
}
}
var token = {
this.tokens.push({
index: start,
text: ident
};
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);
text: ident,
fn: CONSTANTS[ident] || getterFn(ident, this.options, expression)
});
if (methodName) {
this.tokens.push({
@@ -397,6 +387,7 @@ var Parser = function (lexer, $filter, options) {
Parser.ZERO = extend(function () {
return 0;
}, {
sharedGetter: true,
constant: true
});
@@ -935,9 +926,14 @@ function getterFn(path, options, fullExp) {
var evaledFnGetter = new Function('s', 'l', code); // s=scope, l=locals
/* jshint +W054 */
evaledFnGetter.toString = valueFn(code);
evaledFnGetter.assign = function(self, value) {
return setter(self, path, value, path);
};
fn = evaledFnGetter;
}
fn.sharedGetter = true;
getterFnCache[path] = fn;
return fn;
}
@@ -1004,6 +1000,21 @@ function $ParseProvider() {
this.$get = ['$filter', '$sniffer', function($filter, $sniffer) {
$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) {
var parsedExpression, oneTime, cacheKey;
@@ -1026,6 +1037,9 @@ function $ParseProvider() {
if (parsedExpression.constant) {
parsedExpression.$$watchDelegate = constantWatchDelegate;
} 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 ?
oneTimeLiteralWatchDelegate : oneTimeWatchDelegate;
}

View File

@@ -722,7 +722,7 @@ describe('parser', function() {
scope.$eval('a.toString.constructor = 1', scope);
}).toThrowMinErr(
'$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() {
@@ -733,7 +733,7 @@ describe('parser', function() {
scope.$eval('hasOwnProperty.constructor.prototype.valueOf = 1');
}).toThrowMinErr(
'$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() {