michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: var BUGNUMBER = 450369; michael@0: var summary = 'Crash with JIT and json2.js'; michael@0: var actual = 'No Crash'; michael@0: var expect = 'No Crash'; michael@0: michael@0: jit(true); michael@0: michael@0: /* michael@0: json2.js michael@0: 2007-11-06 michael@0: michael@0: Public Domain michael@0: michael@0: See http://www.JSON.org/js.html michael@0: michael@0: This file creates a global JSON object containing two methods: michael@0: michael@0: JSON.stringify(value, whitelist) michael@0: value any JavaScript value, usually an object or array. michael@0: michael@0: whitelist an optional that determines how object values are michael@0: stringified. michael@0: michael@0: This method produces a JSON text from a JavaScript value. michael@0: There are three possible ways to stringify an object, depending michael@0: on the optional whitelist parameter. michael@0: michael@0: If an object has a toJSON method, then the toJSON() method will be michael@0: called. The value returned from the toJSON method will be michael@0: stringified. michael@0: michael@0: Otherwise, if the optional whitelist parameter is an array, then michael@0: the elements of the array will be used to select members of the michael@0: object for stringification. michael@0: michael@0: Otherwise, if there is no whitelist parameter, then all of the michael@0: members of the object will be stringified. michael@0: michael@0: Values that do not have JSON representaions, such as undefined or michael@0: functions, will not be serialized. Such values in objects will be michael@0: dropped, in arrays will be replaced with null. JSON.stringify() michael@0: returns undefined. Dates will be stringified as quoted ISO dates. michael@0: michael@0: Example: michael@0: michael@0: var text = JSON.stringify(['e', {pluribus: 'unum'}]); michael@0: // text is '["e",{"pluribus":"unum"}]' michael@0: michael@0: JSON.parse(text, filter) michael@0: This method parses a JSON text to produce an object or michael@0: array. It can throw a SyntaxError exception. michael@0: michael@0: The optional filter parameter is a function that can filter and michael@0: transform the results. It receives each of the keys and values, and michael@0: its return value is used instead of the original value. If it michael@0: returns what it received, then structure is not modified. If it michael@0: returns undefined then the member is deleted. michael@0: michael@0: Example: michael@0: michael@0: // Parse the text. If a key contains the string 'date' then michael@0: // convert the value to a date. michael@0: michael@0: myData = JSON.parse(text, function (key, value) { michael@0: return key.indexOf('date') >= 0 ? new Date(value) : value; michael@0: }); michael@0: michael@0: This is a reference implementation. You are free to copy, modify, or michael@0: redistribute. michael@0: michael@0: Use your own copy. It is extremely unwise to load third party michael@0: code into your pages. michael@0: */ michael@0: michael@0: /*jslint evil: true */ michael@0: /*extern JSON */ michael@0: michael@0: if (!this.emulatedJSON) { michael@0: michael@0: emulatedJSON = function () { michael@0: michael@0: function f(n) { // Format integers to have at least two digits. michael@0: return n < 10 ? '0' + n : n; michael@0: } michael@0: michael@0: Date.prototype.toJSON = function () { michael@0: michael@0: // Eventually, this method will be based on the date.toISOString method. michael@0: michael@0: return this.getUTCFullYear() + '-' + michael@0: f(this.getUTCMonth() + 1) + '-' + michael@0: f(this.getUTCDate()) + 'T' + michael@0: f(this.getUTCHours()) + ':' + michael@0: f(this.getUTCMinutes()) + ':' + michael@0: f(this.getUTCSeconds()) + 'Z'; michael@0: }; michael@0: michael@0: michael@0: var m = { // table of character substitutions michael@0: '\b': '\\b', michael@0: '\t': '\\t', michael@0: '\n': '\\n', michael@0: '\f': '\\f', michael@0: '\r': '\\r', michael@0: '"' : '\\"', michael@0: '\\': '\\\\' michael@0: }; michael@0: michael@0: function stringify(value, whitelist) { michael@0: var a, // The array holding the partial texts. michael@0: i, // The loop counter. michael@0: k, // The member key. michael@0: l, // Length. michael@0: r = /["\\\x00-\x1f\x7f-\x9f]/g, michael@0: v; // The member value. michael@0: michael@0: switch (typeof value) { michael@0: case 'string': michael@0: michael@0: // If the string contains no control characters, no quote characters, and no michael@0: // backslash characters, then we can safely slap some quotes around it. michael@0: // Otherwise we must also replace the offending characters with safe sequences. michael@0: michael@0: return r.test(value) ? michael@0: '"' + value.replace(r, function (a) { michael@0: var c = m[a]; michael@0: if (c) { michael@0: return c; michael@0: } michael@0: c = a.charCodeAt(); michael@0: return '\\u00' + Math.floor(c / 16).toString(16) + michael@0: (c % 16).toString(16); michael@0: }) + '"' : michael@0: '"' + value + '"'; michael@0: michael@0: case 'number': michael@0: michael@0: // JSON numbers must be finite. Encode non-finite numbers as null. michael@0: michael@0: return isFinite(value) ? String(value) : 'null'; michael@0: michael@0: case 'boolean': michael@0: case 'null': michael@0: return String(value); michael@0: michael@0: case 'object': michael@0: michael@0: // Due to a specification blunder in ECMAScript, michael@0: // typeof null is 'object', so watch out for that case. michael@0: michael@0: if (!value) { michael@0: return 'null'; michael@0: } michael@0: michael@0: // If the object has a toJSON method, call it, and stringify the result. michael@0: michael@0: if (typeof value.toJSON === 'function') { michael@0: return stringify(value.toJSON()); michael@0: } michael@0: a = []; michael@0: if (typeof value.length === 'number' && michael@0: !(value.propertyIsEnumerable('length'))) { michael@0: michael@0: // The object is an array. Stringify every element. Use null as a placeholder michael@0: // for non-JSON values. michael@0: michael@0: l = value.length; michael@0: for (i = 0; i < l; i += 1) { michael@0: a.push(stringify(value[i], whitelist) || 'null'); michael@0: } michael@0: michael@0: // Join all of the elements together and wrap them in brackets. michael@0: michael@0: return '[' + a.join(',') + ']'; michael@0: } michael@0: if (whitelist) { michael@0: michael@0: // If a whitelist (array of keys) is provided, use it to select the components michael@0: // of the object. michael@0: michael@0: l = whitelist.length; michael@0: for (i = 0; i < l; i += 1) { michael@0: k = whitelist[i]; michael@0: if (typeof k === 'string') { michael@0: v = stringify(value[k], whitelist); michael@0: if (v) { michael@0: a.push(stringify(k) + ':' + v); michael@0: } michael@0: } michael@0: } michael@0: } else { michael@0: michael@0: // Otherwise, iterate through all of the keys in the object. michael@0: michael@0: for (k in value) { michael@0: if (typeof k === 'string') { michael@0: v = stringify(value[k], whitelist); michael@0: if (v) { michael@0: a.push(stringify(k) + ':' + v); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Join all of the member texts together and wrap them in braces. michael@0: michael@0: return '{' + a.join(',') + '}'; michael@0: } michael@0: return undefined; michael@0: } michael@0: michael@0: return { michael@0: stringify: stringify, michael@0: parse: function (text, filter) { michael@0: var j; michael@0: michael@0: function walk(k, v) { michael@0: var i, n; michael@0: if (v && typeof v === 'object') { michael@0: for (i in v) { michael@0: if (Object.prototype.hasOwnProperty.apply(v, [i])) { michael@0: n = walk(i, v[i]); michael@0: if (n !== undefined) { michael@0: v[i] = n; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: return filter(k, v); michael@0: } michael@0: michael@0: michael@0: // Parsing happens in three stages. In the first stage, we run the text against michael@0: // regular expressions that look for non-JSON patterns. We are especially michael@0: // concerned with '()' and 'new' because they can cause invocation, and '=' michael@0: // because it can cause mutation. But just to be safe, we want to reject all michael@0: // unexpected forms. michael@0: michael@0: // We split the first stage into 4 regexp operations in order to work around michael@0: // crippling inefficiencies in IE's and Safari's regexp engines. First we michael@0: // replace all backslash pairs with '@' (a non-JSON character). Second, we michael@0: // replace all simple value tokens with ']' characters. Third, we delete all michael@0: // open brackets that follow a colon or comma or that begin the text. Finally, michael@0: // we look to see that the remaining characters are only whitespace or ']' or michael@0: // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. michael@0: michael@0: if (/^[\],:{}\s]*$/.test(text.replace(/\\./g, '@'). michael@0: replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(:?[eE][+\-]?\d+)?/g, ']'). michael@0: replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { michael@0: michael@0: // In the second stage we use the eval function to compile the text into a michael@0: // JavaScript structure. The '{' operator is subject to a syntactic ambiguity michael@0: // in JavaScript: it can begin a block or an object literal. We wrap the text michael@0: // in parens to eliminate the ambiguity. michael@0: michael@0: j = eval('(' + text + ')'); michael@0: michael@0: // In the optional third stage, we recursively walk the new structure, passing michael@0: // each name/value pair to a filter function for possible transformation. michael@0: michael@0: return typeof filter === 'function' ? walk('', j) : j; michael@0: } michael@0: michael@0: // If the text is not JSON parseable, then a SyntaxError is thrown. michael@0: michael@0: throw new SyntaxError('parseJSON'); michael@0: } michael@0: }; michael@0: }(); michael@0: } michael@0: michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: test(); michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: jit(false); michael@0: michael@0: function test() michael@0: { michael@0: enterFunc ('test'); michael@0: printBugNumber(BUGNUMBER); michael@0: printStatus (summary); michael@0: michael@0: michael@0: var testPairs = [ michael@0: ["{}", {}], michael@0: ["[]", []], michael@0: ['{"foo":"bar"}', {"foo":"bar"}], michael@0: ['{"null":null}', {"null":null}], michael@0: ['{"five":5}', {"five":5}], michael@0: ] michael@0: michael@0: var a = []; michael@0: for (var i=0; i < testPairs.length; i++) { michael@0: var pair = testPairs[i]; michael@0: var s = emulatedJSON.stringify(pair[1]) michael@0: a[i] = s; michael@0: } michael@0: print(a.join("\n")); michael@0: michael@0: reportCompare(expect, actual, summary); michael@0: michael@0: exitFunc ('test'); michael@0: } michael@0: