1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=99:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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
17 * The Original Code is JSAPI tests.
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.
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.
39 * ***** END LICENSE BLOCK ***** */
52 explicit jsvalRoot(JSContext *context, jsval value = JSVAL_NULL)
53 : cx(context), v(value)
55 if (!JS_AddValueRoot(cx, &v)) {
56 fprintf(stderr, "Out of memory in jsvalRoot constructor, aborting\n");
61 ~jsvalRoot() { JS_RemoveValueRoot(cx, &v); }
63 operator jsval() const { return value(); }
65 jsvalRoot & operator=(jsval value) {
70 jsval * addr() { return &v; }
71 jsval value() const { return v; }
78 /* Note: Aborts on OOM. */
79 class JSAPITestString {
80 js::Vector<char, 0, js::SystemAllocPolicy> chars;
83 JSAPITestString(const char *s) { *this += s; }
84 JSAPITestString(const JSAPITestString &s) { *this += s; }
86 const char *begin() const { return chars.begin(); }
87 const char *end() const { return chars.end(); }
88 size_t length() const { return chars.length(); }
90 JSAPITestString & operator +=(const char *s) {
91 if (!chars.append(s, strlen(s)))
96 JSAPITestString & operator +=(const JSAPITestString &s) {
97 if (!chars.append(s.begin(), s.length()))
103 inline JSAPITestString operator+(JSAPITestString a, const char *b) { return a += b; }
104 inline JSAPITestString operator+(JSAPITestString a, const JSAPITestString &b) { return a += b; }
109 static JSAPITest *list;
116 JSAPITestString msgs;
117 JSCrossCompartmentCall *call;
119 JSAPITest() : rt(NULL), cx(NULL), global(NULL), knownFail(false), call(NULL) {
124 virtual ~JSAPITest() { uninit(); }
126 virtual bool init() {
127 rt = createRuntime();
130 cx = createContext();
134 global = createGlobal();
137 call = JS_EnterCrossCompartmentCall(cx, global);
141 virtual void uninit() {
143 JS_LeaveCrossCompartmentCall(call);
148 JS_DestroyContext(cx);
157 virtual const char * name() = 0;
158 virtual bool run() = 0;
160 #define EXEC(s) do { if (!exec(s, __FILE__, __LINE__)) return false; } while (false)
162 bool exec(const char *bytes, const char *filename, int lineno) {
164 return JS_EvaluateScript(cx, global, bytes, strlen(bytes), filename, lineno, v.addr()) ||
165 fail(bytes, filename, lineno);
168 #define EVAL(s, vp) do { if (!evaluate(s, __FILE__, __LINE__, vp)) return false; } while (false)
170 bool evaluate(const char *bytes, const char *filename, int lineno, jsval *vp) {
171 return JS_EvaluateScript(cx, global, bytes, strlen(bytes), filename, lineno, vp) ||
172 fail(bytes, filename, lineno);
175 JSAPITestString toSource(jsval v) {
176 JSString *str = JS_ValueToSource(cx, v);
178 JSAutoByteString bytes(cx, str);
180 return JSAPITestString(bytes.ptr());
182 JS_ClearPendingException(cx);
183 return JSAPITestString("<<error converting value to string>>");
186 #define CHECK_SAME(actual, expected) \
188 if (!checkSame(actual, expected, #actual, #expected, __FILE__, __LINE__)) \
192 bool checkSame(jsval actual, jsval expected,
193 const char *actualExpr, const char *expectedExpr,
194 const char *filename, int lineno) {
196 return (JS_SameValue(cx, actual, expected, &same) && same) ||
197 fail(JSAPITestString("CHECK_SAME failed: expected JS_SameValue(cx, ") +
198 actualExpr + ", " + expectedExpr + "), got !JS_SameValue(cx, " +
199 toSource(actual) + ", " + toSource(expected) + ")", filename, lineno);
202 #define CHECK(expr) \
205 return fail("CHECK failed: " #expr, __FILE__, __LINE__); \
208 bool fail(JSAPITestString msg = JSAPITestString(), const char *filename = "-", int lineno = 0) {
209 if (JS_IsExceptionPending(cx)) {
211 JS_GetPendingException(cx, v.addr());
212 JS_ClearPendingException(cx);
213 JSString *s = JS_ValueToString(cx, v);
215 JSAutoByteString bytes(cx, s);
220 fprintf(stderr, "%s:%d:%.*s\n", filename, lineno, (int) msg.length(), msg.begin());
225 JSAPITestString messages() const { return msgs; }
227 static JSClass * basicGlobalClass() {
229 "global", JSCLASS_GLOBAL_FLAGS,
230 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
231 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
232 JSCLASS_NO_OPTIONAL_MEMBERS
239 print(JSContext *cx, uintN argc, jsval *vp)
241 jsval *argv = JS_ARGV(cx, vp);
242 for (uintN i = 0; i < argc; i++) {
243 JSString *str = JS_ValueToString(cx, argv[i]);
246 char *bytes = JS_EncodeString(cx, str);
249 printf("%s%s", i ? " " : "", bytes);
255 JS_SET_RVAL(cx, vp, JSVAL_VOID);
260 return JS_DefineFunction(cx, global, "print", (JSNative) print, 0, 0);
263 virtual JSRuntime * createRuntime() {
264 return JS_NewRuntime(8L * 1024 * 1024);
267 virtual void destroyRuntime() {
270 JS_DestroyRuntime(rt);
273 static void reportError(JSContext *cx, const char *message, JSErrorReport *report) {
274 fprintf(stderr, "%s:%u:%s\n",
275 report->filename ? report->filename : "<no filename>",
276 (unsigned int) report->lineno,
280 virtual JSContext * createContext() {
281 JSContext *cx = JS_NewContext(rt, 8192);
284 JS_SetOptions(cx, JSOPTION_VAROBJFIX | JSOPTION_JIT);
285 JS_SetVersion(cx, JSVERSION_LATEST);
286 JS_SetErrorReporter(cx, &reportError);
290 virtual JSClass * getGlobalClass() {
291 return basicGlobalClass();
294 virtual JSObject * createGlobal() {
295 /* Create the global object. */
296 JSObject *global = JS_NewCompartmentAndGlobalObject(cx, getGlobalClass(), NULL);
300 JSAutoEnterCompartment ac;
301 if (!ac.enter(cx, global))
304 /* Populate the global object with the standard globals,
305 like Object and Array. */
306 if (!JS_InitStandardClasses(cx, global))
312 #define BEGIN_TEST(testname) \
313 class cls_##testname : public JSAPITest { \
315 virtual const char * name() { return #testname; } \
318 #define END_TEST(testname) \
320 static cls_##testname cls_##testname##_instance;
323 * A "fixture" is a subclass of JSAPITest that holds common definitions for a
324 * set of tests. Each test that wants to use the fixture should use
325 * BEGIN_FIXTURE_TEST and END_FIXTURE_TEST, just as one would use BEGIN_TEST and
326 * END_TEST, but include the fixture class as the first argument. The fixture
327 * class's declarations are then in scope for the test bodies.
330 #define BEGIN_FIXTURE_TEST(fixture, testname) \
331 class cls_##testname : public fixture { \
333 virtual const char * name() { return #testname; } \
336 #define END_FIXTURE_TEST(fixture, testname) \
338 static cls_##testname cls_##testname##_instance;
341 * A class for creating and managing one temporary file.
343 * We could use the ISO C temporary file functions here, but those try to
344 * create files in the root directory on Windows, which fails for users
345 * without Administrator privileges.
352 TempFile() : name(), stream() { }
361 * Return a stream for a temporary file named |fileName|. Infallible.
362 * Use only once per TempFile instance. If the file is not explicitly
363 * closed and deleted via the member functions below, this object's
364 * destructor will clean them up.
366 FILE *open(const char *fileName)
368 stream = fopen(fileName, "wb+");
370 fprintf(stderr, "error opening temporary file '%s': %s\n",
371 fileName, strerror(errno));
378 /* Close the temporary file's stream. */
380 if (fclose(stream) == EOF) {
381 fprintf(stderr, "error closing temporary file '%s': %s\n",
382 name, strerror(errno));
388 /* Delete the temporary file. */
390 if (::remove(name) != 0) {
391 fprintf(stderr, "error deleting temporary file '%s': %s\n",
392 name, strerror(errno));