source: webkit/trunk/Source/JavaScriptCore/jsc.cpp@ 148849

Last change on this file since 148849 was 148849, checked in by [email protected], 12 years ago

JS Lexer and Parser should be more informative when they encounter errors
https://bugs.webkit.org/show_bug.cgi?id=114924

Reviewed by Filip Pizlo.

Source/JavaScriptCore:

Add new tokens to represent the various ways that parsing and lexing have failed.
This gives us the ability to produce better error messages in some cases,
and to indicate whether or not the failure was due to invalid source, or simply
early termination.

The jsc prompt now makes use of this so that you can write functions that
are more than one line long.

  • bytecompiler/BytecodeGenerator.cpp:

(JSC::BytecodeGenerator::generate):

  • jsc.cpp:

(stringFromUTF):
(jscSource):
(runInteractive):

  • parser/Lexer.cpp:

(JSC::::parseFourDigitUnicodeHex):
(JSC::::parseIdentifierSlowCase):
(JSC::::parseString):
(JSC::::parseStringSlowCase):
(JSC::::lex):

  • parser/Lexer.h:

(UnicodeHexValue):
(JSC::Lexer::UnicodeHexValue::UnicodeHexValue):
(JSC::Lexer::UnicodeHexValue::valueType):
(JSC::Lexer::UnicodeHexValue::isValid):
(JSC::Lexer::UnicodeHexValue::value):
(Lexer):

  • parser/Parser.h:

(JSC::Parser::getTokenName):
(JSC::Parser::updateErrorMessageSpecialCase):
(JSC::::parse):

  • parser/ParserError.h:

(ParserError):
(JSC::ParserError::ParserError):

  • parser/ParserTokens.h:
  • runtime/Completion.cpp:

(JSC):
(JSC::checkSyntax):

  • runtime/Completion.h:

(JSC):

LayoutTests:

Update test results to cover improved error messages.

  • fast/js/kde/parse-expected.txt:
  • sputnik/Conformance/07_Lexical_Conventions/7.2_White_Space/S7.2_A5_T1-expected.txt:
  • sputnik/Conformance/07_Lexical_Conventions/7.2_White_Space/S7.2_A5_T2-expected.txt:
  • sputnik/Conformance/07_Lexical_Conventions/7.2_White_Space/S7.2_A5_T3-expected.txt:
  • sputnik/Conformance/07_Lexical_Conventions/7.2_White_Space/S7.2_A5_T4-expected.txt:
  • sputnik/Conformance/07_Lexical_Conventions/7.2_White_Space/S7.2_A5_T5-expected.txt:
  • sputnik/Conformance/07_Lexical_Conventions/7.3_Line_Terminators/S7.3_A6_T1-expected.txt:
  • sputnik/Conformance/07_Lexical_Conventions/7.3_Line_Terminators/S7.3_A6_T2-expected.txt:
  • sputnik/Conformance/07_Lexical_Conventions/7.3_Line_Terminators/S7.3_A6_T3-expected.txt:
  • sputnik/Conformance/07_Lexical_Conventions/7.3_Line_Terminators/S7.3_A6_T4-expected.txt:
  • sputnik/Conformance/07_Lexical_Conventions/7.7_Punctuators/S7.7_A2_T1-expected.txt:
  • sputnik/Conformance/07_Lexical_Conventions/7.7_Punctuators/S7.7_A2_T10-expected.txt:
  • sputnik/Conformance/07_Lexical_Conventions/7.7_Punctuators/S7.7_A2_T2-expected.txt:
  • sputnik/Conformance/07_Lexical_Conventions/7.7_Punctuators/S7.7_A2_T3-expected.txt:
  • sputnik/Conformance/07_Lexical_Conventions/7.7_Punctuators/S7.7_A2_T4-expected.txt:
  • sputnik/Conformance/07_Lexical_Conventions/7.7_Punctuators/S7.7_A2_T5-expected.txt:
  • sputnik/Conformance/07_Lexical_Conventions/7.7_Punctuators/S7.7_A2_T6-expected.txt:
  • sputnik/Conformance/07_Lexical_Conventions/7.7_Punctuators/S7.7_A2_T7-expected.txt:
  • sputnik/Conformance/07_Lexical_Conventions/7.7_Punctuators/S7.7_A2_T8-expected.txt:
  • sputnik/Conformance/07_Lexical_Conventions/7.7_Punctuators/S7.7_A2_T9-expected.txt:
  • sputnik/Conformance/13_Function_Definition/S13_A7_T3-expected.txt:
  • Property svn:eol-style set to native
File size: 26.7 KB
Line 
1/*
2 * Copyright (C) 1999-2000 Harri Porten ([email protected])
3 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2012 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 "APIShims.h"
26#include "ButterflyInlines.h"
27#include "BytecodeGenerator.h"
28#include "Completion.h"
29#include "CopiedSpaceInlines.h"
30#include "ExceptionHelpers.h"
31#include "HeapStatistics.h"
32#include "InitializeThreading.h"
33#include "Interpreter.h"
34#include "JSArray.h"
35#include "JSCTypedArrayStubs.h"
36#include "JSFunction.h"
37#include "JSLock.h"
38#include "JSProxy.h"
39#include "JSString.h"
40#include "Operations.h"
41#include "SamplingTool.h"
42#include "StructureRareDataInlines.h"
43#include <math.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <string.h>
47#include <wtf/CurrentTime.h>
48#include <wtf/MainThread.h>
49#include <wtf/StringPrintStream.h>
50#include <wtf/text/StringBuilder.h>
51
52#if !OS(WINDOWS)
53#include <unistd.h>
54#endif
55
56#if HAVE(READLINE)
57// readline/history.h has a Function typedef which conflicts with the WTF::Function template from WTF/Forward.h
58// We #define it to something else to avoid this conflict.
59#define Function ReadlineFunction
60#include <readline/history.h>
61#include <readline/readline.h>
62#undef Function
63#endif
64
65#if HAVE(SYS_TIME_H)
66#include <sys/time.h>
67#endif
68
69#if HAVE(SIGNAL_H)
70#include <signal.h>
71#endif
72
73#if COMPILER(MSVC) && !OS(WINCE)
74#include <crtdbg.h>
75#include <mmsystem.h>
76#include <windows.h>
77#endif
78
79#if PLATFORM(QT)
80#include <QCoreApplication>
81#include <QDateTime>
82#endif
83
84#if PLATFORM(IOS)
85#include <fenv.h>
86#include <arm/arch.h>
87#endif
88
89using namespace JSC;
90using namespace WTF;
91
92static bool fillBufferWithContentsOfFile(const String& fileName, Vector<char>& buffer);
93
94static EncodedJSValue JSC_HOST_CALL functionPrint(ExecState*);
95static EncodedJSValue JSC_HOST_CALL functionDebug(ExecState*);
96static EncodedJSValue JSC_HOST_CALL functionDescribe(ExecState*);
97static EncodedJSValue JSC_HOST_CALL functionJSCStack(ExecState*);
98static EncodedJSValue JSC_HOST_CALL functionGC(ExecState*);
99#ifndef NDEBUG
100static EncodedJSValue JSC_HOST_CALL functionReleaseExecutableMemory(ExecState*);
101static EncodedJSValue JSC_HOST_CALL functionDumpCallFrame(ExecState*);
102#endif
103static EncodedJSValue JSC_HOST_CALL functionVersion(ExecState*);
104static EncodedJSValue JSC_HOST_CALL functionRun(ExecState*);
105static EncodedJSValue JSC_HOST_CALL functionLoad(ExecState*);
106static EncodedJSValue JSC_HOST_CALL functionCheckSyntax(ExecState*);
107static EncodedJSValue JSC_HOST_CALL functionReadline(ExecState*);
108static EncodedJSValue JSC_HOST_CALL functionPreciseTime(ExecState*);
109static NO_RETURN_WITH_VALUE EncodedJSValue JSC_HOST_CALL functionQuit(ExecState*);
110
111#if ENABLE(SAMPLING_FLAGS)
112static EncodedJSValue JSC_HOST_CALL functionSetSamplingFlags(ExecState*);
113static EncodedJSValue JSC_HOST_CALL functionClearSamplingFlags(ExecState*);
114#endif
115
116struct Script {
117 bool isFile;
118 char* argument;
119
120 Script(bool isFile, char *argument)
121 : isFile(isFile)
122 , argument(argument)
123 {
124 }
125};
126
127class CommandLine {
128public:
129 CommandLine(int argc, char** argv)
130 : m_interactive(false)
131 , m_dump(false)
132 , m_exitCode(false)
133 , m_profile(false)
134 {
135 parseArguments(argc, argv);
136 }
137
138 bool m_interactive;
139 bool m_dump;
140 bool m_exitCode;
141 Vector<Script> m_scripts;
142 Vector<String> m_arguments;
143 bool m_profile;
144 String m_profilerOutput;
145
146 void parseArguments(int, char**);
147};
148
149static const char interactivePrompt[] = ">>> ";
150
151class StopWatch {
152public:
153 void start();
154 void stop();
155 long getElapsedMS(); // call stop() first
156
157private:
158 double m_startTime;
159 double m_stopTime;
160};
161
162void StopWatch::start()
163{
164 m_startTime = currentTime();
165}
166
167void StopWatch::stop()
168{
169 m_stopTime = currentTime();
170}
171
172long StopWatch::getElapsedMS()
173{
174 return static_cast<long>((m_stopTime - m_startTime) * 1000);
175}
176
177class GlobalObject : public JSGlobalObject {
178private:
179 GlobalObject(VM&, Structure*);
180
181public:
182 typedef JSGlobalObject Base;
183
184 static GlobalObject* create(VM& vm, Structure* structure, const Vector<String>& arguments)
185 {
186 GlobalObject* object = new (NotNull, allocateCell<GlobalObject>(vm.heap)) GlobalObject(vm, structure);
187 object->finishCreation(vm, arguments);
188 vm.heap.addFinalizer(object, destroy);
189 object->setGlobalThis(vm, JSProxy::create(vm, JSProxy::createStructure(vm, object, object->prototype()), object));
190 return object;
191 }
192
193 static const bool needsDestruction = false;
194
195 static const ClassInfo s_info;
196 static const GlobalObjectMethodTable s_globalObjectMethodTable;
197
198 static Structure* createStructure(VM& vm, JSValue prototype)
199 {
200 return Structure::create(vm, 0, prototype, TypeInfo(GlobalObjectType, StructureFlags), &s_info);
201 }
202
203 static bool javaScriptExperimentsEnabled(const JSGlobalObject*) { return true; }
204
205protected:
206 void finishCreation(VM& vm, const Vector<String>& arguments)
207 {
208 Base::finishCreation(vm);
209
210 addFunction(vm, "debug", functionDebug, 1);
211 addFunction(vm, "describe", functionDescribe, 1);
212 addFunction(vm, "print", functionPrint, 1);
213 addFunction(vm, "quit", functionQuit, 0);
214 addFunction(vm, "gc", functionGC, 0);
215#ifndef NDEBUG
216 addFunction(vm, "dumpCallFrame", functionDumpCallFrame, 0);
217 addFunction(vm, "releaseExecutableMemory", functionReleaseExecutableMemory, 0);
218#endif
219 addFunction(vm, "version", functionVersion, 1);
220 addFunction(vm, "run", functionRun, 1);
221 addFunction(vm, "load", functionLoad, 1);
222 addFunction(vm, "checkSyntax", functionCheckSyntax, 1);
223 addFunction(vm, "jscStack", functionJSCStack, 1);
224 addFunction(vm, "readline", functionReadline, 0);
225 addFunction(vm, "preciseTime", functionPreciseTime, 0);
226#if ENABLE(SAMPLING_FLAGS)
227 addFunction(vm, "setSamplingFlags", functionSetSamplingFlags, 1);
228 addFunction(vm, "clearSamplingFlags", functionClearSamplingFlags, 1);
229#endif
230
231 addConstructableFunction(vm, "Uint8Array", constructJSUint8Array, 1);
232 addConstructableFunction(vm, "Uint8ClampedArray", constructJSUint8ClampedArray, 1);
233 addConstructableFunction(vm, "Uint16Array", constructJSUint16Array, 1);
234 addConstructableFunction(vm, "Uint32Array", constructJSUint32Array, 1);
235 addConstructableFunction(vm, "Int8Array", constructJSInt8Array, 1);
236 addConstructableFunction(vm, "Int16Array", constructJSInt16Array, 1);
237 addConstructableFunction(vm, "Int32Array", constructJSInt32Array, 1);
238 addConstructableFunction(vm, "Float32Array", constructJSFloat32Array, 1);
239 addConstructableFunction(vm, "Float64Array", constructJSFloat64Array, 1);
240
241 JSArray* array = constructEmptyArray(globalExec(), 0);
242 for (size_t i = 0; i < arguments.size(); ++i)
243 array->putDirectIndex(globalExec(), i, jsString(globalExec(), arguments[i]));
244 putDirect(vm, Identifier(globalExec(), "arguments"), array);
245 }
246
247 void addFunction(VM& vm, const char* name, NativeFunction function, unsigned arguments)
248 {
249 Identifier identifier(globalExec(), name);
250 putDirect(vm, identifier, JSFunction::create(globalExec(), this, arguments, identifier.string(), function));
251 }
252
253 void addConstructableFunction(VM& vm, const char* name, NativeFunction function, unsigned arguments)
254 {
255 Identifier identifier(globalExec(), name);
256 putDirect(vm, identifier, JSFunction::create(globalExec(), this, arguments, identifier.string(), function, NoIntrinsic, function));
257 }
258};
259
260COMPILE_ASSERT(!IsInteger<GlobalObject>::value, WTF_IsInteger_GlobalObject_false);
261
262const ClassInfo GlobalObject::s_info = { "global", &JSGlobalObject::s_info, 0, ExecState::globalObjectTable, CREATE_METHOD_TABLE(GlobalObject) };
263const GlobalObjectMethodTable GlobalObject::s_globalObjectMethodTable = { &allowsAccessFrom, &supportsProfiling, &supportsRichSourceInfo, &shouldInterruptScript, &javaScriptExperimentsEnabled };
264
265
266GlobalObject::GlobalObject(VM& vm, Structure* structure)
267 : JSGlobalObject(vm, structure, &s_globalObjectMethodTable)
268{
269}
270
271static inline String stringFromUTF(const char* utf8)
272{
273 // Find the the first non-ascii character, or nul.
274 const char* pos = utf8;
275 while (*pos > 0)
276 pos++;
277 size_t asciiLength = pos - utf8;
278
279 // Fast case - string is all ascii.
280 if (!*pos)
281 return String(utf8, asciiLength);
282
283 // Slow case - contains non-ascii characters, use fromUTF8WithLatin1Fallback.
284 ASSERT(*pos < 0);
285 ASSERT(strlen(utf8) == asciiLength + strlen(pos));
286 return String::fromUTF8WithLatin1Fallback(utf8, asciiLength + strlen(pos));
287}
288
289static inline SourceCode jscSource(const char* utf8, const String& filename)
290{
291 String str = stringFromUTF(utf8);
292 return makeSource(str, filename);
293}
294
295EncodedJSValue JSC_HOST_CALL functionPrint(ExecState* exec)
296{
297 for (unsigned i = 0; i < exec->argumentCount(); ++i) {
298 if (i)
299 putchar(' ');
300
301 printf("%s", exec->argument(i).toString(exec)->value(exec).utf8().data());
302 }
303
304 putchar('\n');
305 fflush(stdout);
306 return JSValue::encode(jsUndefined());
307}
308
309#ifndef NDEBUG
310EncodedJSValue JSC_HOST_CALL functionDumpCallFrame(ExecState* exec)
311{
312 if (!exec->callerFrame()->hasHostCallFrameFlag())
313 exec->vm().interpreter->dumpCallFrame(exec->callerFrame());
314 return JSValue::encode(jsUndefined());
315}
316#endif
317
318EncodedJSValue JSC_HOST_CALL functionDebug(ExecState* exec)
319{
320 fprintf(stderr, "--> %s\n", exec->argument(0).toString(exec)->value(exec).utf8().data());
321 return JSValue::encode(jsUndefined());
322}
323
324EncodedJSValue JSC_HOST_CALL functionDescribe(ExecState* exec)
325{
326 fprintf(stderr, "--> %s\n", toCString(exec->argument(0)).data());
327 return JSValue::encode(jsUndefined());
328}
329
330EncodedJSValue JSC_HOST_CALL functionJSCStack(ExecState* exec)
331{
332 StringBuilder trace;
333 trace.appendLiteral("--> Stack trace:\n");
334
335 Vector<StackFrame> stackTrace;
336 Interpreter::getStackTrace(&exec->vm(), stackTrace);
337 int i = 0;
338
339 for (Vector<StackFrame>::iterator iter = stackTrace.begin(); iter < stackTrace.end(); iter++) {
340 StackFrame level = *iter;
341 trace.append(String::format(" %i %s\n", i, level.toString(exec).utf8().data()));
342 i++;
343 }
344 fprintf(stderr, "%s", trace.toString().utf8().data());
345 return JSValue::encode(jsUndefined());
346}
347
348EncodedJSValue JSC_HOST_CALL functionGC(ExecState* exec)
349{
350 JSLockHolder lock(exec);
351 exec->heap()->collectAllGarbage();
352 return JSValue::encode(jsUndefined());
353}
354
355#ifndef NDEBUG
356EncodedJSValue JSC_HOST_CALL functionReleaseExecutableMemory(ExecState* exec)
357{
358 JSLockHolder lock(exec);
359 exec->vm().releaseExecutableMemory();
360 return JSValue::encode(jsUndefined());
361}
362#endif
363
364EncodedJSValue JSC_HOST_CALL functionVersion(ExecState*)
365{
366 // We need this function for compatibility with the Mozilla JS tests but for now
367 // we don't actually do any version-specific handling
368 return JSValue::encode(jsUndefined());
369}
370
371EncodedJSValue JSC_HOST_CALL functionRun(ExecState* exec)
372{
373 String fileName = exec->argument(0).toString(exec)->value(exec);
374 Vector<char> script;
375 if (!fillBufferWithContentsOfFile(fileName, script))
376 return JSValue::encode(throwError(exec, createError(exec, "Could not open file.")));
377
378 GlobalObject* globalObject = GlobalObject::create(exec->vm(), GlobalObject::createStructure(exec->vm(), jsNull()), Vector<String>());
379
380 JSValue exception;
381 StopWatch stopWatch;
382 stopWatch.start();
383 evaluate(globalObject->globalExec(), jscSource(script.data(), fileName), JSValue(), &exception);
384 stopWatch.stop();
385
386 if (!!exception) {
387 throwError(globalObject->globalExec(), exception);
388 return JSValue::encode(jsUndefined());
389 }
390
391 return JSValue::encode(jsNumber(stopWatch.getElapsedMS()));
392}
393
394EncodedJSValue JSC_HOST_CALL functionLoad(ExecState* exec)
395{
396 String fileName = exec->argument(0).toString(exec)->value(exec);
397 Vector<char> script;
398 if (!fillBufferWithContentsOfFile(fileName, script))
399 return JSValue::encode(throwError(exec, createError(exec, "Could not open file.")));
400
401 JSGlobalObject* globalObject = exec->lexicalGlobalObject();
402
403 JSValue evaluationException;
404 JSValue result = evaluate(globalObject->globalExec(), jscSource(script.data(), fileName), JSValue(), &evaluationException);
405 if (evaluationException)
406 throwError(exec, evaluationException);
407 return JSValue::encode(result);
408}
409
410EncodedJSValue JSC_HOST_CALL functionCheckSyntax(ExecState* exec)
411{
412 String fileName = exec->argument(0).toString(exec)->value(exec);
413 Vector<char> script;
414 if (!fillBufferWithContentsOfFile(fileName, script))
415 return JSValue::encode(throwError(exec, createError(exec, "Could not open file.")));
416
417 JSGlobalObject* globalObject = exec->lexicalGlobalObject();
418
419 StopWatch stopWatch;
420 stopWatch.start();
421
422 JSValue syntaxException;
423 bool validSyntax = checkSyntax(globalObject->globalExec(), jscSource(script.data(), fileName), &syntaxException);
424 stopWatch.stop();
425
426 if (!validSyntax)
427 throwError(exec, syntaxException);
428 return JSValue::encode(jsNumber(stopWatch.getElapsedMS()));
429}
430
431#if ENABLE(SAMPLING_FLAGS)
432EncodedJSValue JSC_HOST_CALL functionSetSamplingFlags(ExecState* exec)
433{
434 for (unsigned i = 0; i < exec->argumentCount(); ++i) {
435 unsigned flag = static_cast<unsigned>(exec->argument(i).toNumber(exec));
436 if ((flag >= 1) && (flag <= 32))
437 SamplingFlags::setFlag(flag);
438 }
439 return JSValue::encode(jsNull());
440}
441
442EncodedJSValue JSC_HOST_CALL functionClearSamplingFlags(ExecState* exec)
443{
444 for (unsigned i = 0; i < exec->argumentCount(); ++i) {
445 unsigned flag = static_cast<unsigned>(exec->argument(i).toNumber(exec));
446 if ((flag >= 1) && (flag <= 32))
447 SamplingFlags::clearFlag(flag);
448 }
449 return JSValue::encode(jsNull());
450}
451#endif
452
453EncodedJSValue JSC_HOST_CALL functionReadline(ExecState* exec)
454{
455 Vector<char, 256> line;
456 int c;
457 while ((c = getchar()) != EOF) {
458 // FIXME: Should we also break on \r?
459 if (c == '\n')
460 break;
461 line.append(c);
462 }
463 line.append('\0');
464 return JSValue::encode(jsString(exec, line.data()));
465}
466
467EncodedJSValue JSC_HOST_CALL functionPreciseTime(ExecState*)
468{
469 return JSValue::encode(jsNumber(currentTime()));
470}
471
472EncodedJSValue JSC_HOST_CALL functionQuit(ExecState*)
473{
474 exit(EXIT_SUCCESS);
475
476#if COMPILER(MSVC) && OS(WINCE)
477 // Without this, Visual Studio will complain that this method does not return a value.
478 return JSValue::encode(jsUndefined());
479#endif
480}
481
482// Use SEH for Release builds only to get rid of the crash report dialog
483// (luckily the same tests fail in Release and Debug builds so far). Need to
484// be in a separate main function because the jscmain function requires object
485// unwinding.
486
487#if COMPILER(MSVC) && !COMPILER(INTEL) && !defined(_DEBUG) && !OS(WINCE)
488#define TRY __try {
489#define EXCEPT(x) } __except (EXCEPTION_EXECUTE_HANDLER) { x; }
490#else
491#define TRY
492#define EXCEPT(x)
493#endif
494
495int jscmain(int argc, char** argv);
496
497int main(int argc, char** argv)
498{
499#if PLATFORM(IOS)
500 // Enabled IEEE754 denormal support.
501 fenv_t env;
502 fegetenv( &env );
503 env.__fpscr &= ~0x01000000u;
504 fesetenv( &env );
505#endif
506
507#if OS(WINDOWS)
508#if !OS(WINCE)
509 // Cygwin calls ::SetErrorMode(SEM_FAILCRITICALERRORS), which we will inherit. This is bad for
510 // testing/debugging, as it causes the post-mortem debugger not to be invoked. We reset the
511 // error mode here to work around Cygwin's behavior. See <http://webkit.org/b/55222>.
512 ::SetErrorMode(0);
513#endif
514
515#if defined(_DEBUG)
516 _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
517 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
518 _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
519 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
520 _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
521 _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
522#endif
523
524 timeBeginPeriod(1);
525#endif
526
527#if PLATFORM(QT)
528 QCoreApplication app(argc, argv);
529#endif
530
531 // Initialize JSC before getting VM.
532#if ENABLE(SAMPLING_REGIONS)
533 WTF::initializeMainThread();
534#endif
535 JSC::initializeThreading();
536
537 // We can't use destructors in the following code because it uses Windows
538 // Structured Exception Handling
539 int res = 0;
540 TRY
541 res = jscmain(argc, argv);
542 EXCEPT(res = 3)
543 if (Options::logHeapStatisticsAtExit())
544 HeapStatistics::reportSuccess();
545 return res;
546}
547
548static bool runWithScripts(GlobalObject* globalObject, const Vector<Script>& scripts, bool dump)
549{
550 const char* script;
551 String fileName;
552 Vector<char> scriptBuffer;
553
554 if (dump)
555 JSC::Options::dumpGeneratedBytecodes() = true;
556
557 VM& vm = globalObject->vm();
558
559#if ENABLE(SAMPLING_FLAGS)
560 SamplingFlags::start();
561#endif
562
563 bool success = true;
564 for (size_t i = 0; i < scripts.size(); i++) {
565 if (scripts[i].isFile) {
566 fileName = scripts[i].argument;
567 if (!fillBufferWithContentsOfFile(fileName, scriptBuffer))
568 return false; // fail early so we can catch missing files
569 script = scriptBuffer.data();
570 } else {
571 script = scripts[i].argument;
572 fileName = "[Command Line]";
573 }
574
575 vm.startSampling();
576
577 JSValue evaluationException;
578 JSValue returnValue = evaluate(globalObject->globalExec(), jscSource(script, fileName), JSValue(), &evaluationException);
579 success = success && !evaluationException;
580 if (dump && !evaluationException)
581 printf("End: %s\n", returnValue.toString(globalObject->globalExec())->value(globalObject->globalExec()).utf8().data());
582 if (evaluationException) {
583 printf("Exception: %s\n", evaluationException.toString(globalObject->globalExec())->value(globalObject->globalExec()).utf8().data());
584 Identifier stackID(globalObject->globalExec(), "stack");
585 JSValue stackValue = evaluationException.get(globalObject->globalExec(), stackID);
586 if (!stackValue.isUndefinedOrNull())
587 printf("%s\n", stackValue.toString(globalObject->globalExec())->value(globalObject->globalExec()).utf8().data());
588 }
589
590 vm.stopSampling();
591 globalObject->globalExec()->clearException();
592 }
593
594#if ENABLE(SAMPLING_FLAGS)
595 SamplingFlags::stop();
596#endif
597#if ENABLE(SAMPLING_REGIONS)
598 SamplingRegion::dump();
599#endif
600 vm.dumpSampleData(globalObject->globalExec());
601#if ENABLE(SAMPLING_COUNTERS)
602 AbstractSamplingCounter::dump();
603#endif
604#if ENABLE(REGEXP_TRACING)
605 vm.dumpRegExpTrace();
606#endif
607 return success;
608}
609
610#define RUNNING_FROM_XCODE 0
611
612static void runInteractive(GlobalObject* globalObject)
613{
614 String interpreterName("Interpreter");
615
616 bool shouldQuit = false;
617 while (!shouldQuit) {
618#if HAVE(READLINE) && !RUNNING_FROM_XCODE
619 ParserError error;
620 String source;
621 do {
622 error = ParserError();
623 char* line = readline(source.isEmpty() ? interactivePrompt : "... ");
624 source = source + line;
625 source = source + '\n';
626 checkSyntax(globalObject->globalExec(), makeSource(source, interpreterName), error);
627 shouldQuit = !line;
628 if (!line || !line[0])
629 break;
630 if (line[0])
631 add_history(line);
632 } while (error.m_syntaxErrorType == ParserError::SyntaxErrorRecoverable);
633
634 if (error.m_type != ParserError::ErrorNone) {
635 printf("%s:%d\n", error.m_message.utf8().data(), error.m_line);
636 continue;
637 }
638
639
640 JSValue evaluationException;
641 JSValue returnValue = evaluate(globalObject->globalExec(), makeSource(source, interpreterName), JSValue(), &evaluationException);
642#else
643 printf("%s", interactivePrompt);
644 Vector<char, 256> line;
645 int c;
646 while ((c = getchar()) != EOF) {
647 // FIXME: Should we also break on \r?
648 if (c == '\n')
649 break;
650 line.append(c);
651 }
652 if (line.isEmpty())
653 break;
654 line.append('\0');
655
656 JSValue evaluationException;
657 JSValue returnValue = evaluate(globalObject->globalExec(), jscSource(line.data(), interpreterName), JSValue(), &evaluationException);
658#endif
659 if (evaluationException)
660 printf("Exception: %s\n", evaluationException.toString(globalObject->globalExec())->value(globalObject->globalExec()).utf8().data());
661 else
662 printf("%s\n", returnValue.toString(globalObject->globalExec())->value(globalObject->globalExec()).utf8().data());
663
664 globalObject->globalExec()->clearException();
665 }
666 printf("\n");
667}
668
669static NO_RETURN void printUsageStatement(bool help = false)
670{
671 fprintf(stderr, "Usage: jsc [options] [files] [-- arguments]\n");
672 fprintf(stderr, " -d Dumps bytecode (debug builds only)\n");
673 fprintf(stderr, " -e Evaluate argument as script code\n");
674 fprintf(stderr, " -f Specifies a source file (deprecated)\n");
675 fprintf(stderr, " -h|--help Prints this help message\n");
676 fprintf(stderr, " -i Enables interactive mode (default if no files are specified)\n");
677#if HAVE(SIGNAL_H)
678 fprintf(stderr, " -s Installs signal handlers that exit on a crash (Unix platforms only)\n");
679#endif
680 fprintf(stderr, " -p <file> Outputs profiling data to a file\n");
681 fprintf(stderr, " -x Output exit code before terminating\n");
682 fprintf(stderr, "\n");
683 fprintf(stderr, " --options Dumps all JSC VM options and exits\n");
684 fprintf(stderr, " --dumpOptions Dumps all JSC VM options before continuing\n");
685 fprintf(stderr, " --<jsc VM option>=<value> Sets the specified JSC VM option\n");
686 fprintf(stderr, "\n");
687
688 exit(help ? EXIT_SUCCESS : EXIT_FAILURE);
689}
690
691void CommandLine::parseArguments(int argc, char** argv)
692{
693 int i = 1;
694 bool needToDumpOptions = false;
695 bool needToExit = false;
696
697 for (; i < argc; ++i) {
698 const char* arg = argv[i];
699 if (!strcmp(arg, "-f")) {
700 if (++i == argc)
701 printUsageStatement();
702 m_scripts.append(Script(true, argv[i]));
703 continue;
704 }
705 if (!strcmp(arg, "-e")) {
706 if (++i == argc)
707 printUsageStatement();
708 m_scripts.append(Script(false, argv[i]));
709 continue;
710 }
711 if (!strcmp(arg, "-i")) {
712 m_interactive = true;
713 continue;
714 }
715 if (!strcmp(arg, "-d")) {
716 m_dump = true;
717 continue;
718 }
719 if (!strcmp(arg, "-p")) {
720 if (++i == argc)
721 printUsageStatement();
722 m_profile = true;
723 m_profilerOutput = argv[i];
724 continue;
725 }
726 if (!strcmp(arg, "-s")) {
727#if HAVE(SIGNAL_H)
728 signal(SIGILL, _exit);
729 signal(SIGFPE, _exit);
730 signal(SIGBUS, _exit);
731 signal(SIGSEGV, _exit);
732#endif
733 continue;
734 }
735 if (!strcmp(arg, "-x")) {
736 m_exitCode = true;
737 continue;
738 }
739 if (!strcmp(arg, "--")) {
740 ++i;
741 break;
742 }
743 if (!strcmp(arg, "-h") || !strcmp(arg, "--help"))
744 printUsageStatement(true);
745
746 if (!strcmp(arg, "--options")) {
747 needToDumpOptions = true;
748 needToExit = true;
749 continue;
750 }
751 if (!strcmp(arg, "--dumpOptions")) {
752 needToDumpOptions = true;
753 continue;
754 }
755
756 // See if the -- option is a JSC VM option.
757 // NOTE: At this point, we know that the arg starts with "--". Skip it.
758 if (JSC::Options::setOption(&arg[2])) {
759 // The arg was recognized as a VM option and has been parsed.
760 continue; // Just continue with the next arg.
761 }
762
763 // This arg is not recognized by the VM nor by jsc. Pass it on to the
764 // script.
765 m_scripts.append(Script(true, argv[i]));
766 }
767
768 if (m_scripts.isEmpty())
769 m_interactive = true;
770
771 for (; i < argc; ++i)
772 m_arguments.append(argv[i]);
773
774 if (needToDumpOptions)
775 JSC::Options::dumpAllOptions(stderr);
776 if (needToExit)
777 exit(EXIT_SUCCESS);
778}
779
780int jscmain(int argc, char** argv)
781{
782 // Note that the options parsing can affect VM creation, and thus
783 // comes first.
784 CommandLine options(argc, argv);
785 VM* vm = VM::create(LargeHeap).leakRef();
786 APIEntryShim shim(vm);
787 int result;
788
789 if (options.m_profile && !vm->m_perBytecodeProfiler)
790 vm->m_perBytecodeProfiler = adoptPtr(new Profiler::Database(*vm));
791
792 GlobalObject* globalObject = GlobalObject::create(*vm, GlobalObject::createStructure(*vm, jsNull()), options.m_arguments);
793 bool success = runWithScripts(globalObject, options.m_scripts, options.m_dump);
794 if (options.m_interactive && success)
795 runInteractive(globalObject);
796
797 result = success ? 0 : 3;
798
799 if (options.m_exitCode)
800 printf("jsc exiting %d\n", result);
801
802 if (options.m_profile) {
803 if (!vm->m_perBytecodeProfiler->save(options.m_profilerOutput.utf8().data()))
804 fprintf(stderr, "could not save profiler output.\n");
805 }
806
807 return result;
808}
809
810static bool fillBufferWithContentsOfFile(const String& fileName, Vector<char>& buffer)
811{
812 FILE* f = fopen(fileName.utf8().data(), "r");
813 if (!f) {
814 fprintf(stderr, "Could not open file: %s\n", fileName.utf8().data());
815 return false;
816 }
817
818 size_t bufferSize = 0;
819 size_t bufferCapacity = 1024;
820
821 buffer.resize(bufferCapacity);
822
823 while (!feof(f) && !ferror(f)) {
824 bufferSize += fread(buffer.data() + bufferSize, 1, bufferCapacity - bufferSize, f);
825 if (bufferSize == bufferCapacity) { // guarantees space for trailing '\0'
826 bufferCapacity *= 2;
827 buffer.resize(bufferCapacity);
828 }
829 }
830 fclose(f);
831 buffer[bufferSize] = '\0';
832
833 if (buffer[0] == '#' && buffer[1] == '!')
834 buffer[0] = buffer[1] = '/';
835
836 return true;
837}
Note: See TracBrowser for help on using the repository browser.