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 : * ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * The Original Code is JSAPI tests.
18 : *
19 : * The Initial Developer of the Original Code is
20 : * Mozilla Corporation.
21 : * Portions created by the Initial Developer are Copyright (C) 2009
22 : * the Initial Developer. All Rights Reserved.
23 : *
24 : * Contributor(s):
25 : * Jason Orendorff
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either of the GNU General Public License Version 2 or later (the "GPL"),
29 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : #include "mozilla/Util.h"
42 :
43 : #include "jsapi.h"
44 : #include "jsprvtd.h"
45 : #include "jsalloc.h"
46 :
47 : #include "js/Vector.h"
48 :
49 : #include <errno.h>
50 : #include <string.h>
51 : #include <stdio.h>
52 : #include <stdlib.h>
53 :
54 : class jsvalRoot
55 : {
56 : public:
57 171 : explicit jsvalRoot(JSContext *context, jsval value = JSVAL_NULL)
58 171 : : cx(context), v(value)
59 : {
60 171 : if (!JS_AddValueRoot(cx, &v)) {
61 0 : fprintf(stderr, "Out of memory in jsvalRoot constructor, aborting\n");
62 0 : abort();
63 : }
64 171 : }
65 :
66 171 : ~jsvalRoot() { JS_RemoveValueRoot(cx, &v); }
67 :
68 227 : operator jsval() const { return value(); }
69 :
70 108 : jsvalRoot & operator=(jsval value) {
71 108 : v = value;
72 108 : return *this;
73 : }
74 :
75 75 : jsval * addr() { return &v; }
76 248 : jsval value() const { return v; }
77 :
78 : private:
79 : JSContext *cx;
80 : jsval v;
81 : };
82 :
83 : /* Note: Aborts on OOM. */
84 92 : class JSAPITestString {
85 : js::Vector<char, 0, js::SystemAllocPolicy> chars;
86 : public:
87 92 : JSAPITestString() {}
88 0 : JSAPITestString(const char *s) { *this += s; }
89 0 : JSAPITestString(const JSAPITestString &s) { *this += s; }
90 :
91 0 : const char *begin() const { return chars.begin(); }
92 : const char *end() const { return chars.end(); }
93 0 : size_t length() const { return chars.length(); }
94 :
95 0 : JSAPITestString & operator +=(const char *s) {
96 0 : if (!chars.append(s, strlen(s)))
97 0 : abort();
98 0 : return *this;
99 : }
100 :
101 0 : JSAPITestString & operator +=(const JSAPITestString &s) {
102 0 : if (!chars.append(s.begin(), s.length()))
103 0 : abort();
104 0 : return *this;
105 : }
106 : };
107 :
108 0 : inline JSAPITestString operator+(JSAPITestString a, const char *b) { return a += b; }
109 0 : inline JSAPITestString operator+(JSAPITestString a, const JSAPITestString &b) { return a += b; }
110 :
111 : class JSAPITest
112 : {
113 : public:
114 : static JSAPITest *list;
115 : JSAPITest *next;
116 :
117 : JSRuntime *rt;
118 : JSContext *cx;
119 : JSObject *global;
120 : bool knownFail;
121 : JSAPITestString msgs;
122 : JSCrossCompartmentCall *call;
123 :
124 92 : JSAPITest() : rt(NULL), cx(NULL), global(NULL), knownFail(false), call(NULL) {
125 92 : next = list;
126 92 : list = this;
127 92 : }
128 :
129 92 : virtual ~JSAPITest() { uninit(); }
130 :
131 92 : virtual bool init() {
132 92 : rt = createRuntime();
133 92 : if (!rt)
134 0 : return false;
135 92 : cx = createContext();
136 92 : if (!cx)
137 0 : return false;
138 92 : JS_BeginRequest(cx);
139 92 : global = createGlobal();
140 92 : if (!global)
141 0 : return false;
142 92 : call = JS_EnterCrossCompartmentCall(cx, global);
143 92 : return call != NULL;
144 : }
145 :
146 184 : virtual void uninit() {
147 184 : if (call) {
148 92 : JS_LeaveCrossCompartmentCall(call);
149 92 : call = NULL;
150 : }
151 184 : if (cx) {
152 92 : JS_EndRequest(cx);
153 92 : JS_DestroyContext(cx);
154 92 : cx = NULL;
155 : }
156 184 : if (rt) {
157 92 : destroyRuntime();
158 92 : rt = NULL;
159 : }
160 184 : }
161 :
162 : virtual const char * name() = 0;
163 : virtual bool run() = 0;
164 :
165 : #define EXEC(s) do { if (!exec(s, __FILE__, __LINE__)) return false; } while (false)
166 :
167 27 : bool exec(const char *bytes, const char *filename, int lineno) {
168 54 : jsvalRoot v(cx);
169 27 : return JS_EvaluateScript(cx, global, bytes, strlen(bytes), filename, lineno, v.addr()) ||
170 54 : fail(bytes, filename, lineno);
171 : }
172 :
173 : #define EVAL(s, vp) do { if (!evaluate(s, __FILE__, __LINE__, vp)) return false; } while (false)
174 :
175 127 : bool evaluate(const char *bytes, const char *filename, int lineno, jsval *vp) {
176 127 : return JS_EvaluateScript(cx, global, bytes, strlen(bytes), filename, lineno, vp) ||
177 254 : fail(bytes, filename, lineno);
178 : }
179 :
180 0 : JSAPITestString jsvalToSource(jsval v) {
181 0 : JSString *str = JS_ValueToSource(cx, v);
182 0 : if (str) {
183 0 : JSAutoByteString bytes(cx, str);
184 0 : if (!!bytes)
185 0 : return JSAPITestString(bytes.ptr());
186 : }
187 0 : JS_ClearPendingException(cx);
188 0 : return JSAPITestString("<<error converting value to string>>");
189 : }
190 :
191 0 : JSAPITestString toSource(long v) {
192 : char buf[40];
193 0 : sprintf(buf, "%ld", v);
194 0 : return JSAPITestString(buf);
195 : }
196 :
197 0 : JSAPITestString toSource(unsigned long v) {
198 : char buf[40];
199 0 : sprintf(buf, "%lu", v);
200 0 : return JSAPITestString(buf);
201 : }
202 :
203 : JSAPITestString toSource(long long v) {
204 : char buf[40];
205 : sprintf(buf, "%lld", v);
206 : return JSAPITestString(buf);
207 : }
208 :
209 : JSAPITestString toSource(unsigned long long v) {
210 : char buf[40];
211 : sprintf(buf, "%llu", v);
212 : return JSAPITestString(buf);
213 : }
214 :
215 0 : JSAPITestString toSource(unsigned int v) {
216 0 : return toSource((unsigned long)v);
217 : }
218 :
219 0 : JSAPITestString toSource(int v) {
220 0 : return toSource((long)v);
221 : }
222 :
223 0 : JSAPITestString toSource(bool v) {
224 0 : return JSAPITestString(v ? "true" : "false");
225 : }
226 :
227 0 : JSAPITestString toSource(JSAtom *v) {
228 0 : return jsvalToSource(STRING_TO_JSVAL((JSString*)v));
229 : }
230 :
231 0 : JSAPITestString toSource(JSVersion v) {
232 0 : return JSAPITestString(JS_VersionToString(v));
233 : }
234 :
235 : template<typename T>
236 47 : bool checkEqual(const T &actual, const T &expected,
237 : const char *actualExpr, const char *expectedExpr,
238 : const char *filename, int lineno) {
239 : return (actual == expected) ||
240 : fail(JSAPITestString("CHECK_EQUAL failed: expected (") +
241 : expectedExpr + ") = " + toSource(expected) +
242 47 : ", got (" + actualExpr + ") = " + toSource(actual), filename, lineno);
243 : }
244 :
245 : // There are many cases where the static types of 'actual' and 'expected'
246 : // are not identical, and C++ is understandably cautious about automatic
247 : // coercions. So catch those cases and forcibly coerce, then use the
248 : // identical-type specialization. This may do bad things if the types are
249 : // actually *not* compatible.
250 : template<typename T, typename U>
251 25 : bool checkEqual(const T &actual, const U &expected,
252 : const char *actualExpr, const char *expectedExpr,
253 : const char *filename, int lineno) {
254 25 : return checkEqual(U(actual), expected, actualExpr, expectedExpr, filename, lineno);
255 : }
256 :
257 : #define CHECK_EQUAL(actual, expected) \
258 : do { \
259 : if (!checkEqual(actual, expected, #actual, #expected, __FILE__, __LINE__)) \
260 : return false; \
261 : } while (false)
262 :
263 1531 : bool checkSame(jsval actual, jsval expected,
264 : const char *actualExpr, const char *expectedExpr,
265 : const char *filename, int lineno) {
266 : JSBool same;
267 1531 : return (JS_SameValue(cx, actual, expected, &same) && same) ||
268 0 : fail(JSAPITestString("CHECK_SAME failed: expected JS_SameValue(cx, ") +
269 0 : actualExpr + ", " + expectedExpr + "), got !JS_SameValue(cx, " +
270 10717 : jsvalToSource(actual) + ", " + jsvalToSource(expected) + ")", filename, lineno);
271 : }
272 :
273 : #define CHECK_SAME(actual, expected) \
274 : do { \
275 : if (!checkSame(actual, expected, #actual, #expected, __FILE__, __LINE__)) \
276 : return false; \
277 : } while (false)
278 :
279 : #define CHECK(expr) \
280 : do { \
281 : if (!(expr)) \
282 : return fail("CHECK failed: " #expr, __FILE__, __LINE__); \
283 : } while (false)
284 :
285 0 : bool fail(JSAPITestString msg = JSAPITestString(), const char *filename = "-", int lineno = 0) {
286 0 : if (JS_IsExceptionPending(cx)) {
287 0 : jsvalRoot v(cx);
288 0 : JS_GetPendingException(cx, v.addr());
289 0 : JS_ClearPendingException(cx);
290 0 : JSString *s = JS_ValueToString(cx, v);
291 0 : if (s) {
292 0 : JSAutoByteString bytes(cx, s);
293 0 : if (!!bytes)
294 0 : msg += bytes.ptr();
295 : }
296 : }
297 0 : fprintf(stderr, "%s:%d:%.*s\n", filename, lineno, (int) msg.length(), msg.begin());
298 0 : msgs += msg;
299 0 : return false;
300 : }
301 :
302 0 : JSAPITestString messages() const { return msgs; }
303 :
304 102 : static JSClass * basicGlobalClass() {
305 : static JSClass c = {
306 : "global", JSCLASS_GLOBAL_FLAGS,
307 : JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
308 : JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
309 : JSCLASS_NO_OPTIONAL_MEMBERS
310 : };
311 102 : return &c;
312 : }
313 :
314 : protected:
315 : static JSBool
316 : print(JSContext *cx, unsigned argc, jsval *vp)
317 : {
318 : jsval *argv = JS_ARGV(cx, vp);
319 : for (unsigned i = 0; i < argc; i++) {
320 : JSString *str = JS_ValueToString(cx, argv[i]);
321 : if (!str)
322 : return JS_FALSE;
323 : char *bytes = JS_EncodeString(cx, str);
324 : if (!bytes)
325 : return JS_FALSE;
326 : printf("%s%s", i ? " " : "", bytes);
327 : JS_free(cx, bytes);
328 : }
329 :
330 : putchar('\n');
331 : fflush(stdout);
332 : JS_SET_RVAL(cx, vp, JSVAL_VOID);
333 : return JS_TRUE;
334 : }
335 :
336 : bool definePrint() {
337 : return JS_DefineFunction(cx, global, "print", (JSNative) print, 0, 0);
338 : }
339 :
340 90 : virtual JSRuntime * createRuntime() {
341 90 : JSRuntime *rt = JS_NewRuntime(8L * 1024 * 1024);
342 90 : if (!rt)
343 0 : return NULL;
344 :
345 : const size_t MAX_STACK_SIZE =
346 : /* Assume we can't use more than 5e5 bytes of C stack by default. */
347 : #if (defined(DEBUG) && defined(__SUNPRO_CC)) || defined(JS_CPU_SPARC)
348 : /*
349 : * Sun compiler uses a larger stack space for js::Interpret() with
350 : * debug. Use a bigger gMaxStackSize to make "make check" happy.
351 : */
352 : 5000000
353 : #else
354 90 : 500000
355 : #endif
356 : ;
357 :
358 90 : JS_SetNativeStackQuota(rt, MAX_STACK_SIZE);
359 90 : return rt;
360 : }
361 :
362 91 : virtual void destroyRuntime() {
363 91 : JS_ASSERT(!cx);
364 91 : JS_ASSERT(rt);
365 91 : JS_DestroyRuntime(rt);
366 91 : }
367 :
368 0 : static void reportError(JSContext *cx, const char *message, JSErrorReport *report) {
369 : fprintf(stderr, "%s:%u:%s\n",
370 : report->filename ? report->filename : "<no filename>",
371 : (unsigned int) report->lineno,
372 0 : message);
373 0 : }
374 :
375 92 : virtual JSContext * createContext() {
376 92 : JSContext *cx = JS_NewContext(rt, 8192);
377 92 : if (!cx)
378 0 : return NULL;
379 92 : JS_SetOptions(cx, JSOPTION_VAROBJFIX);
380 92 : JS_SetVersion(cx, JSVERSION_LATEST);
381 92 : JS_SetErrorReporter(cx, &reportError);
382 92 : return cx;
383 : }
384 :
385 102 : virtual JSClass * getGlobalClass() {
386 102 : return basicGlobalClass();
387 : }
388 :
389 96 : virtual JSObject * createGlobal(JSPrincipals *principals = NULL) {
390 : /* Create the global object. */
391 96 : JSObject *global = JS_NewCompartmentAndGlobalObject(cx, getGlobalClass(), principals);
392 96 : if (!global)
393 0 : return NULL;
394 :
395 192 : JSAutoEnterCompartment ac;
396 96 : if (!ac.enter(cx, global))
397 0 : return NULL;
398 :
399 : /* Populate the global object with the standard globals,
400 : like Object and Array. */
401 96 : if (!JS_InitStandardClasses(cx, global))
402 0 : return NULL;
403 96 : return global;
404 : }
405 : };
406 :
407 : #define BEGIN_TEST(testname) \
408 : class cls_##testname : public JSAPITest { \
409 : public: \
410 : virtual const char * name() { return #testname; } \
411 : virtual bool run()
412 :
413 : #define END_TEST(testname) \
414 : }; \
415 : static cls_##testname cls_##testname##_instance;
416 :
417 : /*
418 : * A "fixture" is a subclass of JSAPITest that holds common definitions for a
419 : * set of tests. Each test that wants to use the fixture should use
420 : * BEGIN_FIXTURE_TEST and END_FIXTURE_TEST, just as one would use BEGIN_TEST and
421 : * END_TEST, but include the fixture class as the first argument. The fixture
422 : * class's declarations are then in scope for the test bodies.
423 : */
424 :
425 : #define BEGIN_FIXTURE_TEST(fixture, testname) \
426 : class cls_##testname : public fixture { \
427 : public: \
428 : virtual const char * name() { return #testname; } \
429 : virtual bool run()
430 :
431 : #define END_FIXTURE_TEST(fixture, testname) \
432 : }; \
433 : static cls_##testname cls_##testname##_instance;
434 :
435 : /*
436 : * A class for creating and managing one temporary file.
437 : *
438 : * We could use the ISO C temporary file functions here, but those try to
439 : * create files in the root directory on Windows, which fails for users
440 : * without Administrator privileges.
441 : */
442 : class TempFile {
443 : const char *name;
444 : FILE *stream;
445 :
446 : public:
447 5 : TempFile() : name(), stream() { }
448 5 : ~TempFile() {
449 5 : if (stream)
450 3 : close();
451 5 : if (name)
452 3 : remove();
453 5 : }
454 :
455 : /*
456 : * Return a stream for a temporary file named |fileName|. Infallible.
457 : * Use only once per TempFile instance. If the file is not explicitly
458 : * closed and deleted via the member functions below, this object's
459 : * destructor will clean them up.
460 : */
461 5 : FILE *open(const char *fileName)
462 : {
463 5 : stream = fopen(fileName, "wb+");
464 5 : if (!stream) {
465 : fprintf(stderr, "error opening temporary file '%s': %s\n",
466 0 : fileName, strerror(errno));
467 0 : exit(1);
468 : }
469 5 : name = fileName;
470 5 : return stream;
471 : }
472 :
473 : /* Close the temporary file's stream. */
474 5 : void close() {
475 5 : if (fclose(stream) == EOF) {
476 : fprintf(stderr, "error closing temporary file '%s': %s\n",
477 0 : name, strerror(errno));
478 0 : exit(1);
479 : }
480 5 : stream = NULL;
481 5 : }
482 :
483 : /* Delete the temporary file. */
484 5 : void remove() {
485 5 : if (::remove(name) != 0) {
486 : fprintf(stderr, "error deleting temporary file '%s': %s\n",
487 0 : name, strerror(errno));
488 0 : exit(1);
489 : }
490 5 : name = NULL;
491 5 : }
492 : };
|