]> git.saurik.com Git - apple/javascriptcore.git/blob - jsc.cpp
85ed56b3f3bd086dce1319a06ff7b9b79f322b6d
[apple/javascriptcore.git] / jsc.cpp
1 /*
2 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
4 * Copyright (C) 2006 Bjoern Graf (bjoern.graf@gmail.com)
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 "CurrentTime.h"
28 #include "ExceptionHelpers.h"
29 #include "InitializeThreading.h"
30 #include "JSArray.h"
31 #include "JSFunction.h"
32 #include "JSLock.h"
33 #include "JSString.h"
34 #include "SamplingTool.h"
35 #include <math.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39
40 #if !OS(WINDOWS)
41 #include <unistd.h>
42 #endif
43
44 #if HAVE(READLINE)
45 #include <readline/history.h>
46 #include <readline/readline.h>
47 #endif
48
49 #if HAVE(SYS_TIME_H)
50 #include <sys/time.h>
51 #endif
52
53 #if HAVE(SIGNAL_H)
54 #include <signal.h>
55 #endif
56
57 #if COMPILER(MSVC) && !OS(WINCE)
58 #include <crtdbg.h>
59 #include <mmsystem.h>
60 #include <windows.h>
61 #endif
62
63 #if PLATFORM(QT)
64 #include <QCoreApplication>
65 #include <QDateTime>
66 #endif
67
68 using namespace JSC;
69 using namespace WTF;
70
71 static void cleanupGlobalData(JSGlobalData*);
72 static bool fillBufferWithContentsOfFile(const UString& fileName, Vector<char>& buffer);
73
74 static EncodedJSValue JSC_HOST_CALL functionPrint(ExecState*);
75 static EncodedJSValue JSC_HOST_CALL functionDebug(ExecState*);
76 static EncodedJSValue JSC_HOST_CALL functionGC(ExecState*);
77 #ifndef NDEBUG
78 static EncodedJSValue JSC_HOST_CALL functionReleaseExecutableMemory(ExecState*);
79 #endif
80 static EncodedJSValue JSC_HOST_CALL functionVersion(ExecState*);
81 static EncodedJSValue JSC_HOST_CALL functionRun(ExecState*);
82 static EncodedJSValue JSC_HOST_CALL functionLoad(ExecState*);
83 static EncodedJSValue JSC_HOST_CALL functionCheckSyntax(ExecState*);
84 static EncodedJSValue JSC_HOST_CALL functionReadline(ExecState*);
85 static NO_RETURN_WITH_VALUE EncodedJSValue JSC_HOST_CALL functionQuit(ExecState*);
86
87 #if ENABLE(SAMPLING_FLAGS)
88 static EncodedJSValue JSC_HOST_CALL functionSetSamplingFlags(ExecState*);
89 static EncodedJSValue JSC_HOST_CALL functionClearSamplingFlags(ExecState*);
90 #endif
91
92 struct Script {
93 bool isFile;
94 char* argument;
95
96 Script(bool isFile, char *argument)
97 : isFile(isFile)
98 , argument(argument)
99 {
100 }
101 };
102
103 struct Options {
104 Options()
105 : interactive(false)
106 , dump(false)
107 {
108 }
109
110 bool interactive;
111 bool dump;
112 Vector<Script> scripts;
113 Vector<UString> arguments;
114 };
115
116 static const char interactivePrompt[] = "> ";
117 static const UString interpreterName("Interpreter");
118
119 class StopWatch {
120 public:
121 void start();
122 void stop();
123 long getElapsedMS(); // call stop() first
124
125 private:
126 double m_startTime;
127 double m_stopTime;
128 };
129
130 void StopWatch::start()
131 {
132 m_startTime = currentTime();
133 }
134
135 void StopWatch::stop()
136 {
137 m_stopTime = currentTime();
138 }
139
140 long StopWatch::getElapsedMS()
141 {
142 return static_cast<long>((m_stopTime - m_startTime) * 1000);
143 }
144
145 class GlobalObject : public JSGlobalObject {
146 public:
147 GlobalObject(JSGlobalData&, Structure*, const Vector<UString>& arguments);
148 virtual UString className() const { return "global"; }
149 };
150 COMPILE_ASSERT(!IsInteger<GlobalObject>::value, WTF_IsInteger_GlobalObject_false);
151 ASSERT_CLASS_FITS_IN_CELL(GlobalObject);
152
153 GlobalObject::GlobalObject(JSGlobalData& globalData, Structure* structure, const Vector<UString>& arguments)
154 : JSGlobalObject(globalData, structure)
155 {
156 putDirectFunction(globalExec(), new (globalExec()) JSFunction(globalExec(), this, functionStructure(), 1, Identifier(globalExec(), "debug"), functionDebug));
157 putDirectFunction(globalExec(), new (globalExec()) JSFunction(globalExec(), this, functionStructure(), 1, Identifier(globalExec(), "print"), functionPrint));
158 putDirectFunction(globalExec(), new (globalExec()) JSFunction(globalExec(), this, functionStructure(), 0, Identifier(globalExec(), "quit"), functionQuit));
159 putDirectFunction(globalExec(), new (globalExec()) JSFunction(globalExec(), this, functionStructure(), 0, Identifier(globalExec(), "gc"), functionGC));
160 #ifndef NDEBUG
161 putDirectFunction(globalExec(), new (globalExec()) JSFunction(globalExec(), this, functionStructure(), 0, Identifier(globalExec(), "releaseExecutableMemory"), functionReleaseExecutableMemory));
162 #endif
163 putDirectFunction(globalExec(), new (globalExec()) JSFunction(globalExec(), this, functionStructure(), 1, Identifier(globalExec(), "version"), functionVersion));
164 putDirectFunction(globalExec(), new (globalExec()) JSFunction(globalExec(), this, functionStructure(), 1, Identifier(globalExec(), "run"), functionRun));
165 putDirectFunction(globalExec(), new (globalExec()) JSFunction(globalExec(), this, functionStructure(), 1, Identifier(globalExec(), "load"), functionLoad));
166 putDirectFunction(globalExec(), new (globalExec()) JSFunction(globalExec(), this, functionStructure(), 1, Identifier(globalExec(), "checkSyntax"), functionCheckSyntax));
167 putDirectFunction(globalExec(), new (globalExec()) JSFunction(globalExec(), this, functionStructure(), 0, Identifier(globalExec(), "readline"), functionReadline));
168
169 #if ENABLE(SAMPLING_FLAGS)
170 putDirectFunction(globalExec(), new (globalExec()) JSFunction(globalExec(), this, functionStructure(), 1, Identifier(globalExec(), "setSamplingFlags"), functionSetSamplingFlags));
171 putDirectFunction(globalExec(), new (globalExec()) JSFunction(globalExec(), this, functionStructure(), 1, Identifier(globalExec(), "clearSamplingFlags"), functionClearSamplingFlags));
172 #endif
173
174 JSObject* array = constructEmptyArray(globalExec());
175 for (size_t i = 0; i < arguments.size(); ++i)
176 array->put(globalExec(), i, jsString(globalExec(), arguments[i]));
177 putDirect(globalExec()->globalData(), Identifier(globalExec(), "arguments"), array);
178 }
179
180 EncodedJSValue JSC_HOST_CALL functionPrint(ExecState* exec)
181 {
182 for (unsigned i = 0; i < exec->argumentCount(); ++i) {
183 if (i)
184 putchar(' ');
185
186 printf("%s", exec->argument(i).toString(exec).utf8().data());
187 }
188
189 putchar('\n');
190 fflush(stdout);
191 return JSValue::encode(jsUndefined());
192 }
193
194 EncodedJSValue JSC_HOST_CALL functionDebug(ExecState* exec)
195 {
196 fprintf(stderr, "--> %s\n", exec->argument(0).toString(exec).utf8().data());
197 return JSValue::encode(jsUndefined());
198 }
199
200 EncodedJSValue JSC_HOST_CALL functionGC(ExecState* exec)
201 {
202 JSLock lock(SilenceAssertionsOnly);
203 exec->heap()->collectAllGarbage();
204 return JSValue::encode(jsUndefined());
205 }
206
207 #ifndef NDEBUG
208 EncodedJSValue JSC_HOST_CALL functionReleaseExecutableMemory(ExecState* exec)
209 {
210 JSLock lock(SilenceAssertionsOnly);
211 exec->globalData().releaseExecutableMemory();
212 return JSValue::encode(jsUndefined());
213 }
214 #endif
215
216 EncodedJSValue JSC_HOST_CALL functionVersion(ExecState*)
217 {
218 // We need this function for compatibility with the Mozilla JS tests but for now
219 // we don't actually do any version-specific handling
220 return JSValue::encode(jsUndefined());
221 }
222
223 EncodedJSValue JSC_HOST_CALL functionRun(ExecState* exec)
224 {
225 UString fileName = exec->argument(0).toString(exec);
226 Vector<char> script;
227 if (!fillBufferWithContentsOfFile(fileName, script))
228 return JSValue::encode(throwError(exec, createError(exec, "Could not open file.")));
229
230 GlobalObject* globalObject = new (&exec->globalData()) GlobalObject(exec->globalData(), GlobalObject::createStructure(exec->globalData(), jsNull()), Vector<UString>());
231
232 StopWatch stopWatch;
233 stopWatch.start();
234 evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(script.data(), fileName));
235 stopWatch.stop();
236
237 return JSValue::encode(jsNumber(stopWatch.getElapsedMS()));
238 }
239
240 EncodedJSValue JSC_HOST_CALL functionLoad(ExecState* exec)
241 {
242 UString fileName = exec->argument(0).toString(exec);
243 Vector<char> script;
244 if (!fillBufferWithContentsOfFile(fileName, script))
245 return JSValue::encode(throwError(exec, createError(exec, "Could not open file.")));
246
247 JSGlobalObject* globalObject = exec->lexicalGlobalObject();
248 Completion result = evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(script.data(), fileName));
249 if (result.complType() == Throw)
250 throwError(exec, result.value());
251 return JSValue::encode(result.value());
252 }
253
254 EncodedJSValue JSC_HOST_CALL functionCheckSyntax(ExecState* exec)
255 {
256 UString fileName = exec->argument(0).toString(exec);
257 Vector<char> script;
258 if (!fillBufferWithContentsOfFile(fileName, script))
259 return JSValue::encode(throwError(exec, createError(exec, "Could not open file.")));
260
261 JSGlobalObject* globalObject = exec->lexicalGlobalObject();
262
263 StopWatch stopWatch;
264 stopWatch.start();
265 Completion result = checkSyntax(globalObject->globalExec(), makeSource(script.data(), fileName));
266 stopWatch.stop();
267
268 if (result.complType() == Throw)
269 throwError(exec, result.value());
270 return JSValue::encode(jsNumber(stopWatch.getElapsedMS()));
271 }
272
273 #if ENABLE(SAMPLING_FLAGS)
274 EncodedJSValue JSC_HOST_CALL functionSetSamplingFlags(ExecState* exec)
275 {
276 for (unsigned i = 0; i < exec->argumentCount(); ++i) {
277 unsigned flag = static_cast<unsigned>(exec->argument(i).toNumber(exec));
278 if ((flag >= 1) && (flag <= 32))
279 SamplingFlags::setFlag(flag);
280 }
281 return JSValue::encode(jsNull());
282 }
283
284 EncodedJSValue JSC_HOST_CALL functionClearSamplingFlags(ExecState* exec)
285 {
286 for (unsigned i = 0; i < exec->argumentCount(); ++i) {
287 unsigned flag = static_cast<unsigned>(exec->argument(i).toNumber(exec));
288 if ((flag >= 1) && (flag <= 32))
289 SamplingFlags::clearFlag(flag);
290 }
291 return JSValue::encode(jsNull());
292 }
293 #endif
294
295 EncodedJSValue JSC_HOST_CALL functionReadline(ExecState* exec)
296 {
297 Vector<char, 256> line;
298 int c;
299 while ((c = getchar()) != EOF) {
300 // FIXME: Should we also break on \r?
301 if (c == '\n')
302 break;
303 line.append(c);
304 }
305 line.append('\0');
306 return JSValue::encode(jsString(exec, line.data()));
307 }
308
309 EncodedJSValue JSC_HOST_CALL functionQuit(ExecState* exec)
310 {
311 // Technically, destroying the heap in the middle of JS execution is a no-no,
312 // but we want to maintain compatibility with the Mozilla test suite, so
313 // we pretend that execution has terminated to avoid ASSERTs, then tear down the heap.
314 exec->globalData().dynamicGlobalObject = 0;
315
316 cleanupGlobalData(&exec->globalData());
317 exit(EXIT_SUCCESS);
318
319 #if COMPILER(MSVC) && OS(WINCE)
320 // Without this, Visual Studio will complain that this method does not return a value.
321 return JSValue::encode(jsUndefined());
322 #endif
323 }
324
325 // Use SEH for Release builds only to get rid of the crash report dialog
326 // (luckily the same tests fail in Release and Debug builds so far). Need to
327 // be in a separate main function because the jscmain function requires object
328 // unwinding.
329
330 #if COMPILER(MSVC) && !defined(_DEBUG) && !OS(WINCE)
331 #define TRY __try {
332 #define EXCEPT(x) } __except (EXCEPTION_EXECUTE_HANDLER) { x; }
333 #else
334 #define TRY
335 #define EXCEPT(x)
336 #endif
337
338 int jscmain(int argc, char** argv, JSGlobalData*);
339
340 int main(int argc, char** argv)
341 {
342 #if OS(WINDOWS)
343 #if !OS(WINCE)
344 // Cygwin calls ::SetErrorMode(SEM_FAILCRITICALERRORS), which we will inherit. This is bad for
345 // testing/debugging, as it causes the post-mortem debugger not to be invoked. We reset the
346 // error mode here to work around Cygwin's behavior. See <http://webkit.org/b/55222>.
347 ::SetErrorMode(0);
348 #endif
349
350 #if defined(_DEBUG)
351 _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
352 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
353 _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
354 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
355 _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
356 _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
357 #endif
358
359 timeBeginPeriod(1);
360 #endif
361
362 #if PLATFORM(QT)
363 QCoreApplication app(argc, argv);
364 #endif
365
366 // Initialize JSC before getting JSGlobalData.
367 JSC::initializeThreading();
368
369 // We can't use destructors in the following code because it uses Windows
370 // Structured Exception Handling
371 int res = 0;
372 JSGlobalData* globalData = JSGlobalData::create(ThreadStackTypeLarge).leakRef();
373 TRY
374 res = jscmain(argc, argv, globalData);
375 EXCEPT(res = 3)
376
377 cleanupGlobalData(globalData);
378 return res;
379 }
380
381 static void cleanupGlobalData(JSGlobalData* globalData)
382 {
383 JSLock lock(SilenceAssertionsOnly);
384 globalData->clearBuiltinStructures();
385 globalData->heap.destroy();
386 globalData->deref();
387 }
388
389 static bool runWithScripts(GlobalObject* globalObject, const Vector<Script>& scripts, bool dump)
390 {
391 UString script;
392 UString fileName;
393 Vector<char> scriptBuffer;
394
395 if (dump)
396 BytecodeGenerator::setDumpsGeneratedCode(true);
397
398 JSGlobalData& globalData = globalObject->globalData();
399
400 #if ENABLE(SAMPLING_FLAGS)
401 SamplingFlags::start();
402 #endif
403
404 bool success = true;
405 for (size_t i = 0; i < scripts.size(); i++) {
406 if (scripts[i].isFile) {
407 fileName = scripts[i].argument;
408 if (!fillBufferWithContentsOfFile(fileName, scriptBuffer))
409 return false; // fail early so we can catch missing files
410 script = scriptBuffer.data();
411 } else {
412 script = scripts[i].argument;
413 fileName = "[Command Line]";
414 }
415
416 globalData.startSampling();
417
418 Completion completion = evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(script, fileName));
419 success = success && completion.complType() != Throw;
420 if (dump) {
421 if (completion.complType() == Throw)
422 printf("Exception: %s\n", completion.value().toString(globalObject->globalExec()).utf8().data());
423 else
424 printf("End: %s\n", completion.value().toString(globalObject->globalExec()).utf8().data());
425 }
426
427 globalData.stopSampling();
428 globalObject->globalExec()->clearException();
429 }
430
431 #if ENABLE(SAMPLING_FLAGS)
432 SamplingFlags::stop();
433 #endif
434 globalData.dumpSampleData(globalObject->globalExec());
435 #if ENABLE(SAMPLING_COUNTERS)
436 AbstractSamplingCounter::dump();
437 #endif
438 #if ENABLE(REGEXP_TRACING)
439 globalData.dumpRegExpTrace();
440 #endif
441 return success;
442 }
443
444 #define RUNNING_FROM_XCODE 0
445
446 static void runInteractive(GlobalObject* globalObject)
447 {
448 while (true) {
449 #if HAVE(READLINE) && !RUNNING_FROM_XCODE
450 char* line = readline(interactivePrompt);
451 if (!line)
452 break;
453 if (line[0])
454 add_history(line);
455 Completion completion = evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(line, interpreterName));
456 free(line);
457 #else
458 printf("%s", interactivePrompt);
459 Vector<char, 256> line;
460 int c;
461 while ((c = getchar()) != EOF) {
462 // FIXME: Should we also break on \r?
463 if (c == '\n')
464 break;
465 line.append(c);
466 }
467 if (line.isEmpty())
468 break;
469 line.append('\0');
470 Completion completion = evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(line.data(), interpreterName));
471 #endif
472 if (completion.complType() == Throw)
473 printf("Exception: %s\n", completion.value().toString(globalObject->globalExec()).utf8().data());
474 else
475 printf("%s\n", completion.value().toString(globalObject->globalExec()).utf8().data());
476
477 globalObject->globalExec()->clearException();
478 }
479 printf("\n");
480 }
481
482 static NO_RETURN void printUsageStatement(JSGlobalData* globalData, bool help = false)
483 {
484 fprintf(stderr, "Usage: jsc [options] [files] [-- arguments]\n");
485 fprintf(stderr, " -d Dumps bytecode (debug builds only)\n");
486 fprintf(stderr, " -e Evaluate argument as script code\n");
487 fprintf(stderr, " -f Specifies a source file (deprecated)\n");
488 fprintf(stderr, " -h|--help Prints this help message\n");
489 fprintf(stderr, " -i Enables interactive mode (default if no files are specified)\n");
490 #if HAVE(SIGNAL_H)
491 fprintf(stderr, " -s Installs signal handlers that exit on a crash (Unix platforms only)\n");
492 #endif
493
494 cleanupGlobalData(globalData);
495 exit(help ? EXIT_SUCCESS : EXIT_FAILURE);
496 }
497
498 static void parseArguments(int argc, char** argv, Options& options, JSGlobalData* globalData)
499 {
500 int i = 1;
501 for (; i < argc; ++i) {
502 const char* arg = argv[i];
503 if (!strcmp(arg, "-f")) {
504 if (++i == argc)
505 printUsageStatement(globalData);
506 options.scripts.append(Script(true, argv[i]));
507 continue;
508 }
509 if (!strcmp(arg, "-e")) {
510 if (++i == argc)
511 printUsageStatement(globalData);
512 options.scripts.append(Script(false, argv[i]));
513 continue;
514 }
515 if (!strcmp(arg, "-i")) {
516 options.interactive = true;
517 continue;
518 }
519 if (!strcmp(arg, "-d")) {
520 options.dump = true;
521 continue;
522 }
523 if (!strcmp(arg, "-s")) {
524 #if HAVE(SIGNAL_H)
525 signal(SIGILL, _exit);
526 signal(SIGFPE, _exit);
527 signal(SIGBUS, _exit);
528 signal(SIGSEGV, _exit);
529 #endif
530 continue;
531 }
532 if (!strcmp(arg, "--")) {
533 ++i;
534 break;
535 }
536 if (!strcmp(arg, "-h") || !strcmp(arg, "--help"))
537 printUsageStatement(globalData, true);
538 options.scripts.append(Script(true, argv[i]));
539 }
540
541 if (options.scripts.isEmpty())
542 options.interactive = true;
543
544 for (; i < argc; ++i)
545 options.arguments.append(argv[i]);
546 }
547
548 int jscmain(int argc, char** argv, JSGlobalData* globalData)
549 {
550 JSLock lock(SilenceAssertionsOnly);
551
552 Options options;
553 parseArguments(argc, argv, options, globalData);
554
555 GlobalObject* globalObject = new (globalData) GlobalObject(*globalData, GlobalObject::createStructure(*globalData, jsNull()), options.arguments);
556 bool success = runWithScripts(globalObject, options.scripts, options.dump);
557 if (options.interactive && success)
558 runInteractive(globalObject);
559
560 return success ? 0 : 3;
561 }
562
563 static bool fillBufferWithContentsOfFile(const UString& fileName, Vector<char>& buffer)
564 {
565 FILE* f = fopen(fileName.utf8().data(), "r");
566 if (!f) {
567 fprintf(stderr, "Could not open file: %s\n", fileName.utf8().data());
568 return false;
569 }
570
571 size_t bufferSize = 0;
572 size_t bufferCapacity = 1024;
573
574 buffer.resize(bufferCapacity);
575
576 while (!feof(f) && !ferror(f)) {
577 bufferSize += fread(buffer.data() + bufferSize, 1, bufferCapacity - bufferSize, f);
578 if (bufferSize == bufferCapacity) { // guarantees space for trailing '\0'
579 bufferCapacity *= 2;
580 buffer.resize(bufferCapacity);
581 }
582 }
583 fclose(f);
584 buffer[bufferSize] = '\0';
585
586 if (buffer[0] == '#' && buffer[1] == '!')
587 buffer[0] = buffer[1] = '/';
588
589 return true;
590 }