From b3b476db7d34bc2f8b099ab5b993b1e899b9cffd Mon Sep 17 00:00:00 2001 From: Jason Bedard Date: Tue, 2 Sep 2014 22:13:51 -0700 Subject: [PATCH] perf($parse): remove getterFn wrapper for internal use Closes #8901 --- src/ng/parse.js | 68 ++++++++++++++++++++++++++------------------ test/ng/parseSpec.js | 4 +-- 2 files changed, 43 insertions(+), 29 deletions(-) diff --git a/src/ng/parse.js b/src/ng/parse.js index bd5f558e..69e71e20 100644 --- a/src/ng/parse.js +++ b/src/ng/parse.js @@ -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; } diff --git a/test/ng/parseSpec.js b/test/ng/parseSpec.js index 1f98ff87..618149ce 100644 --- a/test/ng/parseSpec.js +++ b/test/ng/parseSpec.js @@ -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() {