summaryrefslogtreecommitdiffstats
path: root/JavaScriptCore/kjs/testkjs.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'JavaScriptCore/kjs/testkjs.cpp')
-rw-r--r--JavaScriptCore/kjs/testkjs.cpp374
1 files changed, 374 insertions, 0 deletions
diff --git a/JavaScriptCore/kjs/testkjs.cpp b/JavaScriptCore/kjs/testkjs.cpp
new file mode 100644
index 0000000..c229336
--- /dev/null
+++ b/JavaScriptCore/kjs/testkjs.cpp
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
+ * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Bjoern Graf (bjoern.graf@gmail.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+
+#include "JSGlobalObject.h"
+#include "JSLock.h"
+#include "Parser.h"
+#include "array_object.h"
+#include "collector.h"
+#include "function.h"
+#include "interpreter.h"
+#include "nodes.h"
+#include "object.h"
+#include "protect.h"
+#include <math.h>
+#include <stdio.h>
+#include <string.h>
+#include <wtf/Assertions.h>
+#include <wtf/HashTraits.h>
+
+#if HAVE(SYS_TIME_H)
+#include <sys/time.h>
+#endif
+
+#if PLATFORM(WIN_OS)
+#include <crtdbg.h>
+#include <windows.h>
+#endif
+
+#if PLATFORM(QT)
+#include <QDateTime>
+#endif
+
+using namespace KJS;
+using namespace WTF;
+
+static bool fillBufferWithContentsOfFile(const UString& fileName, Vector<char>& buffer);
+
+static JSValue* functionPrint(ExecState*, JSObject*, const List&);
+static JSValue* functionDebug(ExecState*, JSObject*, const List&);
+static JSValue* functionGC(ExecState*, JSObject*, const List&);
+static JSValue* functionVersion(ExecState*, JSObject*, const List&);
+static JSValue* functionRun(ExecState*, JSObject*, const List&);
+static JSValue* functionLoad(ExecState*, JSObject*, const List&);
+static JSValue* functionReadline(ExecState*, JSObject*, const List&);
+static JSValue* functionQuit(ExecState*, JSObject*, const List&);
+
+class StopWatch {
+public:
+ void start();
+ void stop();
+ long getElapsedMS(); // call stop() first
+
+private:
+#if PLATFORM(QT)
+ uint m_startTime;
+ uint m_stopTime;
+#elif PLATFORM(WIN_OS)
+ DWORD m_startTime;
+ DWORD m_stopTime;
+#else
+ // Windows does not have timeval, disabling this class for now (bug 7399)
+ timeval m_startTime;
+ timeval m_stopTime;
+#endif
+};
+
+void StopWatch::start()
+{
+#if PLATFORM(QT)
+ QDateTime t = QDateTime::currentDateTime();
+ m_startTime = t.toTime_t() * 1000 + t.time().msec();
+#elif PLATFORM(WIN_OS)
+ m_startTime = timeGetTime();
+#else
+ gettimeofday(&m_startTime, 0);
+#endif
+}
+
+void StopWatch::stop()
+{
+#if PLATFORM(QT)
+ QDateTime t = QDateTime::currentDateTime();
+ m_stopTime = t.toTime_t() * 1000 + t.time().msec();
+#elif PLATFORM(WIN_OS)
+ m_stopTime = timeGetTime();
+#else
+ gettimeofday(&m_stopTime, 0);
+#endif
+}
+
+long StopWatch::getElapsedMS()
+{
+#if PLATFORM(WIN_OS) || PLATFORM(QT)
+ return m_stopTime - m_startTime;
+#else
+ timeval elapsedTime;
+ timersub(&m_stopTime, &m_startTime, &elapsedTime);
+
+ return elapsedTime.tv_sec * 1000 + lroundf(elapsedTime.tv_usec / 1000.0f);
+#endif
+}
+
+class GlobalObject : public JSGlobalObject {
+public:
+ GlobalObject(Vector<UString>& arguments);
+ virtual UString className() const { return "global"; }
+};
+COMPILE_ASSERT(!IsInteger<GlobalObject>::value, WTF_IsInteger_GlobalObject_false);
+
+GlobalObject::GlobalObject(Vector<UString>& arguments)
+{
+ putDirectFunction(new PrototypeFunction(globalExec(), functionPrototype(), 1, "debug", functionDebug));
+ putDirectFunction(new PrototypeFunction(globalExec(), functionPrototype(), 1, "print", functionPrint));
+ putDirectFunction(new PrototypeFunction(globalExec(), functionPrototype(), 0, "quit", functionQuit));
+ putDirectFunction(new PrototypeFunction(globalExec(), functionPrototype(), 0, "gc", functionGC));
+ putDirectFunction(new PrototypeFunction(globalExec(), functionPrototype(), 1, "version", functionVersion));
+ putDirectFunction(new PrototypeFunction(globalExec(), functionPrototype(), 1, "run", functionRun));
+ putDirectFunction(new PrototypeFunction(globalExec(), functionPrototype(), 1, "load", functionLoad));
+ putDirectFunction(new PrototypeFunction(globalExec(), functionPrototype(), 0, "readline", functionReadline));
+
+ JSObject* array = arrayConstructor()->construct(globalExec(), globalExec()->emptyList());
+ for (size_t i = 0; i < arguments.size(); ++i)
+ array->put(globalExec(), i, jsString(arguments[i]));
+ putDirect("arguments", array);
+
+ Interpreter::setShouldPrintExceptions(true);
+}
+
+JSValue* functionPrint(ExecState* exec, JSObject*, const List& args)
+{
+ printf("%s\n", args[0]->toString(exec).UTF8String().c_str());
+ return jsUndefined();
+}
+
+JSValue* functionDebug(ExecState* exec, JSObject*, const List& args)
+{
+ fprintf(stderr, "--> %s\n", args[0]->toString(exec).UTF8String().c_str());
+ return jsUndefined();
+}
+
+JSValue* functionGC(ExecState*, JSObject*, const List&)
+{
+ JSLock lock;
+ Collector::collect();
+ return jsUndefined();
+}
+
+JSValue* functionVersion(ExecState*, JSObject*, const List&)
+{
+ // We need this function for compatibility with the Mozilla JS tests but for now
+ // we don't actually do any version-specific handling
+ return jsUndefined();
+}
+
+JSValue* functionRun(ExecState* exec, JSObject*, const List& args)
+{
+ StopWatch stopWatch;
+ UString fileName = args[0]->toString(exec);
+ Vector<char> script;
+ if (!fillBufferWithContentsOfFile(fileName, script))
+ return throwError(exec, GeneralError, "Could not open file.");
+
+ stopWatch.start();
+ Interpreter::evaluate(exec->dynamicGlobalObject()->globalExec(), fileName, 0, script.data());
+ stopWatch.stop();
+
+ return jsNumber(stopWatch.getElapsedMS());
+}
+
+JSValue* functionLoad(ExecState* exec, JSObject*, const List& args)
+{
+ UString fileName = args[0]->toString(exec);
+ Vector<char> script;
+ if (!fillBufferWithContentsOfFile(fileName, script))
+ return throwError(exec, GeneralError, "Could not open file.");
+
+ Interpreter::evaluate(exec->dynamicGlobalObject()->globalExec(), fileName, 0, script.data());
+
+ return jsUndefined();
+}
+
+JSValue* functionReadline(ExecState*, JSObject*, const List&)
+{
+ Vector<char, 256> line;
+ int c;
+ while ((c = getchar()) != EOF) {
+ // FIXME: Should we also break on \r?
+ if (c == '\n')
+ break;
+ line.append(c);
+ }
+ line.append('\0');
+ return jsString(line.data());
+}
+
+JSValue* functionQuit(ExecState*, JSObject*, const List&)
+{
+ exit(0);
+#if !COMPILER(MSVC)
+ // MSVC knows that exit(0) never returns, so it flags this return statement as unreachable.
+ return jsUndefined();
+#endif
+}
+
+// Use SEH for Release builds only to get rid of the crash report dialog
+// (luckily the same tests fail in Release and Debug builds so far). Need to
+// be in a separate main function because the kjsmain function requires object
+// unwinding.
+
+#if PLATFORM(WIN_OS) && !defined(_DEBUG)
+#define TRY __try {
+#define EXCEPT(x) } __except (EXCEPTION_EXECUTE_HANDLER) { x; }
+#else
+#define TRY
+#define EXCEPT(x)
+#endif
+
+int kjsmain(int argc, char** argv);
+
+int main(int argc, char** argv)
+{
+#if defined(_DEBUG) && PLATFORM(WIN_OS)
+ _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
+ _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
+ _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
+ _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
+ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
+ _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
+#endif
+
+ int res = 0;
+ TRY
+ res = kjsmain(argc, argv);
+ EXCEPT(res = 3)
+ return res;
+}
+
+static bool prettyPrintScript(const UString& fileName, const Vector<char>& script)
+{
+ int errLine = 0;
+ UString errMsg;
+ UString scriptUString(script.data());
+ RefPtr<ProgramNode> programNode = parser().parse<ProgramNode>(fileName, 0, scriptUString.data(), scriptUString.size(), 0, &errLine, &errMsg);
+ if (!programNode) {
+ fprintf(stderr, "%s:%d: %s.\n", fileName.UTF8String().c_str(), errLine, errMsg.UTF8String().c_str());
+ return false;
+ }
+
+ printf("%s\n", programNode->toString().UTF8String().c_str());
+ return true;
+}
+
+static bool runWithScripts(const Vector<UString>& fileNames, Vector<UString>& arguments, bool prettyPrint)
+{
+ GlobalObject* globalObject = new GlobalObject(arguments);
+ Vector<char> script;
+
+ bool success = true;
+
+ for (size_t i = 0; i < fileNames.size(); i++) {
+ UString fileName = fileNames[i];
+
+ if (!fillBufferWithContentsOfFile(fileName, script))
+ return false; // fail early so we can catch missing files
+
+ if (prettyPrint)
+ prettyPrintScript(fileName, script);
+ else {
+ Completion completion = Interpreter::evaluate(globalObject->globalExec(), fileName, 0, script.data());
+ success = success && completion.complType() != Throw;
+ }
+ }
+ return success;
+}
+
+static void printUsageStatement()
+{
+ fprintf(stderr, "Usage: testkjs -f file1 [-f file2...][-p][-- arguments...]\n");
+ exit(-1);
+}
+
+static void parseArguments(int argc, char** argv, Vector<UString>& fileNames, Vector<UString>& arguments, bool& prettyPrint)
+{
+ if (argc < 3)
+ printUsageStatement();
+
+ int i = 1;
+ for (; i < argc; ++i) {
+ const char* arg = argv[i];
+ if (strcmp(arg, "-f") == 0) {
+ if (++i == argc)
+ printUsageStatement();
+ fileNames.append(argv[i]);
+ continue;
+ }
+ if (strcmp(arg, "-p") == 0) {
+ prettyPrint = true;
+ continue;
+ }
+ if (strcmp(arg, "--") == 0) {
+ ++i;
+ break;
+ }
+ break;
+ }
+
+ for (; i < argc; ++i)
+ arguments.append(argv[i]);
+}
+
+int kjsmain(int argc, char** argv)
+{
+ JSLock lock;
+
+ bool prettyPrint = false;
+ Vector<UString> fileNames;
+ Vector<UString> arguments;
+ parseArguments(argc, argv, fileNames, arguments, prettyPrint);
+
+ bool success = runWithScripts(fileNames, arguments, prettyPrint);
+
+#ifndef NDEBUG
+ Collector::collect();
+#endif
+
+ return success ? 0 : 3;
+}
+
+static bool fillBufferWithContentsOfFile(const UString& fileName, Vector<char>& buffer)
+{
+ FILE* f = fopen(fileName.UTF8String().c_str(), "r");
+ if (!f) {
+ fprintf(stderr, "Could not open file: %s\n", fileName.UTF8String().c_str());
+ return false;
+ }
+
+ size_t buffer_size = 0;
+ size_t buffer_capacity = 1024;
+
+ buffer.resize(buffer_capacity);
+
+ while (!feof(f) && !ferror(f)) {
+ buffer_size += fread(buffer.data() + buffer_size, 1, buffer_capacity - buffer_size, f);
+ if (buffer_size == buffer_capacity) { // guarantees space for trailing '\0'
+ buffer_capacity *= 2;
+ buffer.resize(buffer_capacity);
+ }
+ }
+ fclose(f);
+ buffer[buffer_size] = '\0';
+
+ return true;
+}