File indexing completed on 2024-04-28 15:28:39

0001 /*
0002  *  This file is part of the KDE libraries
0003  *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
0004  *  Copyright (C) 2004-2006 Apple Computer, Inc.
0005  *  Copyright (C) 2006 Björn Graf (bjoern.graf@gmail.com)
0006  *
0007  *  This library is free software; you can redistribute it and/or
0008  *  modify it under the terms of the GNU Library General Public
0009  *  License as published by the Free Software Foundation; either
0010  *  version 2 of the License, or (at your option) any later version.
0011  *
0012  *  This library is distributed in the hope that it will be useful,
0013  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
0014  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0015  *  Library General Public License for more details.
0016  *
0017  *  You should have received a copy of the GNU Library General Public License
0018  *  along with this library; see the file COPYING.LIB.  If not, write to
0019  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0020  *  Boston, MA 02110-1301, USA.
0021  *
0022  */
0023 
0024 #include "collector.h"
0025 
0026 #include <wtf/HashTraits.h>
0027 #include "JSLock.h"
0028 #include "object.h"
0029 #include "JSVariableObject.h"
0030 #include "Parser.h"
0031 
0032 #include <math.h>
0033 #include <stdio.h>
0034 #include <string.h>
0035 
0036 #include "global.h"
0037 #if HAVE_SYS_TIME_H
0038 #include <sys/time.h>
0039 #endif
0040 
0041 #include "protect.h"
0042 
0043 #if defined(WTF_COMPILER_MSVC)
0044 #include <windows.h>
0045 #include <timeapi.h>
0046 #if HAVE_CRTDBG_H
0047 #include <crtdbg.h>
0048 #endif
0049 #endif
0050 
0051 #if PLATFORM(WIN_OS) && ! defined(WTF_COMPILER_MSVC)
0052 #ifndef timersub
0053 # define timersub(a, b, result)                                               \
0054   do {                                                                        \
0055     (result)->tv_sec = (a)->tv_sec - (b)->tv_sec;                             \
0056     (result)->tv_usec = (a)->tv_usec - (b)->tv_usec;                          \
0057     if ((result)->tv_usec < 0) {                                              \
0058       --(result)->tv_sec;                                                     \
0059       (result)->tv_usec += 1000000;                                           \
0060     }                                                                         \
0061   } while (0)
0062 #endif
0063 #endif
0064 
0065 using namespace KJS;
0066 using namespace WTF;
0067 
0068 static void testIsInteger();
0069 static void testUString();
0070 static char *createStringWithContentsOfFile(const char *fileName);
0071 
0072 class StopWatch
0073 {
0074 public:
0075     void start();
0076     void stop();
0077     long getElapsedMS(); // call stop() first
0078 
0079 private:
0080 #if ! HAVE_GETTIMEOFDAY
0081     DWORD m_startTime;
0082     DWORD m_stopTime;
0083 #else
0084     // Windows does not have timeval, disabling this class for now (bug 7399)
0085     timeval m_startTime;
0086     timeval m_stopTime;
0087 #endif
0088 };
0089 
0090 void StopWatch::start()
0091 {
0092 #if ! HAVE_GETTIMEOFDAY
0093     m_startTime = timeGetTime();
0094 #else
0095     gettimeofday(&m_startTime, nullptr);
0096 #endif
0097 }
0098 
0099 void StopWatch::stop()
0100 {
0101 #if ! HAVE_GETTIMEOFDAY
0102     m_stopTime = timeGetTime();
0103 #else
0104     gettimeofday(&m_stopTime, nullptr);
0105 #endif
0106 }
0107 
0108 long StopWatch::getElapsedMS()
0109 {
0110 #if ! HAVE_GETTIMEOFDAY
0111     return m_stopTime - m_startTime;
0112 #else
0113     timeval elapsedTime;
0114     timersub(&m_stopTime, &m_startTime, &elapsedTime);
0115 
0116     return elapsedTime.tv_sec * 1000 + lroundf(elapsedTime.tv_usec / 1000.0f);
0117 #endif
0118 }
0119 
0120 class GlobalImp : public JSGlobalObject
0121 {
0122 public:
0123     UString className() const override
0124     {
0125         return "global";
0126     }
0127 };
0128 
0129 class TestFunctionImp : public JSObject
0130 {
0131 public:
0132     TestFunctionImp(int i, int length);
0133     bool implementsCall() const override
0134     {
0135         return true;
0136     }
0137     JSValue *callAsFunction(ExecState *exec, JSObject *thisObj, const List &args) override;
0138 
0139     enum { Print, Debug, Quit, GC, Version, Run };
0140 
0141 private:
0142     int id;
0143 };
0144 
0145 TestFunctionImp::TestFunctionImp(int i, int length) : JSObject(), id(i)
0146 {
0147     putDirect(Identifier("length"), length, DontDelete | ReadOnly | DontEnum);
0148 }
0149 
0150 JSValue *TestFunctionImp::callAsFunction(ExecState *exec, JSObject *, const List &args)
0151 {
0152     switch (id) {
0153     case Print:
0154         printf("--> %s\n", JSValue::toString(args[0], exec).UTF8String().c_str());
0155         return jsUndefined();
0156     case Debug:
0157         fprintf(stderr, "--> %s\n", JSValue::toString(args[0], exec).UTF8String().c_str());
0158         return jsUndefined();
0159     case GC: {
0160         JSLock lock;
0161         Interpreter::collect();
0162         return jsUndefined();
0163     }
0164     case Version:
0165         // We need this function for compatibility with the Mozilla JS tests but for now
0166         // we don't actually do any version-specific handling
0167         return jsUndefined();
0168     case Run: {
0169         StopWatch stopWatch;
0170         char *fileName = strdup(JSValue::toString(args[0], exec).UTF8String().c_str());
0171         char *script = createStringWithContentsOfFile(fileName);
0172         if (!script) {
0173             return throwError(exec, GeneralError, "Could not open file.");
0174         }
0175 
0176         stopWatch.start();
0177         exec->dynamicInterpreter()->evaluate(fileName, 0, script);
0178         stopWatch.stop();
0179 
0180         free(script);
0181         free(fileName);
0182 
0183         return jsNumber(stopWatch.getElapsedMS());
0184     }
0185     case Quit:
0186         exit(0);
0187     default:
0188         abort();
0189     }
0190     return nullptr;
0191 }
0192 
0193 #if PLATFORM(WIN_OS) && defined(HAVE_CRTDBG_H) && !defined(__MINGW32__)
0194 // Use SEH for Release builds only to get rid of the crash report dialog
0195 // (luckily the same tests fail in Release and Debug builds so far). Need to
0196 // be in a separate main function because the kjsmain function requires object
0197 // unwinding.
0198 
0199 #if defined(_DEBUG)
0200 #define TRY
0201 #define EXCEPT(x)
0202 #else
0203 #define TRY       __try {
0204 #define EXCEPT(x) } __except (EXCEPTION_EXECUTE_HANDLER) { x; }
0205 #endif
0206 
0207 #else
0208 
0209 #define TRY
0210 #define EXCEPT(x)
0211 
0212 #endif
0213 
0214 int kjsmain(int argc, char **argv);
0215 
0216 int main(int argc, char **argv)
0217 {
0218 #if defined(_DEBUG) && PLATFORM(WIN_OS)
0219     _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
0220     _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
0221     _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
0222     _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
0223     _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
0224     _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
0225 #endif
0226 
0227     int res = 0;
0228     TRY
0229     res = kjsmain(argc, argv);
0230     EXCEPT(res = 3)
0231     return res;
0232 }
0233 
0234 bool doIt(int argc, char **argv)
0235 {
0236     bool success = true;
0237 #if 0
0238     bool prettyPrint = false;
0239 #endif
0240     GlobalImp *global = new GlobalImp();
0241 
0242     // create interpreter
0243     RefPtr<Interpreter> interp = new Interpreter(global);
0244     // add debug() function
0245     global->put(interp->globalExec(), "debug", new TestFunctionImp(TestFunctionImp::Debug, 1));
0246     // add "print" for compatibility with the mozilla js shell
0247     global->put(interp->globalExec(), "print", new TestFunctionImp(TestFunctionImp::Print, 1));
0248     // add "quit" for compatibility with the mozilla js shell
0249     global->put(interp->globalExec(), "quit", new TestFunctionImp(TestFunctionImp::Quit, 0));
0250     // add "gc" for compatibility with the mozilla js shell
0251     global->put(interp->globalExec(), "gc", new TestFunctionImp(TestFunctionImp::GC, 0));
0252     // add "version" for compatibility with the mozilla js shell
0253     global->put(interp->globalExec(), "version", new TestFunctionImp(TestFunctionImp::Version, 1));
0254     global->put(interp->globalExec(), "run", new TestFunctionImp(TestFunctionImp::Run, 1));
0255 
0256     // abort evaluation of single script file after 5 seconds
0257     interp->setTimeoutTime(5000);
0258 
0259     Interpreter::setShouldPrintExceptions(true);
0260 
0261     for (int i = 1; i < argc; i++) {
0262         const char *fileName = argv[i];
0263         if (strcmp(fileName, "-f") == 0) { // mozilla test driver script uses "-f" prefix for files
0264             continue;
0265         }
0266 #if 0
0267         if (strcmp(fileName, "-p") == 0) {
0268             prettyPrint = true;
0269             continue;
0270         }
0271 #endif
0272 
0273         char *script = createStringWithContentsOfFile(fileName);
0274         if (!script) {
0275             success = false;
0276             break; // fail early so we can catch missing files
0277         }
0278 
0279 #if 0
0280         if (prettyPrint) {
0281             int errLine = 0;
0282             UString errMsg;
0283             UString s = Parser::prettyPrint(script, &errLine, &errMsg);
0284             if (s.isNull()) {
0285                 fprintf(stderr, "%s:%d: %s.\n", fileName, errLine, errMsg.UTF8String().c_str());
0286                 success = false;
0287                 break;
0288             }
0289 
0290             printf("%s\n", s.UTF8String().c_str());
0291         } else
0292 #endif
0293         {
0294             interp->startTimeoutCheck();
0295             Completion completion = interp->evaluate(fileName, 0, script);
0296             interp->stopTimeoutCheck();
0297             success = success && completion.complType() != Throw;
0298         }
0299         free(script);
0300     }
0301 
0302     return success;
0303 }
0304 
0305 int kjsmain(int argc, char **argv)
0306 {
0307     if (argc < 2) {
0308         fprintf(stderr, "Usage: testkjs file1 [file2...]\n");
0309         return -1;
0310     }
0311 
0312     testIsInteger();
0313     testUString();
0314 
0315     JSLock lock;
0316 
0317     bool success = doIt(argc, argv);
0318 
0319 #ifndef NDEBUG
0320     Collector::collect();
0321 #endif
0322 
0323     if (success) {
0324         fprintf(stderr, "OK.\n");
0325     }
0326 
0327     return success ? 0 : 3;
0328 }
0329 
0330 static void testIsInteger()
0331 {
0332     // Unit tests for WTF::IsInteger. Don't have a better place for them now.
0333     // FIXME: move these once we create a unit test directory for WTF.
0334 
0335     assert(IsInteger<bool>::value);
0336     assert(IsInteger<char>::value);
0337     assert(IsInteger<signed char>::value);
0338     assert(IsInteger<unsigned char>::value);
0339     assert(IsInteger<short>::value);
0340     assert(IsInteger<unsigned short>::value);
0341     assert(IsInteger<int>::value);
0342     assert(IsInteger<unsigned int>::value);
0343     assert(IsInteger<long>::value);
0344     assert(IsInteger<unsigned long>::value);
0345     assert(IsInteger<long long>::value);
0346     assert(IsInteger<unsigned long long>::value);
0347 
0348     assert(!IsInteger<char *>::value);
0349     assert(!IsInteger<const char * >::value);
0350     assert(!IsInteger<volatile char * >::value);
0351     assert(!IsInteger<double>::value);
0352     assert(!IsInteger<float>::value);
0353     assert(!IsInteger<GlobalImp>::value);
0354 }
0355 
0356 // not a good place either
0357 void testUString()
0358 {
0359     // bug #141720
0360     UString s1 = "abc";
0361     UString s2 = s1;
0362     s1.append("xxx");
0363     s2.append((unsigned short)0x64);
0364     assert(s2.size() == 4);
0365 }
0366 
0367 static char *createStringWithContentsOfFile(const char *fileName)
0368 {
0369     char *buffer;
0370 
0371     size_t buffer_size = 0;
0372     size_t buffer_capacity = 1024;
0373     buffer = (char *)malloc(buffer_capacity);
0374 
0375     FILE *f = fopen(fileName, "r");
0376     if (!f) {
0377         fprintf(stderr, "Could not open file: %s\n", fileName);
0378         return nullptr;
0379     }
0380 
0381     while (!feof(f) && !ferror(f)) {
0382         buffer_size += fread(buffer + buffer_size, 1, buffer_capacity - buffer_size, f);
0383         if (buffer_size == buffer_capacity) { // guarantees space for trailing '\0'
0384             buffer_capacity *= 2;
0385             buffer = (char *)realloc(buffer, buffer_capacity);
0386             assert(buffer);
0387         }
0388 
0389         assert(buffer_size < buffer_capacity);
0390     }
0391     fclose(f);
0392     buffer[buffer_size] = '\0';
0393 
0394     return buffer;
0395 }