]> git.saurik.com Git - apple/javascriptcore.git/blob - jsc.cpp
ae47d55994cb061fb696e8a167e812abe4e3084e
[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 "InitializeThreading.h"
29 #include "JSArray.h"
30 #include "JSFunction.h"
31 #include "JSLock.h"
32 #include "JSString.h"
33 #include "PrototypeFunction.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 JSValue JSC_HOST_CALL functionPrint(ExecState*, JSObject*, JSValue, const ArgList&);
75 static JSValue JSC_HOST_CALL functionDebug(ExecState*, JSObject*, JSValue, const ArgList&);
76 static JSValue JSC_HOST_CALL functionGC(ExecState*, JSObject*, JSValue, const ArgList&);
77 static JSValue JSC_HOST_CALL functionVersion(ExecState*, JSObject*, JSValue, const ArgList&);
78 static JSValue JSC_HOST_CALL functionRun(ExecState*, JSObject*, JSValue, const ArgList&);
79 static JSValue JSC_HOST_CALL functionLoad(ExecState*, JSObject*, JSValue, const ArgList&);
80 static JSValue JSC_HOST_CALL functionCheckSyntax(ExecState*, JSObject*, JSValue, const ArgList&);
81 static JSValue JSC_HOST_CALL functionReadline(ExecState*, JSObject*, JSValue, const ArgList&);
82 static NO_RETURN_WITH_VALUE JSValue JSC_HOST_CALL functionQuit(ExecState*, JSObject*, JSValue, const ArgList&);
83
84 #if ENABLE(SAMPLING_FLAGS)
85 static JSValue JSC_HOST_CALL functionSetSamplingFlags(ExecState*, JSObject*, JSValue, const ArgList&);
86 static JSValue JSC_HOST_CALL functionClearSamplingFlags(ExecState*, JSObject*, JSValue, const ArgList&);
87 #endif
88
89 struct Script {
90 bool isFile;
91 char* argument;
92
93 Script(bool isFile, char *argument)
94 : isFile(isFile)
95 , argument(argument)
96 {
97 }
98 };
99
100 struct Options {
101 Options()
102 : interactive(false)
103 , dump(false)
104 {
105 }
106
107 bool interactive;
108 bool dump;
109 Vector<Script> scripts;
110 Vector<UString> arguments;
111 };
112
113 static const char interactivePrompt[] = "> ";
114 static const UString interpreterName("Interpreter");
115
116 class StopWatch {
117 public:
118 void start();
119 void stop();
120 long getElapsedMS(); // call stop() first
121
122 private:
123 double m_startTime;
124 double m_stopTime;
125 };
126
127 void StopWatch::start()
128 {
129 m_startTime = currentTime();
130 }
131
132 void StopWatch::stop()
133 {
134 m_stopTime = currentTime();
135 }
136
137 long StopWatch::getElapsedMS()
138 {
139 return static_cast<long>((m_stopTime - m_startTime) * 1000);
140 }
141
142 class GlobalObject : public JSGlobalObject {
143 public:
144 GlobalObject(const Vector<UString>& arguments);
145 virtual UString className() const { return "global"; }
146 };
147 COMPILE_ASSERT(!IsInteger<GlobalObject>::value, WTF_IsInteger_GlobalObject_false);
148 ASSERT_CLASS_FITS_IN_CELL(GlobalObject);
149
150 GlobalObject::GlobalObject(const Vector<UString>& arguments)
151 : JSGlobalObject()
152 {
153 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "debug"), functionDebug));
154 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "print"), functionPrint));
155 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 0, Identifier(globalExec(), "quit"), functionQuit));
156 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 0, Identifier(globalExec(), "gc"), functionGC));
157 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "version"), functionVersion));
158 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "run"), functionRun));
159 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "load"), functionLoad));
160 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "checkSyntax"), functionCheckSyntax));
161 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 0, Identifier(globalExec(), "readline"), functionReadline));
162
163 #if ENABLE(SAMPLING_FLAGS)
164 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "setSamplingFlags"), functionSetSamplingFlags));
165 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "clearSamplingFlags"), functionClearSamplingFlags));
166 #endif
167
168 JSObject* array = constructEmptyArray(globalExec());
169 for (size_t i = 0; i < arguments.size(); ++i)
170 array->put(globalExec(), i, jsString(globalExec(), arguments[i]));
171 putDirect(Identifier(globalExec(), "arguments"), array);
172 }
173
174 JSValue JSC_HOST_CALL functionPrint(ExecState* exec, JSObject*, JSValue, const ArgList& args)
175 {
176 for (unsigned i = 0; i < args.size(); ++i) {
177 if (i)
178 putchar(' ');
179
180 printf("%s", args.at(i).toString(exec).UTF8String().data());
181 }
182
183 putchar('\n');
184 fflush(stdout);
185 return jsUndefined();
186 }
187
188 JSValue JSC_HOST_CALL functionDebug(ExecState* exec, JSObject*, JSValue, const ArgList& args)
189 {
190 fprintf(stderr, "--> %s\n", args.at(0).toString(exec).UTF8String().data());
191 return jsUndefined();
192 }
193
194 JSValue JSC_HOST_CALL functionGC(ExecState* exec, JSObject*, JSValue, const ArgList&)
195 {
196 JSLock lock(SilenceAssertionsOnly);
197 exec->heap()->collectAllGarbage();
198 return jsUndefined();
199 }
200
201 JSValue JSC_HOST_CALL functionVersion(ExecState*, JSObject*, JSValue, const ArgList&)
202 {
203 // We need this function for compatibility with the Mozilla JS tests but for now
204 // we don't actually do any version-specific handling
205 return jsUndefined();
206 }
207
208 JSValue JSC_HOST_CALL functionRun(ExecState* exec, JSObject*, JSValue, const ArgList& args)
209 {
210 StopWatch stopWatch;
211 UString fileName = args.at(0).toString(exec);
212 Vector<char> script;
213 if (!fillBufferWithContentsOfFile(fileName, script))
214 return throwError(exec, GeneralError, "Could not open file.");
215
216 JSGlobalObject* globalObject = exec->lexicalGlobalObject();
217
218 stopWatch.start();
219 evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(script.data(), fileName));
220 stopWatch.stop();
221
222 return jsNumber(globalObject->globalExec(), stopWatch.getElapsedMS());
223 }
224
225 JSValue JSC_HOST_CALL functionLoad(ExecState* exec, JSObject* o, JSValue v, const ArgList& args)
226 {
227 UNUSED_PARAM(o);
228 UNUSED_PARAM(v);
229 UString fileName = args.at(0).toString(exec);
230 Vector<char> script;
231 if (!fillBufferWithContentsOfFile(fileName, script))
232 return throwError(exec, GeneralError, "Could not open file.");
233
234 JSGlobalObject* globalObject = exec->lexicalGlobalObject();
235 Completion result = evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(script.data(), fileName));
236 if (result.complType() == Throw)
237 exec->setException(result.value());
238 return result.value();
239 }
240
241 JSValue JSC_HOST_CALL functionCheckSyntax(ExecState* exec, JSObject* o, JSValue v, const ArgList& args)
242 {
243 UNUSED_PARAM(o);
244 UNUSED_PARAM(v);
245 UString fileName = args.at(0).toString(exec);
246 Vector<char> script;
247 if (!fillBufferWithContentsOfFile(fileName, script))
248 return throwError(exec, GeneralError, "Could not open file.");
249
250 JSGlobalObject* globalObject = exec->lexicalGlobalObject();
251 Completion result = checkSyntax(globalObject->globalExec(), makeSource(script.data(), fileName));
252 if (result.complType() == Throw)
253 exec->setException(result.value());
254 return result.value();
255 }
256
257 #if ENABLE(SAMPLING_FLAGS)
258 JSValue JSC_HOST_CALL functionSetSamplingFlags(ExecState* exec, JSObject*, JSValue, const ArgList& args)
259 {
260 for (unsigned i = 0; i < args.size(); ++i) {
261 unsigned flag = static_cast<unsigned>(args.at(i).toNumber(exec));
262 if ((flag >= 1) && (flag <= 32))
263 SamplingFlags::setFlag(flag);
264 }
265 return jsNull();
266 }
267
268 JSValue JSC_HOST_CALL functionClearSamplingFlags(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::clearFlag(flag);
274 }
275 return jsNull();
276 }
277 #endif
278
279 JSValue JSC_HOST_CALL functionReadline(ExecState* exec, JSObject*, JSValue, const ArgList&)
280 {
281 Vector<char, 256> line;
282 int c;
283 while ((c = getchar()) != EOF) {
284 // FIXME: Should we also break on \r?
285 if (c == '\n')
286 break;
287 line.append(c);
288 }
289 line.append('\0');
290 return jsString(exec, line.data());
291 }
292
293 JSValue JSC_HOST_CALL functionQuit(ExecState* exec, JSObject*, JSValue, const ArgList&)
294 {
295 // Technically, destroying the heap in the middle of JS execution is a no-no,
296 // but we want to maintain compatibility with the Mozilla test suite, so
297 // we pretend that execution has terminated to avoid ASSERTs, then tear down the heap.
298 exec->globalData().dynamicGlobalObject = 0;
299
300 cleanupGlobalData(&exec->globalData());
301 exit(EXIT_SUCCESS);
302
303 #if COMPILER(MSVC) && OS(WINCE)
304 // Without this, Visual Studio will complain that this method does not return a value.
305 return jsUndefined();
306 #endif
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
322 int jscmain(int argc, char** argv, JSGlobalData*);
323
324 int main(int argc, char** argv)
325 {
326 #if defined(_DEBUG) && OS(WINDOWS)
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) && !OS(WINCE)
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(ThreadStackTypeLarge).releaseRef();
350 TRY
351 res = jscmain(argc, argv, globalData);
352 EXCEPT(res = 3)
353
354 cleanupGlobalData(globalData);
355 return res;
356 }
357
358 static void cleanupGlobalData(JSGlobalData* globalData)
359 {
360 JSLock lock(SilenceAssertionsOnly);
361 globalData->heap.destroy();
362 globalData->deref();
363 }
364
365 static 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 JSGlobalData* globalData = globalObject->globalData();
375
376 #if ENABLE(SAMPLING_FLAGS)
377 SamplingFlags::start();
378 #endif
379
380 bool success = true;
381 for (size_t i = 0; i < scripts.size(); i++) {
382 if (scripts[i].isFile) {
383 fileName = scripts[i].argument;
384 if (!fillBufferWithContentsOfFile(fileName, scriptBuffer))
385 return false; // fail early so we can catch missing files
386 script = scriptBuffer.data();
387 } else {
388 script = scripts[i].argument;
389 fileName = "[Command Line]";
390 }
391
392 globalData->startSampling();
393
394 Completion completion = evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(script, fileName));
395 success = success && completion.complType() != Throw;
396 if (dump) {
397 if (completion.complType() == Throw)
398 printf("Exception: %s\n", completion.value().toString(globalObject->globalExec()).ascii());
399 else
400 printf("End: %s\n", completion.value().toString(globalObject->globalExec()).ascii());
401 }
402
403 globalData->stopSampling();
404 globalObject->globalExec()->clearException();
405 }
406
407 #if ENABLE(SAMPLING_FLAGS)
408 SamplingFlags::stop();
409 #endif
410 globalData->dumpSampleData(globalObject->globalExec());
411 #if ENABLE(SAMPLING_COUNTERS)
412 AbstractSamplingCounter::dump();
413 #endif
414 return success;
415 }
416
417 #define RUNNING_FROM_XCODE 0
418
419 static void runInteractive(GlobalObject* globalObject)
420 {
421 while (true) {
422 #if HAVE(READLINE) && !RUNNING_FROM_XCODE
423 char* line = readline(interactivePrompt);
424 if (!line)
425 break;
426 if (line[0])
427 add_history(line);
428 Completion completion = evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(line, interpreterName));
429 free(line);
430 #else
431 printf("%s", interactivePrompt);
432 Vector<char, 256> line;
433 int c;
434 while ((c = getchar()) != EOF) {
435 // FIXME: Should we also break on \r?
436 if (c == '\n')
437 break;
438 line.append(c);
439 }
440 if (line.isEmpty())
441 break;
442 line.append('\0');
443 Completion completion = evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(line.data(), interpreterName));
444 #endif
445 if (completion.complType() == Throw)
446 printf("Exception: %s\n", completion.value().toString(globalObject->globalExec()).ascii());
447 else
448 printf("%s\n", completion.value().toString(globalObject->globalExec()).UTF8String().data());
449
450 globalObject->globalExec()->clearException();
451 }
452 printf("\n");
453 }
454
455 static NO_RETURN void printUsageStatement(JSGlobalData* globalData, bool help = false)
456 {
457 fprintf(stderr, "Usage: jsc [options] [files] [-- arguments]\n");
458 fprintf(stderr, " -d Dumps bytecode (debug builds only)\n");
459 fprintf(stderr, " -e Evaluate argument as script code\n");
460 fprintf(stderr, " -f Specifies a source file (deprecated)\n");
461 fprintf(stderr, " -h|--help Prints this help message\n");
462 fprintf(stderr, " -i Enables interactive mode (default if no files are specified)\n");
463 #if HAVE(SIGNAL_H)
464 fprintf(stderr, " -s Installs signal handlers that exit on a crash (Unix platforms only)\n");
465 #endif
466
467 cleanupGlobalData(globalData);
468 exit(help ? EXIT_SUCCESS : EXIT_FAILURE);
469 }
470
471 static void parseArguments(int argc, char** argv, Options& options, JSGlobalData* globalData)
472 {
473 int i = 1;
474 for (; i < argc; ++i) {
475 const char* arg = argv[i];
476 if (!strcmp(arg, "-f")) {
477 if (++i == argc)
478 printUsageStatement(globalData);
479 options.scripts.append(Script(true, argv[i]));
480 continue;
481 }
482 if (!strcmp(arg, "-e")) {
483 if (++i == argc)
484 printUsageStatement(globalData);
485 options.scripts.append(Script(false, argv[i]));
486 continue;
487 }
488 if (!strcmp(arg, "-i")) {
489 options.interactive = true;
490 continue;
491 }
492 if (!strcmp(arg, "-d")) {
493 options.dump = true;
494 continue;
495 }
496 if (!strcmp(arg, "-s")) {
497 #if HAVE(SIGNAL_H)
498 signal(SIGILL, _exit);
499 signal(SIGFPE, _exit);
500 signal(SIGBUS, _exit);
501 signal(SIGSEGV, _exit);
502 #endif
503 continue;
504 }
505 if (!strcmp(arg, "--")) {
506 ++i;
507 break;
508 }
509 if (!strcmp(arg, "-h") || !strcmp(arg, "--help"))
510 printUsageStatement(globalData, true);
511 options.scripts.append(Script(true, argv[i]));
512 }
513
514 if (options.scripts.isEmpty())
515 options.interactive = true;
516
517 for (; i < argc; ++i)
518 options.arguments.append(argv[i]);
519 }
520
521 int jscmain(int argc, char** argv, JSGlobalData* globalData)
522 {
523 JSLock lock(SilenceAssertionsOnly);
524
525 Options options;
526 parseArguments(argc, argv, options, globalData);
527
528 GlobalObject* globalObject = new (globalData) GlobalObject(options.arguments);
529 bool success = runWithScripts(globalObject, options.scripts, options.dump);
530 if (options.interactive && success)
531 runInteractive(globalObject);
532
533 return success ? 0 : 3;
534 }
535
536 static bool fillBufferWithContentsOfFile(const UString& fileName, Vector<char>& buffer)
537 {
538 FILE* f = fopen(fileName.UTF8String().data(), "r");
539 if (!f) {
540 fprintf(stderr, "Could not open file: %s\n", fileName.UTF8String().data());
541 return false;
542 }
543
544 size_t bufferSize = 0;
545 size_t bufferCapacity = 1024;
546
547 buffer.resize(bufferCapacity);
548
549 while (!feof(f) && !ferror(f)) {
550 bufferSize += fread(buffer.data() + bufferSize, 1, bufferCapacity - bufferSize, f);
551 if (bufferSize == bufferCapacity) { // guarantees space for trailing '\0'
552 bufferCapacity *= 2;
553 buffer.resize(bufferCapacity);
554 }
555 }
556 fclose(f);
557 buffer[bufferSize] = '\0';
558
559 return true;
560 }