source: webkit/trunk/JavaScriptCore/jsc.cpp@ 43439

Last change on this file since 43439 was 43372, checked in by [email protected], 16 years ago

Improve native call performance

Reviewed by Gavin Barraclough.

Fix the windows build by adding calling convention declarations everywhere,
chose fastcall as that seemed most sensible given we were having to declare
the convention explicitly. In addition switched to fastcall on mac in the
deluded belief that documented fastcall behavior on windows would match
actual its actual behavior.

  • Property svn:eol-style set to native
File size: 18.0 KB
Line 
1/*
2 * Copyright (C) 1999-2000 Harri Porten ([email protected])
3 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
4 * Copyright (C) 2006 Bjoern Graf ([email protected])
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 *
21 */
22
23#include "config.h"
24
25#include "BytecodeGenerator.h"
26#include "Completion.h"
27#include "InitializeThreading.h"
28#include "JSArray.h"
29#include "JSFunction.h"
30#include "JSLock.h"
31#include "PrototypeFunction.h"
32#include "SamplingTool.h"
33#include <math.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37
38#if !PLATFORM(WIN_OS)
39#include <unistd.h>
40#endif
41
42#if HAVE(READLINE)
43#include <readline/history.h>
44#include <readline/readline.h>
45#endif
46
47#if HAVE(SYS_TIME_H)
48#include <sys/time.h>
49#endif
50
51#if PLATFORM(UNIX)
52#include <signal.h>
53#endif
54
55#if COMPILER(MSVC) && !PLATFORM(WIN_CE)
56#include <crtdbg.h>
57#include <windows.h>
58#include <mmsystem.h>
59#endif
60
61#if PLATFORM(QT)
62#include <QCoreApplication>
63#include <QDateTime>
64#endif
65
66using namespace JSC;
67using namespace WTF;
68
69static void cleanupGlobalData(JSGlobalData*);
70static bool fillBufferWithContentsOfFile(const UString& fileName, Vector<char>& buffer);
71
72static JSValue JSC_HOST_CALL functionPrint(ExecState*, JSObject*, JSValue, const ArgList&);
73static JSValue JSC_HOST_CALL functionDebug(ExecState*, JSObject*, JSValue, const ArgList&);
74static JSValue JSC_HOST_CALL functionGC(ExecState*, JSObject*, JSValue, const ArgList&);
75static JSValue JSC_HOST_CALL functionVersion(ExecState*, JSObject*, JSValue, const ArgList&);
76static JSValue JSC_HOST_CALL functionRun(ExecState*, JSObject*, JSValue, const ArgList&);
77static JSValue JSC_HOST_CALL functionLoad(ExecState*, JSObject*, JSValue, const ArgList&);
78static JSValue JSC_HOST_CALL functionReadline(ExecState*, JSObject*, JSValue, const ArgList&);
79static NO_RETURN JSValue JSC_HOST_CALL functionQuit(ExecState*, JSObject*, JSValue, const ArgList&);
80
81#if ENABLE(SAMPLING_FLAGS)
82static JSValue JSC_HOST_CALL functionSetSamplingFlags(ExecState*, JSObject*, JSValue, const ArgList&);
83static JSValue JSC_HOST_CALL functionClearSamplingFlags(ExecState*, JSObject*, JSValue, const ArgList&);
84#endif
85
86struct Script {
87 bool isFile;
88 char *argument;
89
90 Script(bool isFile, char *argument)
91 : isFile(isFile)
92 , argument(argument)
93 {
94 }
95};
96
97struct Options {
98 Options()
99 : interactive(false)
100 , dump(false)
101 {
102 }
103
104 bool interactive;
105 bool dump;
106 Vector<Script> scripts;
107 Vector<UString> arguments;
108};
109
110static const char interactivePrompt[] = "> ";
111static const UString interpreterName("Interpreter");
112
113class StopWatch {
114public:
115 void start();
116 void stop();
117 long getElapsedMS(); // call stop() first
118
119private:
120#if PLATFORM(QT)
121 uint m_startTime;
122 uint m_stopTime;
123#elif PLATFORM(WIN_OS)
124 DWORD m_startTime;
125 DWORD m_stopTime;
126#else
127 // Windows does not have timeval, disabling this class for now (bug 7399)
128 timeval m_startTime;
129 timeval m_stopTime;
130#endif
131};
132
133void StopWatch::start()
134{
135#if PLATFORM(QT)
136 QDateTime t = QDateTime::currentDateTime();
137 m_startTime = t.toTime_t() * 1000 + t.time().msec();
138#elif PLATFORM(WIN_OS)
139 m_startTime = timeGetTime();
140#else
141 gettimeofday(&m_startTime, 0);
142#endif
143}
144
145void StopWatch::stop()
146{
147#if PLATFORM(QT)
148 QDateTime t = QDateTime::currentDateTime();
149 m_stopTime = t.toTime_t() * 1000 + t.time().msec();
150#elif PLATFORM(WIN_OS)
151 m_stopTime = timeGetTime();
152#else
153 gettimeofday(&m_stopTime, 0);
154#endif
155}
156
157long StopWatch::getElapsedMS()
158{
159#if PLATFORM(WIN_OS) || PLATFORM(QT)
160 return m_stopTime - m_startTime;
161#else
162 timeval elapsedTime;
163 timersub(&m_stopTime, &m_startTime, &elapsedTime);
164
165 return elapsedTime.tv_sec * 1000 + lroundf(elapsedTime.tv_usec / 1000.0f);
166#endif
167}
168
169class GlobalObject : public JSGlobalObject {
170public:
171 GlobalObject(const Vector<UString>& arguments);
172 virtual UString className() const { return "global"; }
173};
174COMPILE_ASSERT(!IsInteger<GlobalObject>::value, WTF_IsInteger_GlobalObject_false);
175ASSERT_CLASS_FITS_IN_CELL(GlobalObject);
176
177GlobalObject::GlobalObject(const Vector<UString>& arguments)
178 : JSGlobalObject()
179{
180 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "debug"), functionDebug));
181 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "print"), functionPrint));
182 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 0, Identifier(globalExec(), "quit"), functionQuit));
183 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 0, Identifier(globalExec(), "gc"), functionGC));
184 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "version"), functionVersion));
185 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "run"), functionRun));
186 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "load"), functionLoad));
187 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 0, Identifier(globalExec(), "readline"), functionReadline));
188
189#if ENABLE(SAMPLING_FLAGS)
190 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "setSamplingFlags"), functionSetSamplingFlags));
191 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "clearSamplingFlags"), functionClearSamplingFlags));
192#endif
193
194 JSObject* array = constructEmptyArray(globalExec());
195 for (size_t i = 0; i < arguments.size(); ++i)
196 array->put(globalExec(), i, jsString(globalExec(), arguments[i]));
197 putDirect(Identifier(globalExec(), "arguments"), array);
198}
199
200JSValue JSC_HOST_CALL functionPrint(ExecState* exec, JSObject*, JSValue, const ArgList& args)
201{
202 for (unsigned i = 0; i < args.size(); ++i) {
203 if (i != 0)
204 putchar(' ');
205
206 printf("%s", args.at(i).toString(exec).UTF8String().c_str());
207 }
208
209 putchar('\n');
210 fflush(stdout);
211 return jsUndefined();
212}
213
214JSValue JSC_HOST_CALL functionDebug(ExecState* exec, JSObject*, JSValue, const ArgList& args)
215{
216 fprintf(stderr, "--> %s\n", args.at(0).toString(exec).UTF8String().c_str());
217 return jsUndefined();
218}
219
220JSValue JSC_HOST_CALL functionGC(ExecState* exec, JSObject*, JSValue, const ArgList&)
221{
222 JSLock lock(false);
223 exec->heap()->collect();
224 return jsUndefined();
225}
226
227JSValue JSC_HOST_CALL functionVersion(ExecState*, JSObject*, JSValue, const ArgList&)
228{
229 // We need this function for compatibility with the Mozilla JS tests but for now
230 // we don't actually do any version-specific handling
231 return jsUndefined();
232}
233
234JSValue JSC_HOST_CALL functionRun(ExecState* exec, JSObject*, JSValue, const ArgList& args)
235{
236 StopWatch stopWatch;
237 UString fileName = args.at(0).toString(exec);
238 Vector<char> script;
239 if (!fillBufferWithContentsOfFile(fileName, script))
240 return throwError(exec, GeneralError, "Could not open file.");
241
242 JSGlobalObject* globalObject = exec->lexicalGlobalObject();
243
244 stopWatch.start();
245 evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(script.data(), fileName));
246 stopWatch.stop();
247
248 return jsNumber(globalObject->globalExec(), stopWatch.getElapsedMS());
249}
250
251JSValue JSC_HOST_CALL functionLoad(ExecState* exec, JSObject* o, JSValue v, const ArgList& args)
252{
253 UNUSED_PARAM(o);
254 UNUSED_PARAM(v);
255 UString fileName = args.at(0).toString(exec);
256 Vector<char> script;
257 if (!fillBufferWithContentsOfFile(fileName, script))
258 return throwError(exec, GeneralError, "Could not open file.");
259
260 JSGlobalObject* globalObject = exec->lexicalGlobalObject();
261 Completion result = evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(script.data(), fileName));
262 if (result.complType() == Throw)
263 exec->setException(result.value());
264 return result.value();
265}
266
267#if ENABLE(SAMPLING_FLAGS)
268JSValue JSC_HOST_CALL functionSetSamplingFlags(ExecState* exec, JSObject*, JSValue, const ArgList& args)
269{
270 for (unsigned i = 0; i < args.size(); ++i) {
271 unsigned flag = static_cast<unsigned>(args.at(i).toNumber(exec));
272 if ((flag >= 1) && (flag <= 32))
273 SamplingFlags::setFlag(flag);
274 }
275 return jsNull();
276}
277
278JSValue JSC_HOST_CALL functionClearSamplingFlags(ExecState* exec, JSObject*, JSValue, const ArgList& args)
279{
280 for (unsigned i = 0; i < args.size(); ++i) {
281 unsigned flag = static_cast<unsigned>(args.at(i).toNumber(exec));
282 if ((flag >= 1) && (flag <= 32))
283 SamplingFlags::clearFlag(flag);
284 }
285 return jsNull();
286}
287#endif
288
289JSValue JSC_HOST_CALL functionReadline(ExecState* exec, JSObject*, JSValue, const ArgList&)
290{
291 Vector<char, 256> line;
292 int c;
293 while ((c = getchar()) != EOF) {
294 // FIXME: Should we also break on \r?
295 if (c == '\n')
296 break;
297 line.append(c);
298 }
299 line.append('\0');
300 return jsString(exec, line.data());
301}
302
303JSValue JSC_HOST_CALL functionQuit(ExecState* exec, JSObject*, JSValue, const ArgList&)
304{
305 cleanupGlobalData(&exec->globalData());
306 exit(EXIT_SUCCESS);
307}
308
309// Use SEH for Release builds only to get rid of the crash report dialog
310// (luckily the same tests fail in Release and Debug builds so far). Need to
311// be in a separate main function because the jscmain function requires object
312// unwinding.
313
314#if COMPILER(MSVC) && !defined(_DEBUG)
315#define TRY __try {
316#define EXCEPT(x) } __except (EXCEPTION_EXECUTE_HANDLER) { x; }
317#else
318#define TRY
319#define EXCEPT(x)
320#endif
321
322int jscmain(int argc, char** argv, JSGlobalData*);
323
324int main(int argc, char** argv)
325{
326#if defined(_DEBUG) && PLATFORM(WIN_OS)
327 _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
328 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
329 _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
330 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
331 _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
332 _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
333#endif
334
335#if COMPILER(MSVC) && !PLATFORM(WIN_CE)
336 timeBeginPeriod(1);
337#endif
338
339#if PLATFORM(QT)
340 QCoreApplication app(argc, argv);
341#endif
342
343 // Initialize JSC before getting JSGlobalData.
344 JSC::initializeThreading();
345
346 // We can't use destructors in the following code because it uses Windows
347 // Structured Exception Handling
348 int res = 0;
349 JSGlobalData* globalData = JSGlobalData::create().releaseRef();
350 TRY
351 res = jscmain(argc, argv, globalData);
352 EXCEPT(res = 3)
353
354 cleanupGlobalData(globalData);
355 return res;
356}
357
358static void cleanupGlobalData(JSGlobalData* globalData)
359{
360 JSLock lock(false);
361 globalData->heap.destroy();
362 globalData->deref();
363}
364
365static bool runWithScripts(GlobalObject* globalObject, const Vector<Script>& scripts, bool dump)
366{
367 UString script;
368 UString fileName;
369 Vector<char> scriptBuffer;
370
371 if (dump)
372 BytecodeGenerator::setDumpsGeneratedCode(true);
373
374#if ENABLE(OPCODE_SAMPLING)
375 Interpreter* interpreter = globalObject->globalData()->interpreter;
376 interpreter->setSampler(new SamplingTool(interpreter));
377 interpreter->sampler()->setup();
378#endif
379#if ENABLE(SAMPLING_FLAGS)
380 SamplingFlags::start();
381#endif
382
383 bool success = true;
384 for (size_t i = 0; i < scripts.size(); i++) {
385 if (scripts[i].isFile) {
386 fileName = scripts[i].argument;
387 if (!fillBufferWithContentsOfFile(fileName, scriptBuffer))
388 return false; // fail early so we can catch missing files
389 script = scriptBuffer.data();
390 } else {
391 script = scripts[i].argument;
392 fileName = "[Command Line]";
393 }
394
395#if ENABLE(SAMPLING_THREAD)
396 SamplingThread::start();
397#endif
398
399 Completion completion = evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(script, fileName));
400 success = success && completion.complType() != Throw;
401 if (dump) {
402 if (completion.complType() == Throw)
403 printf("Exception: %s\n", completion.value().toString(globalObject->globalExec()).ascii());
404 else
405 printf("End: %s\n", completion.value().toString(globalObject->globalExec()).ascii());
406 }
407
408#if ENABLE(SAMPLING_THREAD)
409 SamplingThread::stop();
410#endif
411
412 globalObject->globalExec()->clearException();
413 }
414
415#if ENABLE(SAMPLING_FLAGS)
416 SamplingFlags::stop();
417#endif
418#if ENABLE(OPCODE_SAMPLING)
419 interpreter->sampler()->dump(globalObject->globalExec());
420 delete interpreter->sampler();
421#endif
422 return success;
423}
424
425static
426#if !HAVE(READLINE)
427NO_RETURN
428#endif
429void runInteractive(GlobalObject* globalObject)
430{
431 while (true) {
432#if HAVE(READLINE)
433 char* line = readline(interactivePrompt);
434 if (!line)
435 break;
436 if (line[0])
437 add_history(line);
438 Completion completion = evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(line, interpreterName));
439 free(line);
440#else
441 puts(interactivePrompt);
442 Vector<char, 256> line;
443 int c;
444 while ((c = getchar()) != EOF) {
445 // FIXME: Should we also break on \r?
446 if (c == '\n')
447 break;
448 line.append(c);
449 }
450 line.append('\0');
451 Completion completion = evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(line.data(), interpreterName));
452#endif
453 if (completion.complType() == Throw)
454 printf("Exception: %s\n", completion.value().toString(globalObject->globalExec()).ascii());
455 else
456 printf("%s\n", completion.value().toString(globalObject->globalExec()).UTF8String().c_str());
457
458 globalObject->globalExec()->clearException();
459 }
460 printf("\n");
461}
462
463static NO_RETURN void printUsageStatement(bool help = false)
464{
465 fprintf(stderr, "Usage: jsc [options] [files] [-- arguments]\n");
466 fprintf(stderr, " -d Dumps bytecode (debug builds only)\n");
467 fprintf(stderr, " -e Evaluate argument as script code\n");
468 fprintf(stderr, " -f Specifies a source file (deprecated)\n");
469 fprintf(stderr, " -h|--help Prints this help message\n");
470 fprintf(stderr, " -i Enables interactive mode (default if no files are specified)\n");
471 fprintf(stderr, " -s Installs signal handlers that exit on a crash (Unix platforms only)\n");
472 exit(help ? EXIT_SUCCESS : EXIT_FAILURE);
473}
474
475static void parseArguments(int argc, char** argv, Options& options)
476{
477 int i = 1;
478 for (; i < argc; ++i) {
479 const char* arg = argv[i];
480 if (strcmp(arg, "-f") == 0) {
481 if (++i == argc)
482 printUsageStatement();
483 options.scripts.append(Script(true, argv[i]));
484 continue;
485 }
486 if (strcmp(arg, "-e") == 0) {
487 if (++i == argc)
488 printUsageStatement();
489 options.scripts.append(Script(false, argv[i]));
490 continue;
491 }
492 if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0) {
493 printUsageStatement(true);
494 }
495 if (strcmp(arg, "-i") == 0) {
496 options.interactive = true;
497 continue;
498 }
499 if (strcmp(arg, "-d") == 0) {
500 options.dump = true;
501 continue;
502 }
503 if (strcmp(arg, "-s") == 0) {
504#if PLATFORM(UNIX)
505 signal(SIGILL, _exit);
506 signal(SIGFPE, _exit);
507 signal(SIGBUS, _exit);
508 signal(SIGSEGV, _exit);
509#endif
510 continue;
511 }
512 if (strcmp(arg, "--") == 0) {
513 ++i;
514 break;
515 }
516 options.scripts.append(Script(true, argv[i]));
517 }
518
519 if (options.scripts.isEmpty())
520 options.interactive = true;
521
522 for (; i < argc; ++i)
523 options.arguments.append(argv[i]);
524}
525
526int jscmain(int argc, char** argv, JSGlobalData* globalData)
527{
528 JSLock lock(false);
529
530 Options options;
531 parseArguments(argc, argv, options);
532
533 GlobalObject* globalObject = new (globalData) GlobalObject(options.arguments);
534 bool success = runWithScripts(globalObject, options.scripts, options.dump);
535 if (options.interactive && success)
536 runInteractive(globalObject);
537
538 return success ? 0 : 3;
539}
540
541static bool fillBufferWithContentsOfFile(const UString& fileName, Vector<char>& buffer)
542{
543 FILE* f = fopen(fileName.UTF8String().c_str(), "r");
544 if (!f) {
545 fprintf(stderr, "Could not open file: %s\n", fileName.UTF8String().c_str());
546 return false;
547 }
548
549 size_t buffer_size = 0;
550 size_t buffer_capacity = 1024;
551
552 buffer.resize(buffer_capacity);
553
554 while (!feof(f) && !ferror(f)) {
555 buffer_size += fread(buffer.data() + buffer_size, 1, buffer_capacity - buffer_size, f);
556 if (buffer_size == buffer_capacity) { // guarantees space for trailing '\0'
557 buffer_capacity *= 2;
558 buffer.resize(buffer_capacity);
559 }
560 }
561 fclose(f);
562 buffer[buffer_size] = '\0';
563
564 return true;
565}
Note: See TracBrowser for help on using the repository browser.