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