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