1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sw=4 et tw=99:
3 : */
4 :
5 : #include <limits>
6 : #include <math.h>
7 :
8 : #include "tests.h"
9 : #include "jsstr.h"
10 : #include "vm/String.h"
11 :
12 : using namespace js;
13 :
14 : class AutoInflatedString {
15 : JSContext * const cx;
16 : jschar *chars_;
17 : size_t length_;
18 :
19 : public:
20 38 : AutoInflatedString(JSContext *cx) : cx(cx), chars_(NULL), length_(0) { }
21 38 : ~AutoInflatedString() {
22 38 : JS_free(cx, chars_);
23 38 : }
24 :
25 38 : template<size_t N> void operator=(const char (&str)[N]) {
26 38 : length_ = N - 1;
27 38 : chars_ = InflateString(cx, str, &length_);
28 38 : if (!chars_)
29 0 : abort();
30 38 : }
31 :
32 38 : const jschar *chars() const { return chars_; }
33 38 : size_t length() const { return length_; }
34 : };
35 :
36 : template<size_t N> JSFlatString *
37 3 : NewString(JSContext *cx, const jschar (&chars)[N])
38 : {
39 3 : return js_NewStringCopyN(cx, chars, N);
40 : }
41 :
42 4 : BEGIN_TEST(testParseJSON_success)
43 : {
44 : // Primitives
45 1 : CHECK(TryParse(cx, "true", JSVAL_TRUE));
46 1 : CHECK(TryParse(cx, "false", JSVAL_FALSE));
47 1 : CHECK(TryParse(cx, "null", JSVAL_NULL));
48 1 : CHECK(TryParse(cx, "0", INT_TO_JSVAL(0)));
49 1 : CHECK(TryParse(cx, "1", INT_TO_JSVAL(1)));
50 1 : CHECK(TryParse(cx, "-1", INT_TO_JSVAL(-1)));
51 1 : CHECK(TryParse(cx, "1", DOUBLE_TO_JSVAL(1)));
52 1 : CHECK(TryParse(cx, "1.75", DOUBLE_TO_JSVAL(1.75)));
53 1 : CHECK(TryParse(cx, "9e9", DOUBLE_TO_JSVAL(9e9)));
54 1 : CHECK(TryParse(cx, "9e99999", DOUBLE_TO_JSVAL(std::numeric_limits<double>::infinity())));
55 :
56 : JSFlatString *str;
57 :
58 1 : const jschar emptystr[] = { '\0' };
59 1 : str = js_NewStringCopyN(cx, emptystr, 0);
60 1 : CHECK(str);
61 1 : CHECK(TryParse(cx, "\"\"", STRING_TO_JSVAL(str)));
62 :
63 1 : const jschar nullstr[] = { '\0' };
64 1 : str = NewString(cx, nullstr);
65 1 : CHECK(str);
66 1 : CHECK(TryParse(cx, "\"\\u0000\"", STRING_TO_JSVAL(str)));
67 :
68 1 : const jschar backstr[] = { '\b' };
69 1 : str = NewString(cx, backstr);
70 1 : CHECK(str);
71 1 : CHECK(TryParse(cx, "\"\\b\"", STRING_TO_JSVAL(str)));
72 1 : CHECK(TryParse(cx, "\"\\u0008\"", STRING_TO_JSVAL(str)));
73 :
74 1 : const jschar newlinestr[] = { '\n', };
75 1 : str = NewString(cx, newlinestr);
76 1 : CHECK(str);
77 1 : CHECK(TryParse(cx, "\"\\n\"", STRING_TO_JSVAL(str)));
78 1 : CHECK(TryParse(cx, "\"\\u000A\"", STRING_TO_JSVAL(str)));
79 :
80 :
81 : // Arrays
82 : jsval v, v2;
83 : JSObject *obj;
84 :
85 1 : CHECK(Parse(cx, "[]", &v));
86 1 : CHECK(!JSVAL_IS_PRIMITIVE(v));
87 1 : obj = JSVAL_TO_OBJECT(v);
88 1 : CHECK(JS_IsArrayObject(cx, obj));
89 1 : CHECK(JS_GetProperty(cx, obj, "length", &v2));
90 1 : CHECK_SAME(v2, JSVAL_ZERO);
91 :
92 1 : CHECK(Parse(cx, "[1]", &v));
93 1 : CHECK(!JSVAL_IS_PRIMITIVE(v));
94 1 : obj = JSVAL_TO_OBJECT(v);
95 1 : CHECK(JS_IsArrayObject(cx, obj));
96 1 : CHECK(JS_GetProperty(cx, obj, "0", &v2));
97 1 : CHECK_SAME(v2, JSVAL_ONE);
98 1 : CHECK(JS_GetProperty(cx, obj, "length", &v2));
99 1 : CHECK_SAME(v2, JSVAL_ONE);
100 :
101 :
102 : // Objects
103 1 : CHECK(Parse(cx, "{}", &v));
104 1 : CHECK(!JSVAL_IS_PRIMITIVE(v));
105 1 : obj = JSVAL_TO_OBJECT(v);
106 1 : CHECK(!JS_IsArrayObject(cx, obj));
107 :
108 1 : CHECK(Parse(cx, "{ \"f\": 17 }", &v));
109 1 : CHECK(!JSVAL_IS_PRIMITIVE(v));
110 1 : obj = JSVAL_TO_OBJECT(v);
111 1 : CHECK(!JS_IsArrayObject(cx, obj));
112 1 : CHECK(JS_GetProperty(cx, obj, "f", &v2));
113 1 : CHECK_SAME(v2, INT_TO_JSVAL(17));
114 :
115 1 : return true;
116 : }
117 :
118 : template<size_t N> inline bool
119 4 : Parse(JSContext *cx, const char (&input)[N], jsval *vp)
120 : {
121 8 : AutoInflatedString str(cx);
122 4 : str = input;
123 4 : CHECK(JS_ParseJSON(cx, str.chars(), str.length(), vp));
124 4 : return true;
125 : }
126 :
127 : template<size_t N> inline bool
128 16 : TryParse(JSContext *cx, const char (&input)[N], const jsval &expected)
129 : {
130 32 : AutoInflatedString str(cx);
131 : jsval v;
132 16 : str = input;
133 16 : CHECK(JS_ParseJSON(cx, str.chars(), str.length(), &v));
134 16 : CHECK_SAME(v, expected);
135 16 : return true;
136 : }
137 1 : END_TEST(testParseJSON_success)
138 :
139 4 : BEGIN_TEST(testParseJSON_error)
140 : {
141 1 : CHECK(Error(cx, "["));
142 1 : CHECK(Error(cx, "[,]"));
143 1 : CHECK(Error(cx, "[1,]"));
144 1 : CHECK(Error(cx, "{a:2}"));
145 1 : CHECK(Error(cx, "{\"a\":2,}"));
146 1 : CHECK(Error(cx, "]"));
147 1 : CHECK(Error(cx, "'bad string'"));
148 1 : CHECK(Error(cx, "\""));
149 1 : CHECK(Error(cx, "{]"));
150 1 : CHECK(Error(cx, "[}"));
151 1 : return true;
152 : }
153 :
154 : template<size_t N> inline bool
155 10 : Error(JSContext *cx, const char (&input)[N])
156 : {
157 20 : AutoInflatedString str(cx);
158 : jsval dummy;
159 10 : str = input;
160 :
161 10 : ContextPrivate p = {0, 0};
162 10 : CHECK(!JS_GetContextPrivate(cx));
163 10 : JS_SetContextPrivate(cx, &p);
164 10 : JSErrorReporter old = JS_SetErrorReporter(cx, reportJSONEror);
165 10 : JSBool ok = JS_ParseJSON(cx, str.chars(), str.length(), &dummy);
166 10 : JS_SetErrorReporter(cx, old);
167 10 : JS_SetContextPrivate(cx, NULL);
168 :
169 10 : CHECK(!ok);
170 10 : CHECK(!p.unexpectedErrorCount);
171 10 : CHECK(p.expectedErrorCount == 1);
172 :
173 : /* We do not execute JS, so there should be no exception thrown. */
174 10 : CHECK(!JS_IsExceptionPending(cx));
175 :
176 10 : return true;
177 : }
178 :
179 : struct ContextPrivate {
180 : unsigned unexpectedErrorCount;
181 : unsigned expectedErrorCount;
182 : };
183 :
184 : static void
185 10 : reportJSONEror(JSContext *cx, const char *message, JSErrorReport *report)
186 : {
187 10 : ContextPrivate *p = static_cast<ContextPrivate *>(JS_GetContextPrivate(cx));
188 10 : if (report->errorNumber == JSMSG_JSON_BAD_PARSE)
189 10 : p->expectedErrorCount++;
190 : else
191 0 : p->unexpectedErrorCount++;
192 10 : }
193 :
194 1 : END_TEST(testParseJSON_error)
195 :
196 : static JSBool
197 9 : Censor(JSContext *cx, unsigned argc, jsval *vp)
198 : {
199 9 : JS_ASSERT(argc == 2);
200 : #ifdef DEBUG
201 9 : jsval *argv = JS_ARGV(cx, vp);
202 9 : JS_ASSERT(JSVAL_IS_STRING(argv[0]));
203 : #endif
204 9 : JS_SET_RVAL(cx, vp, JSVAL_NULL);
205 9 : return true;
206 : }
207 :
208 4 : BEGIN_TEST(testParseJSON_reviver)
209 : {
210 1 : JSFunction *fun = JS_NewFunction(cx, Censor, 0, 0, global, "censor");
211 1 : CHECK(fun);
212 :
213 1 : jsval filter = OBJECT_TO_JSVAL(JS_GetFunctionObject(fun));
214 :
215 1 : CHECK(TryParse(cx, "true", filter));
216 1 : CHECK(TryParse(cx, "false", filter));
217 1 : CHECK(TryParse(cx, "null", filter));
218 1 : CHECK(TryParse(cx, "1", filter));
219 1 : CHECK(TryParse(cx, "1.75", filter));
220 1 : CHECK(TryParse(cx, "[]", filter));
221 1 : CHECK(TryParse(cx, "[1]", filter));
222 1 : CHECK(TryParse(cx, "{}", filter));
223 1 : return true;
224 : }
225 :
226 : template<size_t N> inline bool
227 8 : TryParse(JSContext *cx, const char (&input)[N], jsval filter)
228 : {
229 16 : AutoInflatedString str(cx);
230 : jsval v;
231 8 : str = input;
232 8 : CHECK(JS_ParseJSONWithReviver(cx, str.chars(), str.length(), filter, &v));
233 8 : CHECK_SAME(v, JSVAL_NULL);
234 8 : return true;
235 : }
236 3 : END_TEST(testParseJSON_reviver)
|