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

Commit 94b5c9f

Browse files
committed
perf($interpolate): do not keep empty separators
Do not keep empty separators and keep references to where each expression goes
1 parent 5b77e30 commit 94b5c9f

File tree

2 files changed

+23
-47
lines changed

2 files changed

+23
-47
lines changed

src/ng/interpolate.js

+16-27
Original file line numberDiff line numberDiff line change
@@ -188,68 +188,53 @@ function $InterpolateProvider() {
188188
var startIndex,
189189
endIndex,
190190
index = 0,
191-
separators = [],
192191
expressions = [],
193192
parseFns = [],
194193
textLength = text.length,
195-
hasInterpolation = false,
196-
hasText = false,
197194
exp,
198-
concat = [];
195+
concat = [],
196+
expressionPositions = [];
199197

200198
while(index < textLength) {
201199
if ( ((startIndex = text.indexOf(startSymbol, index)) != -1) &&
202200
((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1) ) {
203-
if (index !== startIndex) hasText = true;
204-
separators.push(text.substring(index, startIndex));
201+
if (index !== startIndex) {
202+
concat.push(unescapeText(text.substring(index, startIndex)));
203+
}
205204
exp = text.substring(startIndex + startSymbolLength, endIndex);
206205
expressions.push(exp);
207206
parseFns.push($parse(exp, parseStringifyInterceptor));
208207
index = endIndex + endSymbolLength;
209-
hasInterpolation = true;
208+
expressionPositions.push(concat.length);
209+
concat.push('');
210210
} else {
211211
// we did not find an interpolation, so we have to add the remainder to the separators array
212212
if (index !== textLength) {
213-
hasText = true;
214-
separators.push(text.substring(index));
213+
concat.push(unescapeText(text.substring(index)));
215214
}
216215
break;
217216
}
218217
}
219218

220-
forEach(separators, function(key, i) {
221-
separators[i] = separators[i].
222-
replace(escapedStartRegexp, startSymbol).
223-
replace(escapedEndRegexp, endSymbol);
224-
});
225-
226-
if (separators.length === expressions.length) {
227-
separators.push('');
228-
}
229-
230219
// Concatenating expressions makes it hard to reason about whether some combination of
231220
// concatenated values are unsafe to use and could easily lead to XSS. By requiring that a
232221
// single expression be used for iframe[src], object[src], etc., we ensure that the value
233222
// that's used is assigned or constructed by some JS code somewhere that is more testable or
234223
// make it obvious that you bound the value to some user controlled value. This helps reduce
235224
// the load when auditing for XSS issues.
236-
if (trustedContext && hasInterpolation && (hasText || expressions.length > 1)) {
225+
if (trustedContext && concat.length > 1) {
237226
throw $interpolateMinErr('noconcat',
238227
"Error while interpolating: {0}\nStrict Contextual Escaping disallows " +
239228
"interpolations that concatenate multiple expressions when a trusted value is " +
240229
"required. See http://docs.angularjs.org/api/ng.$sce", text);
241230
}
242231

243-
if (!mustHaveExpression || hasInterpolation) {
244-
concat.length = separators.length + expressions.length;
245-
232+
if (!mustHaveExpression || expressions.length) {
246233
var compute = function(values) {
247234
for(var i = 0, ii = expressions.length; i < ii; i++) {
248235
if (allOrNothing && isUndefined(values[i])) return;
249-
concat[2*i] = separators[i];
250-
concat[(2*i)+1] = values[i];
236+
concat[expressionPositions[i]] = values[i];
251237
}
252-
concat[2*ii] = separators[ii];
253238
return concat.join('');
254239
};
255240

@@ -299,7 +284,6 @@ function $InterpolateProvider() {
299284
}, {
300285
// all of these properties are undocumented for now
301286
exp: text, //just for compatibility with regular watchers created via $watch
302-
separators: separators,
303287
expressions: expressions,
304288
$$watchDelegate: function (scope, listener, objectEquality) {
305289
var lastValue;
@@ -314,6 +298,11 @@ function $InterpolateProvider() {
314298
});
315299
}
316300

301+
function unescapeText(text) {
302+
return text.replace(escapedStartRegexp, startSymbol).
303+
replace(escapedEndRegexp, endSymbol);
304+
}
305+
317306
function parseStringifyInterceptor(value) {
318307
try {
319308
return stringify(getValue(value));

test/ng/interpolateSpec.js

+7-20
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ describe('$interpolate', function() {
77
var interpolateFn = $interpolate('some text');
88

99
expect(interpolateFn.exp).toBe('some text');
10-
expect(interpolateFn.separators).toEqual(['some text']);
1110
expect(interpolateFn.expressions).toEqual([]);
1211

1312
expect(interpolateFn({})).toBe('some text');
@@ -41,7 +40,6 @@ describe('$interpolate', function() {
4140
var interpolateFn = $interpolate('Hello {{name}}!');
4241

4342
expect(interpolateFn.exp).toBe('Hello {{name}}!');
44-
expect(interpolateFn.separators).toEqual(['Hello ', '!']);
4543
expect(interpolateFn.expressions).toEqual(['name']);
4644

4745
var scope = $rootScope.$new();
@@ -185,7 +183,6 @@ describe('$interpolate', function() {
185183
}));
186184

187185
it('should not get confused with same markers', inject(function($interpolate) {
188-
expect($interpolate('---').separators).toEqual(['---']);
189186
expect($interpolate('---').expressions).toEqual([]);
190187
expect($interpolate('----')({})).toEqual('');
191188
expect($interpolate('--1--')({})).toEqual('1');
@@ -194,67 +191,58 @@ describe('$interpolate', function() {
194191

195192
describe('parseBindings', function() {
196193
it('should Parse Text With No Bindings', inject(function($interpolate) {
197-
expect($interpolate("a").separators).toEqual(['a']);
198194
expect($interpolate("a").expressions).toEqual([]);
199195
}));
200196

201197
it('should Parse Empty Text', inject(function($interpolate) {
202-
expect($interpolate("").separators).toEqual(['']);
203198
expect($interpolate("").expressions).toEqual([]);
204199
}));
205200

206201
it('should Parse Inner Binding', inject(function($interpolate) {
207202
var interpolateFn = $interpolate("a{{b}}C"),
208-
separators = interpolateFn.separators, expressions = interpolateFn.expressions;
209-
expect(separators).toEqual(['a', 'C']);
203+
expressions = interpolateFn.expressions;
210204
expect(expressions).toEqual(['b']);
211205
expect(interpolateFn({b: 123})).toEqual('a123C');
212206
}));
213207

214208
it('should Parse Ending Binding', inject(function($interpolate) {
215209
var interpolateFn = $interpolate("a{{b}}"),
216-
separators = interpolateFn.separators, expressions = interpolateFn.expressions;
217-
expect(separators).toEqual(['a', '']);
210+
expressions = interpolateFn.expressions;
218211
expect(expressions).toEqual(['b']);
219212
expect(interpolateFn({b: 123})).toEqual('a123');
220213
}));
221214

222215
it('should Parse Begging Binding', inject(function($interpolate) {
223216
var interpolateFn = $interpolate("{{b}}c"),
224-
separators = interpolateFn.separators, expressions = interpolateFn.expressions;
225-
expect(separators).toEqual(['', 'c']);
217+
expressions = interpolateFn.expressions;
226218
expect(expressions).toEqual(['b']);
227219
expect(interpolateFn({b: 123})).toEqual('123c');
228220
}));
229221

230222
it('should Parse Loan Binding', inject(function($interpolate) {
231223
var interpolateFn = $interpolate("{{b}}"),
232-
separators = interpolateFn.separators, expressions = interpolateFn.expressions;
233-
expect(separators).toEqual(['', '']);
224+
expressions = interpolateFn.expressions;
234225
expect(expressions).toEqual(['b']);
235226
expect(interpolateFn({b: 123})).toEqual('123');
236227
}));
237228

238229
it('should Parse Two Bindings', inject(function($interpolate) {
239230
var interpolateFn = $interpolate("{{b}}{{c}}"),
240-
separators = interpolateFn.separators, expressions = interpolateFn.expressions;
241-
expect(separators).toEqual(['', '', '']);
231+
expressions = interpolateFn.expressions;
242232
expect(expressions).toEqual(['b', 'c']);
243233
expect(interpolateFn({b: 111, c: 222})).toEqual('111222');
244234
}));
245235

246236
it('should Parse Two Bindings With Text In Middle', inject(function($interpolate) {
247237
var interpolateFn = $interpolate("{{b}}x{{c}}"),
248-
separators = interpolateFn.separators, expressions = interpolateFn.expressions;
249-
expect(separators).toEqual(['', 'x', '']);
238+
expressions = interpolateFn.expressions;
250239
expect(expressions).toEqual(['b', 'c']);
251240
expect(interpolateFn({b: 111, c: 222})).toEqual('111x222');
252241
}));
253242

254243
it('should Parse Multiline', inject(function($interpolate) {
255244
var interpolateFn = $interpolate('"X\nY{{A\n+B}}C\nD"'),
256-
separators = interpolateFn.separators, expressions = interpolateFn.expressions;
257-
expect(separators).toEqual(['"X\nY', 'C\nD"']);
245+
expressions = interpolateFn.expressions;
258246
expect(expressions).toEqual(['A\n+B']);
259247
expect(interpolateFn({'A': 'aa', 'B': 'bb'})).toEqual('"X\nYaabbC\nD"');
260248
}));
@@ -317,7 +305,6 @@ describe('$interpolate', function() {
317305
});
318306

319307
inject(function($interpolate) {
320-
expect($interpolate('---').separators).toEqual(['---']);
321308
expect($interpolate('---').expressions).toEqual([]);
322309
expect($interpolate('----')({})).toEqual('');
323310
expect($interpolate('--1--')({})).toEqual('1');

0 commit comments

Comments
 (0)