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

Commit 3ae6f6b

Browse files
committed
feat(minErr): set max depth for angular error JSON stringify
Closes #15402 Closes #15433
1 parent 54a7caf commit 3ae6f6b

File tree

5 files changed

+63
-19
lines changed

5 files changed

+63
-19
lines changed

src/Angular.js

+20-10
Original file line numberDiff line numberDiff line change
@@ -796,6 +796,7 @@ function arrayRemove(array, value) {
796796
* are deleted and then all elements/properties from the source are copied to it.
797797
* * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned.
798798
* * If `source` is identical to `destination` an exception will be thrown.
799+
* * If `maxDepth`is supplied, all properties of the source will be copied until reaching the maxDepth.
799800
*
800801
* <br />
801802
* <div class="alert alert-warning">
@@ -807,6 +808,7 @@ function arrayRemove(array, value) {
807808
* Can be any type, including primitives, `null`, and `undefined`.
808809
* @param {(Object|Array)=} destination Destination into which the source is copied. If
809810
* provided, must be of the same type as `source`.
811+
* @param {Number} maxDepth all properties of the source will be copied until reaching the maxDepth.
810812
* @returns {*} The copy or updated `destination`, if `destination` was specified.
811813
*
812814
* @example
@@ -847,9 +849,13 @@ function arrayRemove(array, value) {
847849
</file>
848850
</example>
849851
*/
850-
function copy(source, destination) {
852+
function copy(source, destination, maxDepth) {
851853
var stackSource = [];
852854
var stackDest = [];
855+
var currentDepth = 0;
856+
if (!isNumber(maxDepth)) {
857+
maxDepth = NaN;
858+
}
853859

854860
if (destination) {
855861
if (isTypedArray(destination) || isArrayBuffer(destination)) {
@@ -872,43 +878,47 @@ function copy(source, destination) {
872878

873879
stackSource.push(source);
874880
stackDest.push(destination);
875-
return copyRecurse(source, destination);
881+
return copyRecurse(source, destination, currentDepth);
876882
}
877883

878-
return copyElement(source);
884+
return copyElement(source, currentDepth);
879885

880-
function copyRecurse(source, destination) {
886+
function copyRecurse(source, destination, currentDepth) {
887+
currentDepth++;
888+
if (currentDepth > maxDepth) {
889+
return '...';
890+
}
881891
var h = destination.$$hashKey;
882892
var key;
883893
if (isArray(source)) {
884894
for (var i = 0, ii = source.length; i < ii; i++) {
885-
destination.push(copyElement(source[i]));
895+
destination.push(copyElement(source[i], currentDepth));
886896
}
887897
} else if (isBlankObject(source)) {
888898
// createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
889899
for (key in source) {
890-
destination[key] = copyElement(source[key]);
900+
destination[key] = copyElement(source[key], currentDepth);
891901
}
892902
} else if (source && typeof source.hasOwnProperty === 'function') {
893903
// Slow path, which must rely on hasOwnProperty
894904
for (key in source) {
895905
if (source.hasOwnProperty(key)) {
896-
destination[key] = copyElement(source[key]);
906+
destination[key] = copyElement(source[key], currentDepth);
897907
}
898908
}
899909
} else {
900910
// Slowest path --- hasOwnProperty can't be called as a method
901911
for (key in source) {
902912
if (hasOwnProperty.call(source, key)) {
903-
destination[key] = copyElement(source[key]);
913+
destination[key] = copyElement(source[key], currentDepth);
904914
}
905915
}
906916
}
907917
setHashKey(destination, h);
908918
return destination;
909919
}
910920

911-
function copyElement(source) {
921+
function copyElement(source, currentDepth) {
912922
// Simple values
913923
if (!isObject(source)) {
914924
return source;
@@ -937,7 +947,7 @@ function copy(source, destination) {
937947
stackDest.push(destination);
938948

939949
return needsRecurse
940-
? copyRecurse(source, destination)
950+
? copyRecurse(source, destination, currentDepth)
941951
: destination;
942952
}
943953

src/minErr.js

+7-5
Original file line numberDiff line numberDiff line change
@@ -34,19 +34,21 @@ function minErr(module, ErrorConstructor) {
3434
ErrorConstructor = ErrorConstructor || Error;
3535
return function() {
3636
var SKIP_INDEXES = 2;
37+
var DEFAULT_MAX_DEPTH = 3;
3738

3839
var templateArgs = arguments,
3940
code = templateArgs[0],
4041
message = '[' + (module ? module + ':' : '') + code + '] ',
4142
template = templateArgs[1],
4243
paramPrefix, i;
4344

44-
message += template.replace(/\{\d+\}/g, function(match) {
45-
var index = +match.slice(1, -1),
46-
shiftedIndex = index + SKIP_INDEXES;
45+
message += template.replace(/\{(\d)+(,maxDepth=(\d))?\}/g, function(match, index, maxDepthString, maxDepth, offset, wholeString) {
46+
index = Number(index);
47+
maxDepth = isUndefined(maxDepth) ? DEFAULT_MAX_DEPTH : Number(maxDepth);
48+
var shiftedIndex = index + SKIP_INDEXES;
4749

4850
if (shiftedIndex < templateArgs.length) {
49-
return toDebugString(templateArgs[shiftedIndex]);
51+
return toDebugString(templateArgs[shiftedIndex], maxDepth);
5052
}
5153

5254
return match;
@@ -57,7 +59,7 @@ function minErr(module, ErrorConstructor) {
5759

5860
for (i = SKIP_INDEXES, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
5961
message += paramPrefix + 'p' + (i - SKIP_INDEXES) + '=' +
60-
encodeURIComponent(toDebugString(templateArgs[i]));
62+
encodeURIComponent(toDebugString(templateArgs[i], DEFAULT_MAX_DEPTH));
6163
}
6264

6365
return new ErrorConstructor(message);

src/stringify.js

+6-4
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22

33
/* global toDebugString: true */
44

5-
function serializeObject(obj) {
5+
function serializeObject(obj, maxDepth) {
66
var seen = [];
7-
7+
if (maxDepth > 0) {
8+
obj = copy(obj, null, maxDepth);
9+
}
810
return JSON.stringify(obj, function(key, val) {
911
val = toJsonReplacer(key, val);
1012
if (isObject(val)) {
@@ -17,13 +19,13 @@ function serializeObject(obj) {
1719
});
1820
}
1921

20-
function toDebugString(obj) {
22+
function toDebugString(obj, maxDepth) {
2123
if (typeof obj === 'function') {
2224
return obj.toString().replace(/ \{[\s\S]*$/, '');
2325
} else if (isUndefined(obj)) {
2426
return 'undefined';
2527
} else if (typeof obj !== 'string') {
26-
return serializeObject(obj);
28+
return serializeObject(obj, maxDepth);
2729
}
2830
return obj;
2931
}

test/AngularSpec.js

+21
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,27 @@ describe('angular', function() {
602602
expect(copy(new Number(NaN)).valueOf()).toBeNaN();
603603
/* eslint-enable */
604604
});
605+
606+
it('should copy source until reaching a given max depth', function() {
607+
var destinationA = {};
608+
var destinationB = {};
609+
610+
destinationA = copy({a:1}, destinationB, 1);
611+
expect(destinationA).toEqual({a:1});
612+
expect(destinationB).toEqual({a:1});
613+
614+
destinationA = {};
615+
destinationB = {};
616+
destinationA = copy({a:{b:1}}, destinationB, 1);
617+
expect(destinationA).toEqual({a:'...'});
618+
expect(destinationB).toEqual({a:'...'});
619+
620+
destinationA = {};
621+
destinationB = {};
622+
destinationA = copy({a:{b:{c:1}}}, destinationB, 2);
623+
expect(destinationA).toEqual({a:{b:'...'}});
624+
expect(destinationB).toEqual({a:{b:'...'}});
625+
});
605626
});
606627

607628
describe('extend', function() {

test/minErrSpec.js

+9
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,15 @@ describe('minErr', function() {
6868
expect(myError.message).toMatch(/a is {"b":{"a":"..."}}/);
6969
});
7070

71+
it('should handle arguments that are objects with max depth', function() {
72+
var a = { b: {c:{d:1}} };
73+
var myError = testError('26', 'a when maxDepth=1 is {0,maxDepth=1}', a);
74+
expect(myError.message).toMatch(/a when maxDepth=1 is {"b":"..."}/);
75+
76+
myError = testError('26', 'a when maxDepth=2 is {0,maxDepth=2}', a);
77+
expect(myError.message).toMatch(/a when maxDepth=2 is {"b":{"c":"..."}}/);
78+
});
79+
7180
it('should preserve interpolation markers when fewer arguments than needed are provided', function() {
7281
// this way we can easily see if we are passing fewer args than needed
7382

0 commit comments

Comments
 (0)