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