mirror of
https://github.com/zhigang1992/angular.js.git
synced 2026-01-12 22:45:52 +08:00
fix(minErr): stringify non-JSON compatible objects in error messages
Fix the JSON stringification to output a more meaningful string when an object cannot be normally converted to a JSON string, such as when the object contains cyclic references that would cause `JSON.stringify()` to throw an error. Closes #10085
This commit is contained in:
2
angularFiles.js
vendored
2
angularFiles.js
vendored
@@ -5,6 +5,7 @@ var angularFiles = {
|
||||
'src/minErr.js',
|
||||
'src/Angular.js',
|
||||
'src/loader.js',
|
||||
'src/stringify.js',
|
||||
'src/AngularPublic.js',
|
||||
'src/jqLite.js',
|
||||
'src/apis.js',
|
||||
@@ -73,6 +74,7 @@ var angularFiles = {
|
||||
],
|
||||
|
||||
'angularLoader': [
|
||||
'stringify.js',
|
||||
'src/minErr.js',
|
||||
'src/loader.js'
|
||||
],
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
"angularModule": false,
|
||||
"nodeName_": false,
|
||||
"uid": false,
|
||||
"toDebugString": false,
|
||||
|
||||
"REGEX_STRING_REGEXP" : false,
|
||||
"lowercase": false,
|
||||
|
||||
@@ -37,31 +37,14 @@ function minErr(module, ErrorConstructor) {
|
||||
prefix = '[' + (module ? module + ':' : '') + code + '] ',
|
||||
template = arguments[1],
|
||||
templateArgs = arguments,
|
||||
stringify = function(obj) {
|
||||
if (typeof obj === 'function') {
|
||||
return obj.toString().replace(/ \{[\s\S]*$/, '');
|
||||
} else if (typeof obj === 'undefined') {
|
||||
return 'undefined';
|
||||
} else if (typeof obj !== 'string') {
|
||||
return JSON.stringify(obj);
|
||||
}
|
||||
return obj;
|
||||
},
|
||||
|
||||
message, i;
|
||||
|
||||
message = prefix + template.replace(/\{\d+\}/g, function(match) {
|
||||
var index = +match.slice(1, -1), arg;
|
||||
|
||||
if (index + 2 < templateArgs.length) {
|
||||
arg = templateArgs[index + 2];
|
||||
if (typeof arg === 'function') {
|
||||
return arg.toString().replace(/ ?\{[\s\S]*$/, '');
|
||||
} else if (typeof arg === 'undefined') {
|
||||
return 'undefined';
|
||||
} else if (typeof arg !== 'string') {
|
||||
return toJson(arg);
|
||||
}
|
||||
return arg;
|
||||
return toDebugString(templateArgs[index + 2]);
|
||||
}
|
||||
return match;
|
||||
});
|
||||
@@ -70,7 +53,7 @@ function minErr(module, ErrorConstructor) {
|
||||
(module ? module + '/' : '') + code;
|
||||
for (i = 2; i < arguments.length; i++) {
|
||||
message = message + (i == 2 ? '?' : '&') + 'p' + (i - 2) + '=' +
|
||||
encodeURIComponent(stringify(arguments[i]));
|
||||
encodeURIComponent(toDebugString(arguments[i]));
|
||||
}
|
||||
return new ErrorConstructor(message);
|
||||
};
|
||||
|
||||
29
src/stringify.js
Normal file
29
src/stringify.js
Normal file
@@ -0,0 +1,29 @@
|
||||
'use strict';
|
||||
|
||||
/* global: toDebugString: true */
|
||||
|
||||
function serializeObject(obj) {
|
||||
var seen = [];
|
||||
|
||||
return JSON.stringify(obj, function(key, val) {
|
||||
val = toJsonReplacer(key, val);
|
||||
if (isObject(val)) {
|
||||
|
||||
if (seen.indexOf(val) >= 0) return '<<already seen>>';
|
||||
|
||||
seen.push(val);
|
||||
}
|
||||
return val;
|
||||
});
|
||||
}
|
||||
|
||||
function toDebugString(obj) {
|
||||
if (typeof obj === 'function') {
|
||||
return obj.toString().replace(/ \{[\s\S]*$/, '');
|
||||
} else if (typeof obj === 'undefined') {
|
||||
return 'undefined';
|
||||
} else if (typeof obj !== 'string') {
|
||||
return serializeObject(obj);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
@@ -18,6 +18,7 @@
|
||||
"angularModule": false,
|
||||
"nodeName_": false,
|
||||
"uid": false,
|
||||
"toDebugString": false,
|
||||
|
||||
"lowercase": false,
|
||||
"uppercase": false,
|
||||
|
||||
@@ -60,6 +60,13 @@ describe('minErr', function() {
|
||||
toMatch(/^\[test:26\] false: false; zero: 0; null: null; undefined: undefined; emptyStr: /);
|
||||
});
|
||||
|
||||
it('should handle arguments that are objects with cyclic references', function() {
|
||||
var a = { b: { } };
|
||||
a.b.a = a;
|
||||
|
||||
var myError = testError('26', 'a is {0}', a);
|
||||
expect(myError.message).toMatch(/a is {"b":{"a":"<<already seen>>"}}/);
|
||||
});
|
||||
|
||||
it('should preserve interpolation markers when fewer arguments than needed are provided', function() {
|
||||
// this way we can easily see if we are passing fewer args than needed
|
||||
|
||||
15
test/stringifySpec.js
Normal file
15
test/stringifySpec.js
Normal file
@@ -0,0 +1,15 @@
|
||||
'use strict';
|
||||
|
||||
describe('toDebugString', function() {
|
||||
it('should convert its argument to a string', function() {
|
||||
expect(toDebugString('string')).toEqual('string');
|
||||
expect(toDebugString(123)).toEqual('123');
|
||||
expect(toDebugString({a:{b:'c'}})).toEqual('{"a":{"b":"c"}}');
|
||||
expect(toDebugString(function fn() { var a = 10; })).toEqual('function fn()');
|
||||
expect(toDebugString()).toEqual('undefined');
|
||||
var a = { };
|
||||
a.a = a;
|
||||
expect(toDebugString(a)).toEqual('{"a":"<<already seen>>"}');
|
||||
expect(toDebugString([a,a])).toEqual('[{"a":"<<already seen>>"},"<<already seen>>"]');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user