]> git.saurik.com Git - apple/javascriptcore.git/blob - kjs/testkjs.cpp
25c807e6658ecb691e7449b5e76b7dbd94e420bb
[apple/javascriptcore.git] / kjs / testkjs.cpp
1 // -*- c-basic-offset: 2 -*-
2 /*
3 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
4 * Copyright (C) 2004-2007 Apple Inc.
5 * Copyright (C) 2006 Bjoern Graf (bjoern.graf@gmail.com)
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB. If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 *
22 */
23
24 #include "config.h"
25
26 #include "JSGlobalObject.h"
27 #include "JSLock.h"
28 #include "Parser.h"
29 #include "collector.h"
30 #include "interpreter.h"
31 #include "nodes.h"
32 #include "object.h"
33 #include "protect.h"
34 #include <math.h>
35 #include <stdio.h>
36 #include <string.h>
37 #include <wtf/Assertions.h>
38 #include <wtf/HashTraits.h>
39
40 #if HAVE(SYS_TIME_H)
41 #include <sys/time.h>
42 #endif
43
44 #if PLATFORM(WIN_OS)
45 #include <crtdbg.h>
46 #include <windows.h>
47 #endif
48
49 #if PLATFORM(QT)
50 #include <QDateTime>
51 #endif
52
53 using namespace KJS;
54 using namespace WTF;
55
56 static bool fillBufferWithContentsOfFile(const UString& fileName, Vector<char>& buffer);
57
58 class StopWatch
59 {
60 public:
61 void start();
62 void stop();
63 long getElapsedMS(); // call stop() first
64
65 private:
66 #if PLATFORM(QT)
67 uint m_startTime;
68 uint m_stopTime;
69 #elif PLATFORM(WIN_OS)
70 DWORD m_startTime;
71 DWORD m_stopTime;
72 #else
73 // Windows does not have timeval, disabling this class for now (bug 7399)
74 timeval m_startTime;
75 timeval m_stopTime;
76 #endif
77 };
78
79 void StopWatch::start()
80 {
81 #if PLATFORM(QT)
82 QDateTime t = QDateTime::currentDateTime();
83 m_startTime = t.toTime_t() * 1000 + t.time().msec();
84 #elif PLATFORM(WIN_OS)
85 m_startTime = timeGetTime();
86 #else
87 gettimeofday(&m_startTime, 0);
88 #endif
89 }
90
91 void StopWatch::stop()
92 {
93 #if PLATFORM(QT)
94 QDateTime t = QDateTime::currentDateTime();
95 m_stopTime = t.toTime_t() * 1000 + t.time().msec();
96 #elif PLATFORM(WIN_OS)
97 m_stopTime = timeGetTime();
98 #else
99 gettimeofday(&m_stopTime, 0);
100 #endif
101 }
102
103 long StopWatch::getElapsedMS()
104 {
105 #if PLATFORM(WIN_OS) || PLATFORM(QT)
106 return m_stopTime - m_startTime;
107 #else
108 timeval elapsedTime;
109 timersub(&m_stopTime, &m_startTime, &elapsedTime);
110
111 return elapsedTime.tv_sec * 1000 + lroundf(elapsedTime.tv_usec / 1000.0f);
112 #endif
113 }
114
115 class GlobalImp : public JSGlobalObject {
116 public:
117 virtual UString className() const { return "global"; }
118 };
119 COMPILE_ASSERT(!IsInteger<GlobalImp>::value, WTF_IsInteger_GlobalImp_false);
120
121 class TestFunctionImp : public JSObject {
122 public:
123 enum TestFunctionType { Print, Debug, Quit, GC, Version, Run, Load };
124
125 TestFunctionImp(TestFunctionType i, int length);
126 virtual bool implementsCall() const { return true; }
127 virtual JSValue* callAsFunction(ExecState* exec, JSObject* thisObj, const List &args);
128
129 private:
130 TestFunctionType m_type;
131 };
132
133 TestFunctionImp::TestFunctionImp(TestFunctionType i, int length)
134 : JSObject()
135 , m_type(i)
136 {
137 putDirect(Identifier("length"), length, DontDelete | ReadOnly | DontEnum);
138 }
139
140 JSValue* TestFunctionImp::callAsFunction(ExecState* exec, JSObject*, const List &args)
141 {
142 switch (m_type) {
143 case Print:
144 printf("%s\n", args[0]->toString(exec).UTF8String().c_str());
145 return jsUndefined();
146 case Debug:
147 fprintf(stderr, "--> %s\n", args[0]->toString(exec).UTF8String().c_str());
148 return jsUndefined();
149 case GC:
150 {
151 JSLock lock;
152 Collector::collect();
153 return jsUndefined();
154 }
155 case Version:
156 // We need this function for compatibility with the Mozilla JS tests but for now
157 // we don't actually do any version-specific handling
158 return jsUndefined();
159 case Run:
160 {
161 StopWatch stopWatch;
162 UString fileName = args[0]->toString(exec);
163 Vector<char> script;
164 if (!fillBufferWithContentsOfFile(fileName, script))
165 return throwError(exec, GeneralError, "Could not open file.");
166
167 stopWatch.start();
168 Interpreter::evaluate(exec->dynamicGlobalObject()->globalExec(), fileName, 0, script.data());
169 stopWatch.stop();
170
171 return jsNumber(stopWatch.getElapsedMS());
172 }
173 case Load:
174 {
175 UString fileName = args[0]->toString(exec);
176 Vector<char> script;
177 if (!fillBufferWithContentsOfFile(fileName, script))
178 return throwError(exec, GeneralError, "Could not open file.");
179
180 Interpreter::evaluate(exec->dynamicGlobalObject()->globalExec(), fileName, 0, script.data());
181
182 return jsUndefined();
183 }
184 case Quit:
185 exit(0);
186 default:
187 abort();
188 }
189 return 0;
190 }
191
192 // Use SEH for Release builds only to get rid of the crash report dialog
193 // (luckily the same tests fail in Release and Debug builds so far). Need to
194 // be in a separate main function because the kjsmain function requires object
195 // unwinding.
196
197 #if PLATFORM(WIN_OS) && !defined(_DEBUG)
198 #define TRY __try {
199 #define EXCEPT(x) } __except (EXCEPTION_EXECUTE_HANDLER) { x; }
200 #else
201 #define TRY
202 #define EXCEPT(x)
203 #endif
204
205 int kjsmain(int argc, char** argv);
206
207 int main(int argc, char** argv)
208 {
209 #if defined(_DEBUG) && PLATFORM(WIN_OS)
210 _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
211 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
212 _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
213 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
214 _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
215 _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
216 #endif
217
218 int res = 0;
219 TRY
220 res = kjsmain(argc, argv);
221 EXCEPT(res = 3)
222 return res;
223 }
224
225 static GlobalImp* createGlobalObject()
226 {
227 GlobalImp* global = new GlobalImp;
228
229 // add debug() function
230 global->put(global->globalExec(), "debug", new TestFunctionImp(TestFunctionImp::Debug, 1));
231 // add "print" for compatibility with the mozilla js shell
232 global->put(global->globalExec(), "print", new TestFunctionImp(TestFunctionImp::Print, 1));
233 // add "quit" for compatibility with the mozilla js shell
234 global->put(global->globalExec(), "quit", new TestFunctionImp(TestFunctionImp::Quit, 0));
235 // add "gc" for compatibility with the mozilla js shell
236 global->put(global->globalExec(), "gc", new TestFunctionImp(TestFunctionImp::GC, 0));
237 // add "version" for compatibility with the mozilla js shell
238 global->put(global->globalExec(), "version", new TestFunctionImp(TestFunctionImp::Version, 1));
239 global->put(global->globalExec(), "run", new TestFunctionImp(TestFunctionImp::Run, 1));
240 global->put(global->globalExec(), "load", new TestFunctionImp(TestFunctionImp::Load, 1));
241
242 Interpreter::setShouldPrintExceptions(true);
243 return global;
244 }
245
246 static bool prettyPrintScript(const UString& fileName, const Vector<char>& script)
247 {
248 int errLine = 0;
249 UString errMsg;
250 UString scriptUString(script.data());
251 RefPtr<ProgramNode> programNode = parser().parse<ProgramNode>(fileName, 0, scriptUString.data(), scriptUString.size(), 0, &errLine, &errMsg);
252 if (!programNode) {
253 fprintf(stderr, "%s:%d: %s.\n", fileName.UTF8String().c_str(), errLine, errMsg.UTF8String().c_str());
254 return false;
255 }
256
257 printf("%s\n", programNode->toString().UTF8String().c_str());
258 return true;
259 }
260
261 static bool runWithScripts(const Vector<UString>& fileNames, bool prettyPrint)
262 {
263 GlobalImp* globalObject = createGlobalObject();
264 Vector<char> script;
265
266 bool success = true;
267
268 for (size_t i = 0; i < fileNames.size(); i++) {
269 UString fileName = fileNames[i];
270
271 if (!fillBufferWithContentsOfFile(fileName, script))
272 return false; // fail early so we can catch missing files
273
274 if (prettyPrint)
275 prettyPrintScript(fileName, script);
276 else {
277 Completion completion = Interpreter::evaluate(globalObject->globalExec(), fileName, 0, script.data());
278 success = success && completion.complType() != Throw;
279 }
280 }
281 return success;
282 }
283
284 static void parseArguments(int argc, char** argv, Vector<UString>& fileNames, bool& prettyPrint)
285 {
286 if (argc < 2) {
287 fprintf(stderr, "Usage: testkjs file1 [file2...]\n");
288 exit(-1);
289 }
290
291 for (int i = 1; i < argc; i++) {
292 const char* fileName = argv[i];
293 if (strcmp(fileName, "-f") == 0) // mozilla test driver script uses "-f" prefix for files
294 continue;
295 if (strcmp(fileName, "-p") == 0) {
296 prettyPrint = true;
297 continue;
298 }
299 fileNames.append(fileName);
300 }
301 }
302
303 int kjsmain(int argc, char** argv)
304 {
305 JSLock lock;
306
307 bool prettyPrint = false;
308 Vector<UString> fileNames;
309 parseArguments(argc, argv, fileNames, prettyPrint);
310
311 bool success = runWithScripts(fileNames, prettyPrint);
312
313 #ifndef NDEBUG
314 Collector::collect();
315 #endif
316
317 return success ? 0 : 3;
318 }
319
320 static bool fillBufferWithContentsOfFile(const UString& fileName, Vector<char>& buffer)
321 {
322 FILE* f = fopen(fileName.UTF8String().c_str(), "r");
323 if (!f) {
324 fprintf(stderr, "Could not open file: %s\n", fileName.UTF8String().c_str());
325 return false;
326 }
327
328 size_t buffer_size = 0;
329 size_t buffer_capacity = 1024;
330
331 buffer.resize(buffer_capacity);
332
333 while (!feof(f) && !ferror(f)) {
334 buffer_size += fread(buffer.data() + buffer_size, 1, buffer_capacity - buffer_size, f);
335 if (buffer_size == buffer_capacity) { // guarantees space for trailing '\0'
336 buffer_capacity *= 2;
337 buffer.resize(buffer_capacity);
338 }
339 }
340 fclose(f);
341 buffer[buffer_size] = '\0';
342
343 return true;
344 }