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

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

Delayed structure sweep can leak structures without bound
https://bugs.webkit.org/show_bug.cgi?id=96546

Reviewed by Gavin Barraclough.

This patch gets rid of the separate Structure allocator in the MarkedSpace and adds two new destructor-only
allocators. We now have separate allocators for our three types of objects: those objects with no destructors,
those objects with destructors and with immortal structures, and those objects with destructors that don't have
immortal structures. All of the objects of the third type (destructors without immortal structures) now
inherit from a new class named JSDestructibleObject (which in turn is a subclass of JSNonFinalObject), which stores
the ClassInfo for these classes at a fixed offset for safe retrieval during sweeping/destruction.

Source/JavaScriptCore:

  • API/JSCallbackConstructor.cpp: Use JSDestructibleObject for JSCallbackConstructor.

(JSC):
(JSC::JSCallbackConstructor::JSCallbackConstructor):

  • API/JSCallbackConstructor.h:

(JSCallbackConstructor):

  • API/JSCallbackObject.cpp: Inherit from JSDestructibleObject for normal JSCallbackObjects and use a finalizer for

JSCallbackObject<JSGlobalObject>, since JSGlobalObject also uses a finalizer.
(JSC):
(JSC::::create): We need to move the create function for JSCallbackObject<JSGlobalObject> out of line so we can add
the finalizer for it. We don't want to add the finalizer is something like finishCreation in case somebody decides
to subclass this. We use this same technique for many other subclasses of JSGlobalObject.
(JSC::::createStructure):

  • API/JSCallbackObject.h:

(JSCallbackObject):
(JSC):

  • API/JSClassRef.cpp: Change all the JSCallbackObject<JSNonFinalObject> to use JSDestructibleObject instead.

(OpaqueJSClass::prototype):

  • API/JSObjectRef.cpp: Ditto.

(JSObjectMake):
(JSObjectGetPrivate):
(JSObjectSetPrivate):
(JSObjectGetPrivateProperty):
(JSObjectSetPrivateProperty):
(JSObjectDeletePrivateProperty):

  • API/JSValueRef.cpp: Ditto.

(JSValueIsObjectOfClass):

  • API/JSWeakObjectMapRefPrivate.cpp: Ditto.
  • JSCTypedArrayStubs.h:

(JSC):

  • JavaScriptCore.xcodeproj/project.pbxproj:
  • dfg/DFGSpeculativeJIT.h: Use the proper allocator type when doing inline allocation in the DFG.

(JSC::DFG::SpeculativeJIT::emitAllocateBasicJSObject):
(JSC::DFG::SpeculativeJIT::emitAllocateJSFinalObject):

  • heap/Heap.cpp:

(JSC):

  • heap/Heap.h: Add accessors for the various types of allocators now. Also remove the isSafeToSweepStructures function

since it's always safe to sweep Structures now.
(JSC::Heap::allocatorForObjectWithNormalDestructor):
(JSC::Heap::allocatorForObjectWithImmortalStructureDestructor):
(Heap):
(JSC::Heap::allocateWithNormalDestructor):
(JSC):
(JSC::Heap::allocateWithImmortalStructureDestructor):

  • heap/IncrementalSweeper.cpp: Remove all the logic to detect when it's safe to sweep Structures from the

IncrementalSweeper since it's always safe to sweep Structures now.
(JSC::IncrementalSweeper::IncrementalSweeper):
(JSC::IncrementalSweeper::sweepNextBlock):
(JSC::IncrementalSweeper::startSweeping):
(JSC::IncrementalSweeper::willFinishSweeping):
(JSC):

  • heap/IncrementalSweeper.h:

(IncrementalSweeper):

  • heap/MarkedAllocator.cpp: Remove the logic that was preventing us from sweeping Structures if it wasn't safe. Add

tracking of the specific destructor type of allocator.
(JSC::MarkedAllocator::tryAllocateHelper):
(JSC::MarkedAllocator::allocateBlock):

  • heap/MarkedAllocator.h:

(JSC::MarkedAllocator::destructorType):
(MarkedAllocator):
(JSC::MarkedAllocator::MarkedAllocator):
(JSC::MarkedAllocator::init):

  • heap/MarkedBlock.cpp: Add all the destructor type stuff to MarkedBlocks so that we do the right thing when sweeping.

We also use the stored destructor type to determine the right thing to do in all JSCell::classInfo() calls.
(JSC::MarkedBlock::create):
(JSC::MarkedBlock::MarkedBlock):
(JSC):
(JSC::MarkedBlock::specializedSweep):
(JSC::MarkedBlock::sweep):
(JSC::MarkedBlock::sweepHelper):

  • heap/MarkedBlock.h:

(JSC):
(JSC::MarkedBlock::allocator):
(JSC::MarkedBlock::destructorType):

  • heap/MarkedSpace.cpp: Add the new destructor allocators to MarkedSpace.

(JSC::MarkedSpace::MarkedSpace):
(JSC::MarkedSpace::resetAllocators):
(JSC::MarkedSpace::canonicalizeCellLivenessData):
(JSC::MarkedSpace::isPagedOut):
(JSC::MarkedSpace::freeBlock):

  • heap/MarkedSpace.h:

(MarkedSpace):
(JSC::MarkedSpace::immortalStructureDestructorAllocatorFor):
(JSC::MarkedSpace::normalDestructorAllocatorFor):
(JSC::MarkedSpace::allocateWithImmortalStructureDestructor):
(JSC::MarkedSpace::allocateWithNormalDestructor):
(JSC::MarkedSpace::forEachBlock):

  • heap/SlotVisitor.cpp: Add include because the symbol was needed in an inlined function.
  • jit/JIT.h: Make sure we use the correct allocator when doing inline allocations in the baseline JIT.
  • jit/JITInlineMethods.h:

(JSC::JIT::emitAllocateBasicJSObject):
(JSC::JIT::emitAllocateJSFinalObject):
(JSC::JIT::emitAllocateJSArray):

  • jsc.cpp:

(GlobalObject::create): Add finalizer here since JSGlobalObject needs to use a finalizer instead of inheriting from
JSDestructibleObject.

  • runtime/Arguments.cpp: Inherit from JSDestructibleObject.

(JSC):

  • runtime/Arguments.h:

(Arguments):
(JSC::Arguments::Arguments):

  • runtime/ErrorPrototype.cpp: Added an assert to make sure we have a trivial destructor.

(JSC):

  • runtime/Executable.h: Indicate that all of the Executable* classes have immortal Structures.

(JSC):

  • runtime/InternalFunction.cpp: Inherit from JSDestructibleObject.

(JSC):
(JSC::InternalFunction::InternalFunction):

  • runtime/InternalFunction.h:

(InternalFunction):

  • runtime/JSCell.h: Added the NEEDS_DESTRUCTOR macro to make it easier for classes to indicate that instead of being

allocated in a destructor MarkedAllocator that they will handle their destruction themselves through the
use of a finalizer.
(JSC):
(HasImmortalStructure): New template to help us determine at compile-time if a particular class
should be allocated in the immortal structure MarkedAllocator. The default value is false. In order
to be allocated in the immortal structure allocator, classes must specialize this template. Also added
a macro to make it easier for classes to specialize the template.
(JSC::allocateCell): Use the appropriate allocator depending on the destructor type.

  • runtime/JSDestructibleObject.h: Added. New class that stores the ClassInfo of any subclass so that it can be

accessed safely when the object is being destroyed.
(JSC):
(JSDestructibleObject):
(JSC::JSDestructibleObject::classInfo):
(JSC::JSDestructibleObject::JSDestructibleObject):
(JSC::JSCell::classInfo): Checks the current MarkedBlock to see where it should get the ClassInfo from so that it's always safe.

  • runtime/JSGlobalObject.cpp: JSGlobalObject now uses a finalizer instead of a destructor so that it can avoid forcing all

of its relatives in the inheritance hierarchy (e.g. JSScope) to use destructors as well.
(JSC::JSGlobalObject::reset):

  • runtime/JSGlobalObject.h:

(JSGlobalObject):
(JSC::JSGlobalObject::createRareDataIfNeeded): Since we always create a finalizer now, we don't have to worry about adding one
for the m_rareData field when it's created.
(JSC::JSGlobalObject::create):
(JSC):

  • runtime/JSGlobalThis.h: Inherit from JSDestructibleObject.

(JSGlobalThis):
(JSC::JSGlobalThis::JSGlobalThis):

  • runtime/JSPropertyNameIterator.h: Has an immortal Structure.

(JSC):

  • runtime/JSScope.cpp:

(JSC):

  • runtime/JSString.h: Has an immortal Structure.

(JSC):

  • runtime/JSWrapperObject.h: Inherit from JSDestructibleObject.

(JSWrapperObject):
(JSC::JSWrapperObject::JSWrapperObject):

  • runtime/MathObject.cpp: Cleaning up some of the inheritance stuff.

(JSC):

  • runtime/NameInstance.h: Inherit from JSDestructibleObject.

(NameInstance):

  • runtime/RegExp.h: Has immortal Structure.

(JSC):

  • runtime/RegExpObject.cpp: Inheritance cleanup.

(JSC):

  • runtime/SparseArrayValueMap.h: Has immortal Structure.

(JSC):

  • runtime/Structure.h: Has immortal Structure.

(JSC):

  • runtime/StructureChain.h: Ditto.

(JSC):

  • runtime/SymbolTable.h: Ditto.

(SharedSymbolTable):
(JSC):

Source/WebCore:

No new tests.

  • ForwardingHeaders/runtime/JSDestructableObject.h: Added.
  • bindings/js/JSDOMWrapper.h: Inherits from JSDestructibleObject.

(JSDOMWrapper):
(WebCore::JSDOMWrapper::JSDOMWrapper):

  • bindings/scripts/CodeGeneratorJS.pm: Add finalizers to anything that inherits from JSGlobalObject,

e.g. JSDOMWindow and JSWorkerContexts. For those classes we also need to use the NEEDS_DESTRUCTOR macro.
(GenerateHeader):

  • bridge/objc/objc_runtime.h: Inherit from JSDestructibleObject.

(ObjcFallbackObjectImp):

  • bridge/objc/objc_runtime.mm:

(Bindings):
(JSC::Bindings::ObjcFallbackObjectImp::ObjcFallbackObjectImp):

  • bridge/runtime_array.cpp: Use a finalizer so that JSArray isn't forced to inherit from JSDestructibleObject.

(JSC):
(JSC::RuntimeArray::destroy):

  • bridge/runtime_array.h:

(JSC::RuntimeArray::create):
(JSC):

  • bridge/runtime_object.cpp: Inherit from JSDestructibleObject.

(Bindings):
(JSC::Bindings::RuntimeObject::RuntimeObject):

  • bridge/runtime_object.h:

(RuntimeObject):

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