Bug 387522. Native JSON support. r=crowder/jst, sr=brendan

This commit is contained in:
sayrer@gmail.com 2007-12-27 13:34:03 -08:00
parent 3fda481a95
commit 43999e482b
22 changed files with 2449 additions and 45 deletions

View File

@ -58,6 +58,7 @@ DIRS = \
ls \
xul \
storage \
json \
offline
ifdef MOZ_SVG

View File

@ -0,0 +1,57 @@
#
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is mozilla.org code.
#
# The Initial Developer of the Original Code is Mozilla Corporation.
# Portions created by the Initial Developer are Copyright (C) 2007
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Robert Sayre <sayrer@gmail.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either of the GNU General Public License Version 2 or later (the "GPL"),
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
DEPTH = ../../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = dom
XPIDL_MODULE = dom_json
GRE_MODULE = 1
XPIDLSRCS = \
nsIJSON.idl \
$(NULL)
SDK_XPIDLSRCS = \
nsIJSON.idl \
$(NULL)
include $(topsrcdir)/config/rules.mk

View File

@ -0,0 +1,66 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2007
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Robert Sayre <sayrer@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "domstubs.idl"
interface nsIInputStream;
interface nsIOutputStream;
interface nsIScriptGlobalObject;
/**
* Encode and decode JSON text.
*/
[scriptable, uuid(45464c36-efde-4cb5-8e00-07480533ff35)]
interface nsIJSON : nsISupports
{
AString encode(/* in JSObject value,
/* [optional] in JSObject whitelist */);
void encodeToStream(in nsIOutputStream stream,
in string charset,
in boolean writeBOM
/* in JSObject value,
/* [optional] in JSObject optFilter */);
void /* JSObject */ decode(in AString str
/* , [optional] in JSObject whitelist */);
void /* JSObject */ decodeFromStream(in nsIInputStream stream,
in long contentLength
/*[optional] in JSObject optFilter */);
};

View File

@ -42,7 +42,7 @@ VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
DIRS = base jsurl events storage offline
DIRS = base jsurl events storage offline json
include $(topsrcdir)/config/rules.mk

80
dom/src/json/Makefile.in Normal file
View File

@ -0,0 +1,80 @@
#
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is mozilla.org code.
#
# The Initial Developer of the Original Code is Mozilla Corporation.
# Portions created by the Initial Developer are Copyright (C) 2007
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Robert Sayre <sayrer@gmail.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either of the GNU General Public License Version 2 or later (the "GPL"),
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
DEPTH = ../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = dom
LIBRARY_NAME = json_s
LIBXUL_LIBRARY = 1
REQUIRES = xpcom \
string \
content \
caps \
js \
locale \
layout \
necko \
pref \
uconv \
unicharutil \
widget \
xpconnect \
$(NULL)
CPPSRCS = \
nsJSON.cpp \
$(NULL)
FORCE_STATIC_LIB = 1
LOCAL_INCLUDES = \
-I$(srcdir)/../base \
-I$(topsrcdir)/content/events/src
DEFINES += -D_IMPL_NS_LAYOUT
ifdef ENABLE_TESTS
DIRS += test
endif
include $(topsrcdir)/config/rules.mk

1245
dom/src/json/nsJSON.cpp Normal file

File diff suppressed because it is too large Load Diff

164
dom/src/json/nsJSON.h Normal file
View File

@ -0,0 +1,164 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2007
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Robert Sayre <sayrer@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef nsJSON_h__
#define nsJSON_h__
#include "jsprvtd.h"
#include "nsIJSON.h"
#include "nsString.h"
#include "nsCOMPtr.h"
#include "nsIOutputStream.h"
#include "nsIUnicodeEncoder.h"
#include "nsIUnicodeDecoder.h"
#include "nsIRequestObserver.h"
#include "nsIStreamListener.h"
#include "nsTArray.h"
#define JSON_MAX_DEPTH 2048
#define JSON_PARSER_BUFSIZE 1024
class nsJSONWriter
{
public:
nsJSONWriter();
nsJSONWriter(nsIOutputStream *aStream);
virtual ~nsJSONWriter();
nsresult SetCharset(const char *aCharset);
nsString mBuffer;
nsCOMPtr<nsIOutputStream> mStream;
nsresult WriteString(const PRUnichar* aBuffer, PRUint32);
nsresult Write(const PRUnichar *aBuffer, PRUint32 aLength);
protected:
nsresult WriteToStream(nsIOutputStream *aStream, nsIUnicodeEncoder *encoder,
const PRUnichar *aBuffer, PRUint32 aLength);
nsCOMPtr<nsIUnicodeEncoder> mEncoder;
};
class nsJSON : public nsIJSON
{
public:
nsJSON();
virtual ~nsJSON();
NS_DECL_ISUPPORTS
NS_DECL_NSIJSON
protected:
JSBool ToJSON(JSContext *cx, jsval *vp);
nsresult EncodeObject(JSContext *cx, jsval *vp, nsJSONWriter *writer,
JSObject *whitelist, PRUint32 depth);
nsresult EncodeInternal(nsJSONWriter *writer);
nsresult DecodeInternal(nsIInputStream *aStream,
PRInt32 aContentLength,
PRBool aNeedsConverter);
};
NS_IMETHODIMP
NS_NewJSON(nsISupports* aOuter, REFNSIID aIID, void** aResult);
enum JSONParserState {
JSON_PARSE_STATE_INIT,
JSON_PARSE_STATE_VALUE,
JSON_PARSE_STATE_OBJECT,
JSON_PARSE_STATE_OBJECT_PAIR,
JSON_PARSE_STATE_OBJECT_IN_PAIR,
JSON_PARSE_STATE_ARRAY,
JSON_PARSE_STATE_STRING,
JSON_PARSE_STATE_STRING_ESCAPE,
JSON_PARSE_STATE_STRING_HEX,
JSON_PARSE_STATE_NUMBER,
JSON_PARSE_STATE_KEYWORD,
JSON_PARSE_STATE_FINISHED
};
class nsJSONObjectStack : public nsTArray<JSObject *>,
public JSTempValueRooter
{
};
class nsJSONListener : public nsIStreamListener
{
public:
nsJSONListener(JSContext *cx, jsval *rootVal, PRBool needsConverter);
virtual ~nsJSONListener();
NS_DECL_ISUPPORTS
NS_DECL_NSIREQUESTOBSERVER
NS_DECL_NSISTREAMLISTENER
protected:
PRUint32 mLineNum;
PRUint32 mColumn;
/* Used while handling \uNNNN in strings */
PRUnichar mHexChar;
PRUint8 mNumHex;
JSContext *mCx;
jsval *mRootVal;
PRBool mNeedsConverter;
nsCOMPtr<nsIUnicodeDecoder> mDecoder;
JSONParserState *mStatep;
JSONParserState mStateStack[JSON_MAX_DEPTH];
nsString mStringBuffer;
nsCString mSniffBuffer;
nsresult PushState(JSONParserState state);
nsresult PopState();
nsresult ProcessBytes(const char* aBuffer, PRUint32 aByteLength);
nsresult ConsumeConverted(const char* aBuffer, PRUint32 aByteLength);
nsresult Consume(const PRUnichar *data, PRUint32 len);
// These handle parsed tokens. Could be split to separate interface.
nsJSONObjectStack mObjectStack;
nsresult PushValue(JSObject *aParent, jsval aValue);
nsresult PushObject(JSObject *aObj);
nsresult OpenObject();
nsresult CloseObject();
nsresult OpenArray();
nsresult CloseArray();
nsresult HandleString();
nsresult HandleNumber();
nsresult HandleKeyword();
nsString mObjectKey;
};
#endif

View File

@ -0,0 +1,49 @@
#
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is mozilla.org code.
#
# The Initial Developer of the Original Code is Mozilla Corporation.
# Portions created by the Initial Developer are Copyright (C) 2007
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Robert Sayre <sayrer@gmail.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either of the GNU General Public License Version 2 or later (the "GPL"),
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
DEPTH = ../../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = json_test
XPCSHELL_TESTS = unit
include $(topsrcdir)/config/rules.mk

263
dom/src/json/test/json2.js Normal file
View File

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

View File

@ -0,0 +1,58 @@
[
"JSON Test Pattern pass1",
{"object with 1 member":["array with 1 element"]},
{},
[],
-42,
true,
false,
null,
{
"integer": 1234567890,
"real": -9876.543210,
"e": 0.123456789e-12,
"E": 1.234567890E+34,
"": 23456789012E66,
"zero": 0,
"one": 1,
"space": " ",
"quote": "\"",
"backslash": "\\",
"controls": "\b\f\n\r\t",
"slash": "/ & \/",
"alpha": "abcdefghijklmnopqrstuvwyz",
"ALPHA": "ABCDEFGHIJKLMNOPQRSTUVWYZ",
"digit": "0123456789",
"0123456789": "digit",
"special": "`1~!@#$%^&*()_+-={':[,]}|;.</>?",
"hex": "\u0123\u4567\u89AB\uCDEF\uabcd\uef4A",
"true": true,
"false": false,
"null": null,
"array":[ ],
"object":{ },
"address": "50 St. James Street",
"url": "http://www.JSON.org/",
"comment": "// /* <!-- --",
"# -- --> */": " ",
" s p a c e d " :[1,2 , 3
,
4 , 5 , 6 ,7 ],"compact":[1,2,3,4,5,6,7],
"jsontext": "{\"object with 1 member\":[\"array with 1 element\"]}",
"quotes": "&#34; \u0022 %22 0x22 034 &#x22;",
"\/\\\"\uCAFE\uBABE\uAB98\uFCDE\ubcda\uef4A\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?"
: "A key can be any string"
},
0.5 ,98.6
,
99.44
,
1066,
1e1,
0.1e1,
1e-1,
1e00,2e+00,2e-00
,"rosebud"]

View File

@ -0,0 +1,7 @@
{
"JSON Test Pattern pass3": {
"The outermost value": "must be an object or array.",
"In this test": "It is an object."
}
}

View File

@ -0,0 +1,19 @@
var Ci = Components.interfaces;
var Cc = Components.classes;
var nativeJSON = Cc["@mozilla.org/dom/json;1"].createInstance(Ci.nsIJSON);
var dirSvc = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
var workingDir = dirSvc.get("TmpD", Ci.nsIFile);
var outputName = "json-test-output";
var outputDir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
outputDir.initWithFile(workingDir);
outputDir.append(outputName);
if (!outputDir.exists()) {
outputDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0777);
} else if (!outputDir.isDirectory()) {
do_throw(outputName + " is not a directory?")
}
var JSON = null;
do_import_script("dom/src/json/test/json2.js");

View File

@ -0,0 +1,161 @@
function decode_strings() {
// empty object
var x = nativeJSON.decode("{}");
do_check_eq(typeof x, "object");
// empty array
x = nativeJSON.decode("[]");
do_check_eq(typeof x, "object");
do_check_eq(x.length, 0);
do_check_eq(x.constructor, Array);
// one element array
x = nativeJSON.decode("[[]]");
do_check_eq(typeof x, "object");
do_check_eq(x.length, 1);
do_check_eq(x.constructor, Array);
do_check_eq(x[0].constructor, Array);
// multiple arrays
x = nativeJSON.decode("[[],[],[]]");
do_check_eq(typeof x, "object");
do_check_eq(x.length, 3);
do_check_eq(x.constructor, Array);
do_check_eq(x[0].constructor, Array);
do_check_eq(x[1].constructor, Array);
do_check_eq(x[2].constructor, Array);
// array key/value
x = nativeJSON.decode('{"foo":[]}');
do_check_eq(typeof x, "object");
do_check_eq(typeof x.foo, "object");
do_check_eq(x.foo.constructor, Array);
x = nativeJSON.decode('{"foo":[], "bar":[]}');
do_check_eq(typeof x, "object");
do_check_eq(typeof x.foo, "object");
do_check_eq(x.foo.constructor, Array);
do_check_eq(typeof x.bar, "object");
do_check_eq(x.bar.constructor, Array);
// nesting
x = nativeJSON.decode('{"foo":[{}]}');
do_check_eq(x.foo.constructor, Array);
do_check_eq(x.foo.length, 1);
do_check_eq(typeof x.foo[0], "object");
x = nativeJSON.decode('{"foo":[{"foo":[{"foo":{}}]}]}');
do_check_eq(x.foo[0].foo[0].foo.constructor, Object);
x = nativeJSON.decode('{"foo":[{"foo":[{"foo":[]}]}]}');
do_check_eq(x.foo[0].foo[0].foo.constructor, Array);
// strings
x = nativeJSON.decode('{"foo":"bar"}');
do_check_eq(x.foo, "bar");
x = nativeJSON.decode('["foo", "bar", "baz"]');
do_check_eq(x[0], "foo");
do_check_eq(x[1], "bar");
do_check_eq(x[2], "baz");
// numbers
x = nativeJSON.decode('{"foo":5.5, "bar":5}');
do_check_eq(x.foo, 5.5);
do_check_eq(x.bar, 5);
// keywords
x = nativeJSON.decode('{"foo": true, "bar":false, "baz":null}');
do_check_eq(x.foo, true);
do_check_eq(x.bar, false);
do_check_eq(x.baz, null);
// short escapes
x = nativeJSON.decode('{"foo": "\\"", "bar":"\\\\", "baz":"\\b","qux":"\\f", "quux":"\\n", "quuux":"\\r","quuuux":"\\t"}');
do_check_eq(x.foo, '"');
do_check_eq(x.bar, '\\');
do_check_eq(x.baz, '\b');
do_check_eq(x.qux, '\f');
do_check_eq(x.quux, "\n");
do_check_eq(x.quuux, "\r");
do_check_eq(x.quuuux, "\t");
// unicode escape
x = nativeJSON.decode('{"foo":"hmm\\u006dmm"}');
do_check_eq("hmm\u006dmm", x.foo);
x = nativeJSON.decode('{"JSON Test Pattern pass3": {"The outermost value": "must be an object or array.","In this test": "It is an object." }}');
}
function test_files() {
function read_file(path) {
try {
var f = do_get_file(path);
var istream = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(Ci.nsIFileInputStream);
istream.init(f, -1, -1, false);
var x = nativeJSON.decodeFromStream(istream, istream.available());
} finally {
istream.close();
}
return x;
}
var x = read_file("/dom/src/json/test/pass3.json");
do_check_eq(x["JSON Test Pattern pass3"]["The outermost value"], "must be an object or array.");
do_check_eq(x["JSON Test Pattern pass3"]["In this test"], "It is an object.");
x = read_file("/dom/src/json/test/pass1.json");
do_check_eq(x[0], "JSON Test Pattern pass1");
do_check_eq(x[1]["object with 1 member"][0], "array with 1 element");
do_check_eq(x[2].constructor, Object);
do_check_eq(x[3].constructor, Array);
do_check_eq(x[4], -42);
do_check_eq(x[5], true);
do_check_eq(x[6], false);
do_check_eq(x[7], null);
do_check_eq(x[8].constructor, Object);
do_check_eq(x[8]["integer"], 1234567890);
do_check_eq(x[8]["real"], -9876.543210);
do_check_eq(x[8]["e"], 0.123456789e-12);
do_check_eq(x[8]["E"], 1.234567890E+34);
do_check_eq(x[8][""], 23456789012E66);
do_check_eq(x[8]["zero"], 0);
do_check_eq(x[8]["one"], 1);
do_check_eq(x[8]["space"], " ");
do_check_eq(x[8]["quote"], "\"");
do_check_eq(x[8]["backslash"], "\\");
do_check_eq(x[8]["controls"], "\b\f\n\r\t");
do_check_eq(x[8]["slash"], "/ & /");
do_check_eq(x[8]["alpha"], "abcdefghijklmnopqrstuvwyz");
do_check_eq(x[8]["ALPHA"], "ABCDEFGHIJKLMNOPQRSTUVWYZ");
do_check_eq(x[8]["digit"], "0123456789");
do_check_eq(x[8]["0123456789"], "digit");
do_check_eq(x[8]["special"], "`1~!@#$%^&*()_+-={':[,]}|;.</>?");
do_check_eq(x[8]["hex"], "\u0123\u4567\u89AB\uCDEF\uabcd\uef4A");
do_check_eq(x[8]["true"], true);
do_check_eq(x[8]["false"], false);
do_check_eq(x[8]["null"], null);
do_check_eq(x[8]["array"].length, 0);
do_check_eq(x[8]["object"].constructor, Object);
do_check_eq(x[8]["address"], "50 St. James Street");
do_check_eq(x[8]["url"], "http://www.JSON.org/");
do_check_eq(x[8]["comment"], "// /* <!-- --");
do_check_eq(x[8]["# -- --> */"], " ");
do_check_eq(x[8][" s p a c e d "].length, 7);
do_check_eq(x[8]["compact"].length, 7);
do_check_eq(x[8]["jsontext"], "{\"object with 1 member\":[\"array with 1 element\"]}");
do_check_eq(x[8]["quotes"], "&#34; \u0022 %22 0x22 034 &#x22;");
do_check_eq(x[8]["\/\\\"\uCAFE\uBABE\uAB98\uFCDE\ubcda\uef4A\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?"], "A key can be any string");
do_check_eq(x[9], 0.5);
do_check_eq(x[10], 98.6);
do_check_eq(x[11], 99.44);
do_check_eq(x[12], 1066);
do_check_eq(x[13], 1e1);
do_check_eq(x[14], 0.1e1);
do_check_eq(x[15], 1e-1);
do_check_eq(x[16], 1e00);
do_check_eq(x[17], 2e+00);
do_check_eq(x[18], 2e-00);
do_check_eq(x[19], "rosebud");
}
function run_test() {
decode_strings();
test_files();
}

View File

@ -0,0 +1,237 @@
// returns a list of [string, object] pairs to test encoding
function getTestPairs() {
var testPairs = [
["{}", {}],
["[]", []],
['{"foo":"bar"}', {"foo":"bar"}],
['{"null":null}', {"null":null}],
['{"five":5}', {"five":5}],
['{"five":5,"six":6}', {"five":5, "six":6}],
['{"x":{"y":"z"}}', {"x":{"y":"z"}}],
['{"w":{"x":{"y":"z"}}}', {"w":{"x":{"y":"z"}}}],
['[1,2,3]', [1,2,3]],
['{"w":{"x":{"y":[1,2,3]}}}', {"w":{"x":{"y":[1,2,3]}}}],
['{"false":false}', {"false":false}],
['{"true":true}', {"true":true}],
['{"child has two members":{"this":"one","2":"and this one"}}',
{"child has two members": {"this":"one", 2:"and this one"}}],
['{"x":{"a":"b","c":{"y":"z"},"f":"g"}}',
{"x":{"a":"b","c":{"y":"z"},"f":"g"}}],
['{"x":[1,{"y":"z"},3]}', {"x":[1,{"y":"z"},3]}],
//['{"0":"h","1":"m","2":"m"}', new String("hmm")],
['[1,null,3]',[1,,3]],
[null, function test(){}],
[null, dump],
['{"mm\\\"mm":"hmm"}',{"mm\"mm":"hmm"}],
['{"mm\\\"mm\\\"mm":"hmm"}',{"mm\"mm\"mm":"hmm"}],
['{"\\\"":"hmm"}',{'"':"hmm"}],
['{"\\\\":"hmm"}',{'\\':"hmm"}],
['{"mmm\\\\mmm":"hmm"}',{'mmm\\mmm':"hmm"}],
['{"mmm\\\\mmm\\\\mmm":"hmm"}',{'mmm\\mmm\\mmm':"hmm"}],
['{"mm\\u000bmm":"hmm"}',{"mm\u000bmm":"hmm"}],
['{"mm\\u0000mm":"hmm"}',{"mm\u0000mm":"hmm"}]
];
var x = {"free":"variable"}
testPairs.push(['{"free":"variable"}', x]);
testPairs.push(['{"y":{"free":"variable"}}', {"y":x}]);
// array prop
var x = {
a: [1,2,3]
}
testPairs.push(['{"a":[1,2,3]}', x])
var y = {
foo: function(hmm) { return hmm; }
}
testPairs.push(['{"y":{}}',{"y":y}]);
// test toJSON
var hmm = {
toJSON: function() { return {"foo":"bar"}}
}
testPairs.push(['{"hmm":{"foo":"bar"}}', {"hmm":hmm}]);
testPairs.push(['{"foo":"bar"}', hmm]); // on the root
// toJSON on prototype
var Y = function() {
this.d = "e";
}
Y.prototype = {
not:"there?",
toJSON: function() { return {"foo":"bar"}}
};
var y = new Y();
testPairs.push(['{"foo":"bar"}', y.toJSON()]);
testPairs.push(['{"foo":"bar"}', y]);
// return undefined from toJSON
var hmm = {
toJSON: function() { return; }
}
testPairs.push(['{}', {"hmm":hmm}]);
// array with named prop
var x= new Array();
x[0] = 1;
x.foo = "bar";
//testPairs.push(['[1]', x]);
// prototype
var X = function() { this.a = "b" }
X.prototype = {c:"d"}
var y = new X();
testPairs.push(['{"a":"b","c":"d"}', y]);
// useless roots will be dropped
testPairs.push([null, null]);
testPairs.push([null, ""]);
testPairs.push([null, undefined]);
testPairs.push([null, 5]);
// custom iterator: JS 1.7+
var x = {
"a": "foo",
b: "not included",
c: "bar",
"4": "qux",
__iterator__: function() { return (function() { yield "a"; yield "c"; yield 4; })() }
}
do_check_eq('{"a":"foo","c":"bar","4":"qux"}', nativeJSON.encode(x));
return testPairs;
}
function testStringEncode() {
// test empty arg
do_check_eq(null, nativeJSON.encode());
var pairs = getTestPairs();
for each(pair in pairs) {
var nativeResult = nativeJSON.encode(pair[1]);
var crockfordResult = JSON.stringify(pair[1]);
do_check_eq(pair[0], nativeResult);
// Don't follow json2.js handling of non-objects
if (pair[1] && (typeof pair[1] == "object")) {
do_check_eq(crockfordResult, nativeResult);
}
}
}
function testOutputStreams() {
function writeToFile(obj, charset, writeBOM) {
var jsonFile = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
jsonFile.initWithFile(outputDir);
jsonFile.append("test.json");
jsonFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0600);
var stream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(Ci.nsIFileOutputStream);
try {
stream.init(jsonFile, 0x04 | 0x08 | 0x20, 0600, 0); // write, create, truncate
nativeJSON.encodeToStream(stream, charset, writeBOM, obj);
} finally {
stream.close();
}
return jsonFile;
}
var pairs = getTestPairs();
for each(pair in pairs) {
if (pair[1] && (typeof pair[1] == "object")) {
var utf8File = writeToFile(pair[1], "UTF-8", false);
var utf16LEFile = writeToFile(pair[1], "UTF-16LE", false);
var utf16BEFile = writeToFile(pair[1], "UTF-16BE", false);
var utf32LEFile = writeToFile(pair[1], "UTF-32LE", false);
var utf32BEFile = writeToFile(pair[1], "UTF-32BE", false);
// all ascii with no BOMs, so this will work
do_check_eq(utf16LEFile.fileSize / 2, utf8File.fileSize);
do_check_eq(utf32LEFile.fileSize / 4, utf8File.fileSize);
do_check_eq(utf16LEFile.fileSize, utf16BEFile.fileSize);
do_check_eq(utf32LEFile.fileSize, utf32BEFile.fileSize);
}
}
// check BOMs
var f = writeToFile({},"UTF-8", true);
do_check_eq(f.fileSize, 5);
var f = writeToFile({},"UTF-16LE", true);
do_check_eq(f.fileSize, 6);
var f = writeToFile({},"UTF-16BE", true);
do_check_eq(f.fileSize, 6);
var f = writeToFile({},"UTF-32LE", true);
do_check_eq(f.fileSize, 12);
var f = writeToFile({},"UTF-32BE", true);
do_check_eq(f.fileSize, 12);
outputDir.remove(true);
}
function throwingToJSON() {
var a = {
"b": 1,
"c": 2,
toJSON: function() { throw("uh oh"); }
}
try {
var y = nativeJSON.encode(a);
} catch (ex) {}
}
function throwingIterator() {
var a = {
"b": 1,
"c": 2,
__iterator__: function() { yield "b"; throw("uh oh"); }
}
try {
var y = nativeJSON.encode(a);
} catch (ex) {}
}
function deletingIter(x) {
return function() {
yield "dd";
print("after first yield");
delete x["a"]["c"];
gc();
print("about to yield second");
yield "ddddd";
}
}
function deleteDuringEncode() {
var x = {};
x.a = {
b: 1,
bb: 2,
bbb: 3,
c: {
cc: 2,
ccc: 3,
d: {
dd: 2,
ddd: 3,
__iterator__: deletingIter(x),
dddd: 4,
ddddd: 5
},
cccc: 4,
ccccc: 5
},
bbbb: 4,
bbbbb: 5,
bbbbbb: 6
};
var z = nativeJSON.encode(x);
print(z);
}
function run_test() {
testStringEncode();
testOutputStreams();
throwingToJSON();
throwingIterator();
deleteDuringEncode();
}

View File

@ -136,6 +136,7 @@ EXPORTS = \
jsdate.h \
jsdbgapi.h \
jsdhash.h \
jsdtoa.h \
jsemit.h \
jsfun.h \
jsgc.h \

View File

@ -328,7 +328,7 @@ js_GetNativeIteratorFlags(JSContext *cx, JSObject *iterobj)
* Call ToObject(v).__iterator__(keyonly) if ToObject(v).__iterator__ exists.
* Otherwise construct the defualt iterator.
*/
JSBool
JS_FRIEND_API(JSBool)
js_ValueToIterator(JSContext *cx, uintN flags, jsval *vp)
{
JSObject *obj;
@ -439,7 +439,7 @@ js_ValueToIterator(JSContext *cx, uintN flags, jsval *vp)
goto out;
}
JSBool
JS_FRIEND_API(JSBool)
js_CloseIterator(JSContext *cx, jsval v)
{
JSObject *obj;
@ -593,7 +593,7 @@ CallEnumeratorNext(JSContext *cx, JSObject *iterobj, uintN flags, jsval *rval)
return JS_TRUE;
}
JSBool
JS_FRIEND_API(JSBool)
js_CallIteratorNext(JSContext *cx, JSObject *iterobj, jsval *rval)
{
uintN flags;

View File

@ -58,17 +58,17 @@ JS_BEGIN_EXTERN_C
* for-in semantics are required, and when the caller can guarantee that the
* iterator will never be exposed to scripts.
*/
extern JSBool
extern JS_FRIEND_API(JSBool)
js_ValueToIterator(JSContext *cx, uintN flags, jsval *vp);
extern JSBool
extern JS_FRIEND_API(JSBool)
js_CloseIterator(JSContext *cx, jsval v);
/*
* Given iterobj, call iterobj.next(). If the iterator stopped, set *rval to
* JSVAL_HOLE. Otherwise set it to the result of the next call.
*/
extern JSBool
extern JS_FRIEND_API(JSBool)
js_CallIteratorNext(JSContext *cx, JSObject *iterobj, jsval *rval);
/*

View File

@ -141,6 +141,7 @@ SHARED_LIBRARY_LIBS = \
$(DEPTH)/view/src/$(LIB_PREFIX)gkview_s.$(LIB_SUFFIX) \
$(DEPTH)/dom/src/base/$(LIB_PREFIX)jsdombase_s.$(LIB_SUFFIX) \
$(DEPTH)/dom/src/events/$(LIB_PREFIX)jsdomevents_s.$(LIB_SUFFIX) \
$(DEPTH)/dom/src/json/$(LIB_PREFIX)json_s.$(LIB_SUFFIX) \
$(DEPTH)/dom/src/jsurl/$(LIB_PREFIX)jsurl_s.$(LIB_SUFFIX) \
$(DEPTH)/dom/src/storage/$(LIB_PREFIX)jsdomstorage_s.$(LIB_SUFFIX) \
$(DEPTH)/dom/src/offline/$(LIB_PREFIX)jsdomoffline_s.$(LIB_SUFFIX) \
@ -277,6 +278,7 @@ LOCAL_INCLUDES += -I$(srcdir)/../base \
-I$(topsrcdir)/content/xbl/src \
-I$(topsrcdir)/view/src \
-I$(topsrcdir)/dom/src/base \
-I$(topsrcdir)/dom/src/json \
-I$(topsrcdir)/dom/src/jsurl \
-I$(topsrcdir)/dom/src/storage \
-I$(topsrcdir)/dom/src/offline \

View File

@ -222,4 +222,7 @@
#define NS_XULPOPUPMANAGER_CID \
{ 0x14632191, 0xac21, 0x4bdf, { 0x83, 0xe7, 0x23, 0x63, 0xad, 0x17, 0xe8, 0x38 } }
// {93ad72a6-02cd-4716-9626-d47d5ec275ec}
#define NS_DOMJSON_CID \
{ 0x93ad72a6, 0x02cd, 0x4716, { 0x96, 0x26, 0xd4, 0x7d, 0x5e, 0xc2, 0x75, 0xec } }
#endif /* nsLayoutCID_h__ */

View File

@ -125,6 +125,7 @@
#include "nsIControllerContext.h"
#include "nsDOMScriptObjectFactory.h"
#include "nsDOMStorage.h"
#include "nsJSON.h"
// Editor stuff
#include "nsEditorCID.h"
@ -1321,6 +1322,11 @@ static const nsModuleComponentInfo gComponents[] = {
"@mozilla.org/dom/storagemanager;1",
nsDOMStorageManagerConstructor },
{ "DOM JSON",
NS_DOMJSON_CID,
"@mozilla.org/dom/json;1",
NS_NewJSON },
{ "Text Editor",
NS_TEXTEDITOR_CID,
"@mozilla.org/editor/texteditor;1",

View File

@ -463,34 +463,8 @@ static nsresult GetCharsetFromData(const unsigned char* aStyleSheetData,
step = 1;
pos = 0;
}
else if (aStyleSheetData[0] == 0xEF &&
aStyleSheetData[1] == 0xBB &&
aStyleSheetData[2] == 0xBF) {
// UTF-8 BOM
step = 1;
pos = 3;
aCharset = "UTF-8";
}
// Check for a 4-byte encoding BOM before checking for a 2-byte one,
// since the latter can be a proper subset of the former.
else if (aStyleSheetData[0] == 0x00 &&
aStyleSheetData[1] == 0x00 &&
aStyleSheetData[2] == 0xFE &&
aStyleSheetData[3] == 0xFF) {
// big-endian 4-byte encoding BOM
step = 4;
pos = 7;
aCharset = "UTF-32BE";
}
else if (aStyleSheetData[0] == 0xFF &&
aStyleSheetData[1] == 0xFE &&
aStyleSheetData[2] == 0x00 &&
aStyleSheetData[3] == 0x00) {
// little-endian 4-byte encoding BOM
step = 4;
pos = 4;
aCharset = "UTF-32LE";
}
else if (aStyleSheetData[0] == 0x00 &&
aStyleSheetData[1] == 0x00 &&
aStyleSheetData[2] == 0xFF &&
@ -511,17 +485,28 @@ static nsresult GetCharsetFromData(const unsigned char* aStyleSheetData,
pos = 5;
aCharset = "UTF-32";
}
else if (aStyleSheetData[0] == 0xFE && aStyleSheetData[1] == 0xFF) {
// big-endian 2-byte encoding BOM
step = 2;
pos = 3;
aCharset = "UTF-16BE";
}
else if (aStyleSheetData[0] == 0xFF && aStyleSheetData[1] == 0xFE) {
// little-endian 2-byte encoding BOM
step = 2;
pos = 2;
aCharset = "UTF-16LE";
else if (nsContentUtils::CheckForBOM(aStyleSheetData,
aDataLength, aCharset)) {
if (aCharset.Equals("UTF-8")) {
step = 1;
pos = 3;
}
else if (aCharset.Equals("UTF-32BE")) {
step = 4;
pos = 7;
}
else if (aCharset.Equals("UTF-32LE")) {
step = 4;
pos = 4;
}
else if (aCharset.Equals("UTF-16BE")) {
step = 2;
pos = 3;
}
else if (aCharset.Equals("UTF-16LE")) {
step = 2;
pos = 2;
}
}
else if (aStyleSheetData[0] == 0x00 &&
aStyleSheetData[1] == 0x00 &&

View File

@ -111,7 +111,7 @@ done
for t in $testdir/test_*.js
do
echo -n "$t: "
NATIVE_TOPSRCDIR="$native_topsrcdir" TOPSRCDIR="$topsrcdir" $xpcshell -s $headfiles -f $t $tailfiles 2> $t.log 1>&2
NATIVE_TOPSRCDIR="$native_topsrcdir" TOPSRCDIR="$topsrcdir" $xpcshell -v 180 -s $headfiles -f $t $tailfiles 2> $t.log 1>&2
rv="$?"
if [ ! "$rv" = "0" -o \
`grep -c '\*\*\* PASS' $t.log` = 0 ]