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

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

GC timer should intelligently choose between EdenCollections and FullCollections
https://bugs.webkit.org/show_bug.cgi?id=128261

Reviewed by Geoffrey Garen.

Source/JavaScriptCore:

Most of the GCs while browsing the web are due to the GC timer. Currently the GC timer
always does FullCollections. To reduce the impact of the GC timer on the system this patch
changes Heap so that it has two timers, one for each type of collection. The FullCollection
timer is notified at the end of EdenCollections how much the Heap has grown since the last
FullCollection and when somebody notifies the Heap of abandoned memory (which usually wouldn't
be detected by an EdenCollection).

  • CMakeLists.txt:
  • GNUmakefile.list.am:
  • JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
  • JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters:
  • JavaScriptCore.xcodeproj/project.pbxproj:
  • heap/EdenGCActivityCallback.cpp: Added.

(JSC::EdenGCActivityCallback::EdenGCActivityCallback):
(JSC::EdenGCActivityCallback::doCollection):
(JSC::EdenGCActivityCallback::lastGCLength):
(JSC::EdenGCActivityCallback::deathRate):
(JSC::EdenGCActivityCallback::gcTimeSlice):

  • heap/EdenGCActivityCallback.h: Added.

(JSC::GCActivityCallback::createEdenTimer):

  • heap/FullGCActivityCallback.cpp: Added.

(JSC::FullGCActivityCallback::FullGCActivityCallback):
(JSC::FullGCActivityCallback::doCollection):
(JSC::FullGCActivityCallback::lastGCLength):
(JSC::FullGCActivityCallback::deathRate):
(JSC::FullGCActivityCallback::gcTimeSlice):

  • heap/FullGCActivityCallback.h: Added.

(JSC::GCActivityCallback::createFullTimer):

  • heap/GCActivityCallback.cpp:

(JSC::GCActivityCallback::GCActivityCallback):
(JSC::GCActivityCallback::doWork):
(JSC::GCActivityCallback::scheduleTimer):
(JSC::GCActivityCallback::cancelTimer):
(JSC::GCActivityCallback::didAllocate):
(JSC::GCActivityCallback::willCollect):
(JSC::GCActivityCallback::cancel):

  • heap/GCActivityCallback.h:
  • heap/Heap.cpp:

(JSC::Heap::Heap):
(JSC::Heap::reportAbandonedObjectGraph):
(JSC::Heap::didAbandon):
(JSC::Heap::collectAllGarbage):
(JSC::Heap::collect):
(JSC::Heap::willStartCollection):
(JSC::Heap::updateAllocationLimits):
(JSC::Heap::didFinishCollection):
(JSC::Heap::setFullActivityCallback):
(JSC::Heap::setEdenActivityCallback):
(JSC::Heap::fullActivityCallback):
(JSC::Heap::edenActivityCallback):
(JSC::Heap::setGarbageCollectionTimerEnabled):
(JSC::Heap::didAllocate):
(JSC::Heap::shouldDoFullCollection):

  • heap/Heap.h:

(JSC::Heap::lastFullGCLength):
(JSC::Heap::lastEdenGCLength):
(JSC::Heap::increaseLastFullGCLength):
(JSC::Heap::sizeBeforeLastEdenCollection):
(JSC::Heap::sizeAfterLastEdenCollection):
(JSC::Heap::sizeBeforeLastFullCollection):
(JSC::Heap::sizeAfterLastFullCollection):

  • heap/HeapOperation.h:
  • heap/HeapStatistics.cpp:

(JSC::HeapStatistics::showObjectStatistics):

  • heap/HeapTimer.cpp:

(JSC::HeapTimer::timerDidFire):

  • jsc.cpp:

(functionFullGC):
(functionEdenGC):

  • runtime/Options.h:

Source/WebCore:

No new tests.

Updated WebSafeGCActivityCallbacks for both Eden and Full timers.

  • bindings/js/JSDOMWindowBase.cpp:

(WebCore::JSDOMWindowBase::commonVM):

  • platform/ios/WebSafeGCActivityCallbackIOS.h:
  • Property svn:eol-style set to native
File size: 37.5 KB
Line 
1/*
2 * Copyright (C) 1999-2000 Harri Porten ([email protected])
3 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2012, 2013 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 "ButterflyInlines.h"
26#include "BytecodeGenerator.h"
27#include "Completion.h"
28#include "CopiedSpaceInlines.h"
29#include "ExceptionHelpers.h"
30#include "HeapStatistics.h"
31#include "InitializeThreading.h"
32#include "Interpreter.h"
33#include "JSArray.h"
34#include "JSArrayBuffer.h"
35#include "JSCInlines.h"
36#include "JSFunction.h"
37#include "JSLock.h"
38#include "JSProxy.h"
39#include "JSString.h"
40#include "SamplingTool.h"
41#include "StackVisitor.h"
42#include "StructureRareDataInlines.h"
43#include "TestRunnerUtils.h"
44#include <math.h>
45#include <stdio.h>
46#include <stdlib.h>
47#include <string.h>
48#include <thread>
49#include <wtf/CurrentTime.h>
50#include <wtf/MainThread.h>
51#include <wtf/StringPrintStream.h>
52#include <wtf/text/StringBuilder.h>
53
54#if !OS(WINDOWS)
55#include <unistd.h>
56#endif
57
58#if HAVE(READLINE)
59// readline/history.h has a Function typedef which conflicts with the WTF::Function template from WTF/Forward.h
60// We #define it to something else to avoid this conflict.
61#define Function ReadlineFunction
62#include <readline/history.h>
63#include <readline/readline.h>
64#undef Function
65#endif
66
67#if HAVE(SYS_TIME_H)
68#include <sys/time.h>
69#endif
70
71#if HAVE(SIGNAL_H)
72#include <signal.h>
73#endif
74
75#if COMPILER(MSVC) && !OS(WINCE)
76#include <crtdbg.h>
77#include <mmsystem.h>
78#include <windows.h>
79#endif
80
81#if PLATFORM(IOS) && CPU(ARM_THUMB2)
82#include <fenv.h>
83#include <arm/arch.h>
84#endif
85
86#if PLATFORM(EFL)
87#include <Ecore.h>
88#endif
89
90using namespace JSC;
91using namespace WTF;
92
93namespace {
94
95class Element;
96class ElementHandleOwner;
97class Masuqerader;
98class Root;
99
100class Element : public JSNonFinalObject {
101public:
102 Element(VM& vm, Structure* structure, Root* root)
103 : Base(vm, structure)
104 , m_root(root)
105 {
106 }
107
108 typedef JSNonFinalObject Base;
109 static const bool needsDestruction = false;
110
111 Root* root() const { return m_root; }
112 void setRoot(Root* root) { m_root = root; }
113
114 static Element* create(VM& vm, JSGlobalObject* globalObject, Root* root)
115 {
116 Structure* structure = createStructure(vm, globalObject, jsNull());
117 Element* element = new (NotNull, allocateCell<Element>(vm.heap, sizeof(Element))) Element(vm, structure, root);
118 element->finishCreation(vm);
119 return element;
120 }
121
122 void finishCreation(VM&);
123
124 static ElementHandleOwner* handleOwner();
125
126 static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
127 {
128 return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
129 }
130
131 DECLARE_INFO;
132
133private:
134 Root* m_root;
135};
136
137class ElementHandleOwner : public WeakHandleOwner {
138public:
139 virtual bool isReachableFromOpaqueRoots(Handle<JSC::Unknown> handle, void*, SlotVisitor& visitor)
140 {
141 Element* element = jsCast<Element*>(handle.slot()->asCell());
142 return visitor.containsOpaqueRoot(element->root());
143 }
144};
145
146class Masquerader : public JSNonFinalObject {
147public:
148 Masquerader(VM& vm, Structure* structure)
149 : Base(vm, structure)
150 {
151 }
152
153 typedef JSNonFinalObject Base;
154
155 static Masquerader* create(VM& vm, JSGlobalObject* globalObject)
156 {
157 globalObject->masqueradesAsUndefinedWatchpoint()->fireAll();
158 Structure* structure = createStructure(vm, globalObject, jsNull());
159 Masquerader* result = new (NotNull, allocateCell<Masquerader>(vm.heap, sizeof(Masquerader))) Masquerader(vm, structure);
160 result->finishCreation(vm);
161 return result;
162 }
163
164 static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
165 {
166 return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
167 }
168
169 DECLARE_INFO;
170
171protected:
172 static const unsigned StructureFlags = JSC::MasqueradesAsUndefined | Base::StructureFlags;
173};
174
175class Root : public JSDestructibleObject {
176public:
177 Root(VM& vm, Structure* structure)
178 : Base(vm, structure)
179 {
180 }
181
182 Element* element()
183 {
184 return m_element.get();
185 }
186
187 void setElement(Element* element)
188 {
189 Weak<Element> newElement(element, Element::handleOwner());
190 m_element.swap(newElement);
191 }
192
193 static Root* create(VM& vm, JSGlobalObject* globalObject)
194 {
195 Structure* structure = createStructure(vm, globalObject, jsNull());
196 Root* root = new (NotNull, allocateCell<Root>(vm.heap, sizeof(Root))) Root(vm, structure);
197 root->finishCreation(vm);
198 return root;
199 }
200
201 typedef JSDestructibleObject Base;
202
203 DECLARE_INFO;
204 static const bool needsDestruction = true;
205
206 static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
207 {
208 return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
209 }
210
211 static void visitChildren(JSCell* thisObject, SlotVisitor& visitor)
212 {
213 Base::visitChildren(thisObject, visitor);
214 visitor.addOpaqueRoot(thisObject);
215 }
216
217private:
218 Weak<Element> m_element;
219};
220
221const ClassInfo Element::s_info = { "Element", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(Element) };
222const ClassInfo Masquerader::s_info = { "Masquerader", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(Masquerader) };
223const ClassInfo Root::s_info = { "Root", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(Root) };
224
225ElementHandleOwner* Element::handleOwner()
226{
227 static ElementHandleOwner* owner = 0;
228 if (!owner)
229 owner = new ElementHandleOwner();
230 return owner;
231}
232
233void Element::finishCreation(VM& vm)
234{
235 Base::finishCreation(vm);
236 m_root->setElement(this);
237}
238
239}
240
241static bool fillBufferWithContentsOfFile(const String& fileName, Vector<char>& buffer);
242
243static EncodedJSValue JSC_HOST_CALL functionSetElementRoot(ExecState*);
244static EncodedJSValue JSC_HOST_CALL functionCreateRoot(ExecState*);
245static EncodedJSValue JSC_HOST_CALL functionCreateElement(ExecState*);
246static EncodedJSValue JSC_HOST_CALL functionGetElement(ExecState*);
247static EncodedJSValue JSC_HOST_CALL functionPrint(ExecState*);
248static EncodedJSValue JSC_HOST_CALL functionDebug(ExecState*);
249static EncodedJSValue JSC_HOST_CALL functionDescribe(ExecState*);
250static EncodedJSValue JSC_HOST_CALL functionDescribeArray(ExecState*);
251static EncodedJSValue JSC_HOST_CALL functionJSCStack(ExecState*);
252static EncodedJSValue JSC_HOST_CALL functionGCAndSweep(ExecState*);
253static EncodedJSValue JSC_HOST_CALL functionFullGC(ExecState*);
254static EncodedJSValue JSC_HOST_CALL functionEdenGC(ExecState*);
255#ifndef NDEBUG
256static EncodedJSValue JSC_HOST_CALL functionReleaseExecutableMemory(ExecState*);
257static EncodedJSValue JSC_HOST_CALL functionDumpCallFrame(ExecState*);
258#endif
259static EncodedJSValue JSC_HOST_CALL functionVersion(ExecState*);
260static EncodedJSValue JSC_HOST_CALL functionRun(ExecState*);
261static EncodedJSValue JSC_HOST_CALL functionLoad(ExecState*);
262static EncodedJSValue JSC_HOST_CALL functionReadFile(ExecState*);
263static EncodedJSValue JSC_HOST_CALL functionCheckSyntax(ExecState*);
264static EncodedJSValue JSC_HOST_CALL functionReadline(ExecState*);
265static EncodedJSValue JSC_HOST_CALL functionPreciseTime(ExecState*);
266static EncodedJSValue JSC_HOST_CALL functionNeverInlineFunction(ExecState*);
267static EncodedJSValue JSC_HOST_CALL functionNumberOfDFGCompiles(ExecState*);
268static EncodedJSValue JSC_HOST_CALL functionReoptimizationRetryCount(ExecState*);
269static EncodedJSValue JSC_HOST_CALL functionTransferArrayBuffer(ExecState*);
270static NO_RETURN_WITH_VALUE EncodedJSValue JSC_HOST_CALL functionQuit(ExecState*);
271static EncodedJSValue JSC_HOST_CALL functionFalse(ExecState*);
272static EncodedJSValue JSC_HOST_CALL functionEffectful42(ExecState*);
273static EncodedJSValue JSC_HOST_CALL functionMakeMasquerader(ExecState*);
274
275#if ENABLE(SAMPLING_FLAGS)
276static EncodedJSValue JSC_HOST_CALL functionSetSamplingFlags(ExecState*);
277static EncodedJSValue JSC_HOST_CALL functionClearSamplingFlags(ExecState*);
278#endif
279
280struct Script {
281 bool isFile;
282 char* argument;
283
284 Script(bool isFile, char *argument)
285 : isFile(isFile)
286 , argument(argument)
287 {
288 }
289};
290
291class CommandLine {
292public:
293 CommandLine(int argc, char** argv)
294 : m_interactive(false)
295 , m_dump(false)
296 , m_exitCode(false)
297 , m_profile(false)
298 {
299 parseArguments(argc, argv);
300 }
301
302 bool m_interactive;
303 bool m_dump;
304 bool m_exitCode;
305 Vector<Script> m_scripts;
306 Vector<String> m_arguments;
307 bool m_profile;
308 String m_profilerOutput;
309
310 void parseArguments(int, char**);
311};
312
313static const char interactivePrompt[] = ">>> ";
314
315class StopWatch {
316public:
317 void start();
318 void stop();
319 long getElapsedMS(); // call stop() first
320
321private:
322 double m_startTime;
323 double m_stopTime;
324};
325
326void StopWatch::start()
327{
328 m_startTime = monotonicallyIncreasingTime();
329}
330
331void StopWatch::stop()
332{
333 m_stopTime = monotonicallyIncreasingTime();
334}
335
336long StopWatch::getElapsedMS()
337{
338 return static_cast<long>((m_stopTime - m_startTime) * 1000);
339}
340
341class GlobalObject : public JSGlobalObject {
342private:
343 GlobalObject(VM&, Structure*);
344
345public:
346 typedef JSGlobalObject Base;
347
348 static GlobalObject* create(VM& vm, Structure* structure, const Vector<String>& arguments)
349 {
350 GlobalObject* object = new (NotNull, allocateCell<GlobalObject>(vm.heap)) GlobalObject(vm, structure);
351 object->finishCreation(vm, arguments);
352 vm.heap.addFinalizer(object, destroy);
353 object->setGlobalThis(vm, JSProxy::create(vm, JSProxy::createStructure(vm, object, object->prototype()), object));
354 return object;
355 }
356
357 static const bool needsDestruction = false;
358
359 DECLARE_INFO;
360 static const GlobalObjectMethodTable s_globalObjectMethodTable;
361
362 static Structure* createStructure(VM& vm, JSValue prototype)
363 {
364 return Structure::create(vm, 0, prototype, TypeInfo(GlobalObjectType, StructureFlags), info());
365 }
366
367 static bool javaScriptExperimentsEnabled(const JSGlobalObject*) { return true; }
368
369protected:
370 void finishCreation(VM& vm, const Vector<String>& arguments)
371 {
372 Base::finishCreation(vm);
373
374 addFunction(vm, "debug", functionDebug, 1);
375 addFunction(vm, "describe", functionDescribe, 1);
376 addFunction(vm, "describeArray", functionDescribeArray, 1);
377 addFunction(vm, "print", functionPrint, 1);
378 addFunction(vm, "quit", functionQuit, 0);
379 addFunction(vm, "gc", functionGCAndSweep, 0);
380 addFunction(vm, "fullGC", functionFullGC, 0);
381 addFunction(vm, "edenGC", functionEdenGC, 0);
382#ifndef NDEBUG
383 addFunction(vm, "dumpCallFrame", functionDumpCallFrame, 0);
384 addFunction(vm, "releaseExecutableMemory", functionReleaseExecutableMemory, 0);
385#endif
386 addFunction(vm, "version", functionVersion, 1);
387 addFunction(vm, "run", functionRun, 1);
388 addFunction(vm, "load", functionLoad, 1);
389 addFunction(vm, "readFile", functionReadFile, 1);
390 addFunction(vm, "checkSyntax", functionCheckSyntax, 1);
391 addFunction(vm, "jscStack", functionJSCStack, 1);
392 addFunction(vm, "readline", functionReadline, 0);
393 addFunction(vm, "preciseTime", functionPreciseTime, 0);
394 addFunction(vm, "neverInlineFunction", functionNeverInlineFunction, 1);
395 addFunction(vm, "noInline", functionNeverInlineFunction, 1);
396 addFunction(vm, "numberOfDFGCompiles", functionNumberOfDFGCompiles, 1);
397 addFunction(vm, "reoptimizationRetryCount", functionReoptimizationRetryCount, 1);
398 addFunction(vm, "transferArrayBuffer", functionTransferArrayBuffer, 1);
399#if ENABLE(SAMPLING_FLAGS)
400 addFunction(vm, "setSamplingFlags", functionSetSamplingFlags, 1);
401 addFunction(vm, "clearSamplingFlags", functionClearSamplingFlags, 1);
402#endif
403 addConstructableFunction(vm, "Root", functionCreateRoot, 0);
404 addConstructableFunction(vm, "Element", functionCreateElement, 1);
405 addFunction(vm, "getElement", functionGetElement, 1);
406 addFunction(vm, "setElementRoot", functionSetElementRoot, 2);
407
408 putDirectNativeFunction(vm, this, Identifier(&vm, "DFGTrue"), 0, functionFalse, DFGTrue, DontEnum | JSC::Function);
409
410 addFunction(vm, "effectful42", functionEffectful42, 0);
411 addFunction(vm, "makeMasquerader", functionMakeMasquerader, 0);
412
413 JSArray* array = constructEmptyArray(globalExec(), 0);
414 for (size_t i = 0; i < arguments.size(); ++i)
415 array->putDirectIndex(globalExec(), i, jsString(globalExec(), arguments[i]));
416 putDirect(vm, Identifier(globalExec(), "arguments"), array);
417
418 putDirect(vm, Identifier(globalExec(), "console"), jsUndefined());
419 }
420
421 void addFunction(VM& vm, const char* name, NativeFunction function, unsigned arguments)
422 {
423 Identifier identifier(&vm, name);
424 putDirect(vm, identifier, JSFunction::create(vm, this, arguments, identifier.string(), function));
425 }
426
427 void addConstructableFunction(VM& vm, const char* name, NativeFunction function, unsigned arguments)
428 {
429 Identifier identifier(&vm, name);
430 putDirect(vm, identifier, JSFunction::create(vm, this, arguments, identifier.string(), function, NoIntrinsic, function));
431 }
432};
433
434const ClassInfo GlobalObject::s_info = { "global", &JSGlobalObject::s_info, 0, ExecState::globalObjectTable, CREATE_METHOD_TABLE(GlobalObject) };
435const GlobalObjectMethodTable GlobalObject::s_globalObjectMethodTable = { &allowsAccessFrom, &supportsProfiling, &supportsRichSourceInfo, &shouldInterruptScript, &javaScriptExperimentsEnabled, 0, &shouldInterruptScriptBeforeTimeout };
436
437
438GlobalObject::GlobalObject(VM& vm, Structure* structure)
439 : JSGlobalObject(vm, structure, &s_globalObjectMethodTable)
440{
441}
442
443static inline String stringFromUTF(const char* utf8)
444{
445 // Find the the first non-ascii character, or nul.
446 const char* pos = utf8;
447 while (*pos > 0)
448 pos++;
449 size_t asciiLength = pos - utf8;
450
451 // Fast case - string is all ascii.
452 if (!*pos)
453 return String(utf8, asciiLength);
454
455 // Slow case - contains non-ascii characters, use fromUTF8WithLatin1Fallback.
456 ASSERT(*pos < 0);
457 ASSERT(strlen(utf8) == asciiLength + strlen(pos));
458 return String::fromUTF8WithLatin1Fallback(utf8, asciiLength + strlen(pos));
459}
460
461static inline SourceCode jscSource(const char* utf8, const String& filename)
462{
463 String str = stringFromUTF(utf8);
464 return makeSource(str, filename);
465}
466
467EncodedJSValue JSC_HOST_CALL functionPrint(ExecState* exec)
468{
469 for (unsigned i = 0; i < exec->argumentCount(); ++i) {
470 if (i)
471 putchar(' ');
472
473 printf("%s", exec->uncheckedArgument(i).toString(exec)->value(exec).utf8().data());
474 }
475
476 putchar('\n');
477 fflush(stdout);
478 return JSValue::encode(jsUndefined());
479}
480
481#ifndef NDEBUG
482EncodedJSValue JSC_HOST_CALL functionDumpCallFrame(ExecState* exec)
483{
484 if (!exec->callerFrame()->isVMEntrySentinel())
485 exec->vm().interpreter->dumpCallFrame(exec->callerFrame());
486 return JSValue::encode(jsUndefined());
487}
488#endif
489
490EncodedJSValue JSC_HOST_CALL functionDebug(ExecState* exec)
491{
492 fprintf(stderr, "--> %s\n", exec->argument(0).toString(exec)->value(exec).utf8().data());
493 return JSValue::encode(jsUndefined());
494}
495
496EncodedJSValue JSC_HOST_CALL functionDescribe(ExecState* exec)
497{
498 if (exec->argumentCount() < 1)
499 return JSValue::encode(jsUndefined());
500 return JSValue::encode(jsString(exec, toString(exec->argument(0))));
501}
502
503EncodedJSValue JSC_HOST_CALL functionDescribeArray(ExecState* exec)
504{
505 if (exec->argumentCount() < 1)
506 return JSValue::encode(jsUndefined());
507 JSObject* object = jsDynamicCast<JSObject*>(exec->argument(0));
508 if (!object)
509 return JSValue::encode(jsString(exec, "<not object>"));
510 return JSValue::encode(jsString(exec, toString("<Public length: ", object->getArrayLength(), "; vector length: ", object->getVectorLength(), ">")));
511}
512
513class FunctionJSCStackFunctor {
514public:
515 FunctionJSCStackFunctor(StringBuilder& trace)
516 : m_trace(trace)
517 {
518 }
519
520 StackVisitor::Status operator()(StackVisitor& visitor)
521 {
522 m_trace.append(String::format(" %zu %s\n", visitor->index(), visitor->toString().utf8().data()));
523 return StackVisitor::Continue;
524 }
525
526private:
527 StringBuilder& m_trace;
528};
529
530EncodedJSValue JSC_HOST_CALL functionJSCStack(ExecState* exec)
531{
532 StringBuilder trace;
533 trace.appendLiteral("--> Stack trace:\n");
534
535 FunctionJSCStackFunctor functor(trace);
536 exec->iterate(functor);
537 fprintf(stderr, "%s", trace.toString().utf8().data());
538 return JSValue::encode(jsUndefined());
539}
540
541EncodedJSValue JSC_HOST_CALL functionCreateRoot(ExecState* exec)
542{
543 JSLockHolder lock(exec);
544 return JSValue::encode(Root::create(exec->vm(), exec->lexicalGlobalObject()));
545}
546
547EncodedJSValue JSC_HOST_CALL functionCreateElement(ExecState* exec)
548{
549 JSLockHolder lock(exec);
550 JSValue arg = exec->argument(0);
551 return JSValue::encode(Element::create(exec->vm(), exec->lexicalGlobalObject(), arg.isNull() ? nullptr : jsCast<Root*>(exec->argument(0))));
552}
553
554EncodedJSValue JSC_HOST_CALL functionGetElement(ExecState* exec)
555{
556 JSLockHolder lock(exec);
557 Element* result = jsCast<Root*>(exec->argument(0).asCell())->element();
558 return JSValue::encode(result ? result : jsUndefined());
559}
560
561EncodedJSValue JSC_HOST_CALL functionSetElementRoot(ExecState* exec)
562{
563 JSLockHolder lock(exec);
564 Element* element = jsCast<Element*>(exec->argument(0));
565 Root* root = jsCast<Root*>(exec->argument(1));
566 element->setRoot(root);
567 return JSValue::encode(jsUndefined());
568}
569
570EncodedJSValue JSC_HOST_CALL functionGCAndSweep(ExecState* exec)
571{
572 JSLockHolder lock(exec);
573 exec->heap()->collectAllGarbage();
574 return JSValue::encode(jsUndefined());
575}
576
577EncodedJSValue JSC_HOST_CALL functionFullGC(ExecState* exec)
578{
579 JSLockHolder lock(exec);
580 exec->heap()->collect(FullCollection);
581 return JSValue::encode(jsUndefined());
582}
583
584EncodedJSValue JSC_HOST_CALL functionEdenGC(ExecState* exec)
585{
586 JSLockHolder lock(exec);
587 exec->heap()->collect(EdenCollection);
588 return JSValue::encode(jsUndefined());
589}
590
591#ifndef NDEBUG
592EncodedJSValue JSC_HOST_CALL functionReleaseExecutableMemory(ExecState* exec)
593{
594 JSLockHolder lock(exec);
595 exec->vm().releaseExecutableMemory();
596 return JSValue::encode(jsUndefined());
597}
598#endif
599
600EncodedJSValue JSC_HOST_CALL functionVersion(ExecState*)
601{
602 // We need this function for compatibility with the Mozilla JS tests but for now
603 // we don't actually do any version-specific handling
604 return JSValue::encode(jsUndefined());
605}
606
607EncodedJSValue JSC_HOST_CALL functionRun(ExecState* exec)
608{
609 String fileName = exec->argument(0).toString(exec)->value(exec);
610 Vector<char> script;
611 if (!fillBufferWithContentsOfFile(fileName, script))
612 return JSValue::encode(exec->vm().throwException(exec, createError(exec, "Could not open file.")));
613
614 GlobalObject* globalObject = GlobalObject::create(exec->vm(), GlobalObject::createStructure(exec->vm(), jsNull()), Vector<String>());
615
616 JSArray* array = constructEmptyArray(globalObject->globalExec(), 0);
617 for (unsigned i = 1; i < exec->argumentCount(); ++i)
618 array->putDirectIndex(globalObject->globalExec(), i - 1, exec->uncheckedArgument(i));
619 globalObject->putDirect(
620 exec->vm(), Identifier(globalObject->globalExec(), "arguments"), array);
621
622 JSValue exception;
623 StopWatch stopWatch;
624 stopWatch.start();
625 evaluate(globalObject->globalExec(), jscSource(script.data(), fileName), JSValue(), &exception);
626 stopWatch.stop();
627
628 if (!!exception) {
629 exec->vm().throwException(globalObject->globalExec(), exception);
630 return JSValue::encode(jsUndefined());
631 }
632
633 return JSValue::encode(jsNumber(stopWatch.getElapsedMS()));
634}
635
636EncodedJSValue JSC_HOST_CALL functionLoad(ExecState* exec)
637{
638 String fileName = exec->argument(0).toString(exec)->value(exec);
639 Vector<char> script;
640 if (!fillBufferWithContentsOfFile(fileName, script))
641 return JSValue::encode(exec->vm().throwException(exec, createError(exec, "Could not open file.")));
642
643 JSGlobalObject* globalObject = exec->lexicalGlobalObject();
644
645 JSValue evaluationException;
646 JSValue result = evaluate(globalObject->globalExec(), jscSource(script.data(), fileName), JSValue(), &evaluationException);
647 if (evaluationException)
648 exec->vm().throwException(exec, evaluationException);
649 return JSValue::encode(result);
650}
651
652EncodedJSValue JSC_HOST_CALL functionReadFile(ExecState* exec)
653{
654 String fileName = exec->argument(0).toString(exec)->value(exec);
655 Vector<char> script;
656 if (!fillBufferWithContentsOfFile(fileName, script))
657 return JSValue::encode(exec->vm().throwException(exec, createError(exec, "Could not open file.")));
658
659 return JSValue::encode(jsString(exec, stringFromUTF(script.data())));
660}
661
662EncodedJSValue JSC_HOST_CALL functionCheckSyntax(ExecState* exec)
663{
664 String fileName = exec->argument(0).toString(exec)->value(exec);
665 Vector<char> script;
666 if (!fillBufferWithContentsOfFile(fileName, script))
667 return JSValue::encode(exec->vm().throwException(exec, createError(exec, "Could not open file.")));
668
669 JSGlobalObject* globalObject = exec->lexicalGlobalObject();
670
671 StopWatch stopWatch;
672 stopWatch.start();
673
674 JSValue syntaxException;
675 bool validSyntax = checkSyntax(globalObject->globalExec(), jscSource(script.data(), fileName), &syntaxException);
676 stopWatch.stop();
677
678 if (!validSyntax)
679 exec->vm().throwException(exec, syntaxException);
680 return JSValue::encode(jsNumber(stopWatch.getElapsedMS()));
681}
682
683#if ENABLE(SAMPLING_FLAGS)
684EncodedJSValue JSC_HOST_CALL functionSetSamplingFlags(ExecState* exec)
685{
686 for (unsigned i = 0; i < exec->argumentCount(); ++i) {
687 unsigned flag = static_cast<unsigned>(exec->uncheckedArgument(i).toNumber(exec));
688 if ((flag >= 1) && (flag <= 32))
689 SamplingFlags::setFlag(flag);
690 }
691 return JSValue::encode(jsNull());
692}
693
694EncodedJSValue JSC_HOST_CALL functionClearSamplingFlags(ExecState* exec)
695{
696 for (unsigned i = 0; i < exec->argumentCount(); ++i) {
697 unsigned flag = static_cast<unsigned>(exec->uncheckedArgument(i).toNumber(exec));
698 if ((flag >= 1) && (flag <= 32))
699 SamplingFlags::clearFlag(flag);
700 }
701 return JSValue::encode(jsNull());
702}
703#endif
704
705EncodedJSValue JSC_HOST_CALL functionReadline(ExecState* exec)
706{
707 Vector<char, 256> line;
708 int c;
709 while ((c = getchar()) != EOF) {
710 // FIXME: Should we also break on \r?
711 if (c == '\n')
712 break;
713 line.append(c);
714 }
715 line.append('\0');
716 return JSValue::encode(jsString(exec, line.data()));
717}
718
719EncodedJSValue JSC_HOST_CALL functionPreciseTime(ExecState*)
720{
721 return JSValue::encode(jsNumber(currentTime()));
722}
723
724EncodedJSValue JSC_HOST_CALL functionNeverInlineFunction(ExecState* exec)
725{
726 return JSValue::encode(setNeverInline(exec));
727}
728
729EncodedJSValue JSC_HOST_CALL functionNumberOfDFGCompiles(ExecState* exec)
730{
731 return JSValue::encode(numberOfDFGCompiles(exec));
732}
733
734EncodedJSValue JSC_HOST_CALL functionReoptimizationRetryCount(ExecState* exec)
735{
736 if (exec->argumentCount() < 1)
737 return JSValue::encode(jsUndefined());
738
739 CodeBlock* block = getSomeBaselineCodeBlockForFunction(exec->argument(0));
740 if (!block)
741 return JSValue::encode(jsNumber(0));
742
743 return JSValue::encode(jsNumber(block->reoptimizationRetryCounter()));
744}
745
746EncodedJSValue JSC_HOST_CALL functionTransferArrayBuffer(ExecState* exec)
747{
748 if (exec->argumentCount() < 1)
749 return JSValue::encode(exec->vm().throwException(exec, createError(exec, "Not enough arguments")));
750
751 JSArrayBuffer* buffer = jsDynamicCast<JSArrayBuffer*>(exec->argument(0));
752 if (!buffer)
753 return JSValue::encode(exec->vm().throwException(exec, createError(exec, "Expected an array buffer")));
754
755 ArrayBufferContents dummyContents;
756 buffer->impl()->transfer(dummyContents);
757
758 return JSValue::encode(jsUndefined());
759}
760
761EncodedJSValue JSC_HOST_CALL functionQuit(ExecState*)
762{
763 exit(EXIT_SUCCESS);
764
765#if COMPILER(MSVC) && OS(WINCE)
766 // Without this, Visual Studio will complain that this method does not return a value.
767 return JSValue::encode(jsUndefined());
768#endif
769}
770
771EncodedJSValue JSC_HOST_CALL functionFalse(ExecState*)
772{
773 return JSValue::encode(jsBoolean(false));
774}
775
776EncodedJSValue JSC_HOST_CALL functionEffectful42(ExecState*)
777{
778 return JSValue::encode(jsNumber(42));
779}
780
781EncodedJSValue JSC_HOST_CALL functionMakeMasquerader(ExecState* exec)
782{
783 return JSValue::encode(Masquerader::create(exec->vm(), exec->lexicalGlobalObject()));
784}
785
786// Use SEH for Release builds only to get rid of the crash report dialog
787// (luckily the same tests fail in Release and Debug builds so far). Need to
788// be in a separate main function because the jscmain function requires object
789// unwinding.
790
791#if COMPILER(MSVC) && !defined(_DEBUG) && !OS(WINCE)
792#define TRY __try {
793#define EXCEPT(x) } __except (EXCEPTION_EXECUTE_HANDLER) { x; }
794#else
795#define TRY
796#define EXCEPT(x)
797#endif
798
799int jscmain(int argc, char** argv);
800
801static double s_desiredTimeout;
802
803static NO_RETURN_DUE_TO_CRASH void timeoutThreadMain(void*)
804{
805 auto timeout = std::chrono::microseconds(static_cast<std::chrono::microseconds::rep>(s_desiredTimeout * 1000000));
806 std::this_thread::sleep_for(timeout);
807
808 dataLog("Timed out after ", s_desiredTimeout, " seconds!\n");
809 CRASH();
810}
811
812int main(int argc, char** argv)
813{
814#if PLATFORM(IOS) && CPU(ARM_THUMB2)
815 // Enabled IEEE754 denormal support.
816 fenv_t env;
817 fegetenv( &env );
818 env.__fpscr &= ~0x01000000u;
819 fesetenv( &env );
820#endif
821
822#if OS(WINDOWS)
823#if !OS(WINCE)
824 // Cygwin calls ::SetErrorMode(SEM_FAILCRITICALERRORS), which we will inherit. This is bad for
825 // testing/debugging, as it causes the post-mortem debugger not to be invoked. We reset the
826 // error mode here to work around Cygwin's behavior. See <http://webkit.org/b/55222>.
827 ::SetErrorMode(0);
828
829#if defined(_DEBUG)
830 _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
831 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
832 _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
833 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
834 _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
835 _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
836#endif
837#endif
838
839 timeBeginPeriod(1);
840#endif
841
842#if PLATFORM(EFL)
843 ecore_init();
844#endif
845
846 // Initialize JSC before getting VM.
847#if ENABLE(SAMPLING_REGIONS)
848 WTF::initializeMainThread();
849#endif
850 JSC::initializeThreading();
851
852#if !OS(WINCE)
853 if (char* timeoutString = getenv("JSC_timeout")) {
854 if (sscanf(timeoutString, "%lf", &s_desiredTimeout) != 1) {
855 dataLog(
856 "WARNING: timeout string is malformed, got ", timeoutString,
857 " but expected a number. Not using a timeout.\n");
858 } else
859 createThread(timeoutThreadMain, 0, "jsc Timeout Thread");
860 }
861#endif
862
863#if PLATFORM(IOS)
864 Options::crashIfCantAllocateJITMemory() = true;
865#endif
866
867 // We can't use destructors in the following code because it uses Windows
868 // Structured Exception Handling
869 int res = 0;
870 TRY
871 res = jscmain(argc, argv);
872 EXCEPT(res = 3)
873 if (Options::logHeapStatisticsAtExit())
874 HeapStatistics::reportSuccess();
875
876#if PLATFORM(EFL)
877 ecore_shutdown();
878#endif
879
880 return res;
881}
882
883static bool runWithScripts(GlobalObject* globalObject, const Vector<Script>& scripts, bool dump)
884{
885 const char* script;
886 String fileName;
887 Vector<char> scriptBuffer;
888
889 if (dump)
890 JSC::Options::dumpGeneratedBytecodes() = true;
891
892 VM& vm = globalObject->vm();
893
894#if ENABLE(SAMPLING_FLAGS)
895 SamplingFlags::start();
896#endif
897
898 bool success = true;
899 for (size_t i = 0; i < scripts.size(); i++) {
900 if (scripts[i].isFile) {
901 fileName = scripts[i].argument;
902 if (!fillBufferWithContentsOfFile(fileName, scriptBuffer))
903 return false; // fail early so we can catch missing files
904 script = scriptBuffer.data();
905 } else {
906 script = scripts[i].argument;
907 fileName = "[Command Line]";
908 }
909
910 vm.startSampling();
911
912 JSValue evaluationException;
913 JSValue returnValue = evaluate(globalObject->globalExec(), jscSource(script, fileName), JSValue(), &evaluationException);
914 success = success && !evaluationException;
915 if (dump && !evaluationException)
916 printf("End: %s\n", returnValue.toString(globalObject->globalExec())->value(globalObject->globalExec()).utf8().data());
917 if (evaluationException) {
918 printf("Exception: %s\n", evaluationException.toString(globalObject->globalExec())->value(globalObject->globalExec()).utf8().data());
919 Identifier stackID(globalObject->globalExec(), "stack");
920 JSValue stackValue = evaluationException.get(globalObject->globalExec(), stackID);
921 if (!stackValue.isUndefinedOrNull())
922 printf("%s\n", stackValue.toString(globalObject->globalExec())->value(globalObject->globalExec()).utf8().data());
923 }
924
925 vm.stopSampling();
926 globalObject->globalExec()->clearException();
927 }
928
929#if ENABLE(SAMPLING_FLAGS)
930 SamplingFlags::stop();
931#endif
932#if ENABLE(SAMPLING_REGIONS)
933 SamplingRegion::dump();
934#endif
935 vm.dumpSampleData(globalObject->globalExec());
936#if ENABLE(SAMPLING_COUNTERS)
937 AbstractSamplingCounter::dump();
938#endif
939#if ENABLE(REGEXP_TRACING)
940 vm.dumpRegExpTrace();
941#endif
942 return success;
943}
944
945#define RUNNING_FROM_XCODE 0
946
947static void runInteractive(GlobalObject* globalObject)
948{
949 String interpreterName("Interpreter");
950
951 bool shouldQuit = false;
952 while (!shouldQuit) {
953#if HAVE(READLINE) && !RUNNING_FROM_XCODE
954 ParserError error;
955 String source;
956 do {
957 error = ParserError();
958 char* line = readline(source.isEmpty() ? interactivePrompt : "... ");
959 shouldQuit = !line;
960 if (!line)
961 break;
962 source = source + line;
963 source = source + '\n';
964 checkSyntax(globalObject->vm(), makeSource(source, interpreterName), error);
965 if (!line[0])
966 break;
967 add_history(line);
968 } while (error.m_syntaxErrorType == ParserError::SyntaxErrorRecoverable);
969
970 if (error.m_type != ParserError::ErrorNone) {
971 printf("%s:%d\n", error.m_message.utf8().data(), error.m_line);
972 continue;
973 }
974
975
976 JSValue evaluationException;
977 JSValue returnValue = evaluate(globalObject->globalExec(), makeSource(source, interpreterName), JSValue(), &evaluationException);
978#else
979 printf("%s", interactivePrompt);
980 Vector<char, 256> line;
981 int c;
982 while ((c = getchar()) != EOF) {
983 // FIXME: Should we also break on \r?
984 if (c == '\n')
985 break;
986 line.append(c);
987 }
988 if (line.isEmpty())
989 break;
990 line.append('\0');
991
992 JSValue evaluationException;
993 JSValue returnValue = evaluate(globalObject->globalExec(), jscSource(line.data(), interpreterName), JSValue(), &evaluationException);
994#endif
995 if (evaluationException)
996 printf("Exception: %s\n", evaluationException.toString(globalObject->globalExec())->value(globalObject->globalExec()).utf8().data());
997 else
998 printf("%s\n", returnValue.toString(globalObject->globalExec())->value(globalObject->globalExec()).utf8().data());
999
1000 globalObject->globalExec()->clearException();
1001 }
1002 printf("\n");
1003}
1004
1005static NO_RETURN void printUsageStatement(bool help = false)
1006{
1007 fprintf(stderr, "Usage: jsc [options] [files] [-- arguments]\n");
1008 fprintf(stderr, " -d Dumps bytecode (debug builds only)\n");
1009 fprintf(stderr, " -e Evaluate argument as script code\n");
1010 fprintf(stderr, " -f Specifies a source file (deprecated)\n");
1011 fprintf(stderr, " -h|--help Prints this help message\n");
1012 fprintf(stderr, " -i Enables interactive mode (default if no files are specified)\n");
1013#if HAVE(SIGNAL_H)
1014 fprintf(stderr, " -s Installs signal handlers that exit on a crash (Unix platforms only)\n");
1015#endif
1016 fprintf(stderr, " -p <file> Outputs profiling data to a file\n");
1017 fprintf(stderr, " -x Output exit code before terminating\n");
1018 fprintf(stderr, "\n");
1019 fprintf(stderr, " --options Dumps all JSC VM options and exits\n");
1020 fprintf(stderr, " --dumpOptions Dumps all JSC VM options before continuing\n");
1021 fprintf(stderr, " --<jsc VM option>=<value> Sets the specified JSC VM option\n");
1022 fprintf(stderr, "\n");
1023
1024 exit(help ? EXIT_SUCCESS : EXIT_FAILURE);
1025}
1026
1027void CommandLine::parseArguments(int argc, char** argv)
1028{
1029 int i = 1;
1030 bool needToDumpOptions = false;
1031 bool needToExit = false;
1032
1033 for (; i < argc; ++i) {
1034 const char* arg = argv[i];
1035 if (!strcmp(arg, "-f")) {
1036 if (++i == argc)
1037 printUsageStatement();
1038 m_scripts.append(Script(true, argv[i]));
1039 continue;
1040 }
1041 if (!strcmp(arg, "-e")) {
1042 if (++i == argc)
1043 printUsageStatement();
1044 m_scripts.append(Script(false, argv[i]));
1045 continue;
1046 }
1047 if (!strcmp(arg, "-i")) {
1048 m_interactive = true;
1049 continue;
1050 }
1051 if (!strcmp(arg, "-d")) {
1052 m_dump = true;
1053 continue;
1054 }
1055 if (!strcmp(arg, "-p")) {
1056 if (++i == argc)
1057 printUsageStatement();
1058 m_profile = true;
1059 m_profilerOutput = argv[i];
1060 continue;
1061 }
1062 if (!strcmp(arg, "-s")) {
1063#if HAVE(SIGNAL_H)
1064 signal(SIGILL, _exit);
1065 signal(SIGFPE, _exit);
1066 signal(SIGBUS, _exit);
1067 signal(SIGSEGV, _exit);
1068#endif
1069 continue;
1070 }
1071 if (!strcmp(arg, "-x")) {
1072 m_exitCode = true;
1073 continue;
1074 }
1075 if (!strcmp(arg, "--")) {
1076 ++i;
1077 break;
1078 }
1079 if (!strcmp(arg, "-h") || !strcmp(arg, "--help"))
1080 printUsageStatement(true);
1081
1082 if (!strcmp(arg, "--options")) {
1083 needToDumpOptions = true;
1084 needToExit = true;
1085 continue;
1086 }
1087 if (!strcmp(arg, "--dumpOptions")) {
1088 needToDumpOptions = true;
1089 continue;
1090 }
1091
1092 // See if the -- option is a JSC VM option.
1093 // NOTE: At this point, we know that the arg starts with "--". Skip it.
1094 if (JSC::Options::setOption(&arg[2])) {
1095 // The arg was recognized as a VM option and has been parsed.
1096 continue; // Just continue with the next arg.
1097 }
1098
1099 // This arg is not recognized by the VM nor by jsc. Pass it on to the
1100 // script.
1101 m_scripts.append(Script(true, argv[i]));
1102 }
1103
1104 if (m_scripts.isEmpty())
1105 m_interactive = true;
1106
1107 for (; i < argc; ++i)
1108 m_arguments.append(argv[i]);
1109
1110 if (needToDumpOptions)
1111 JSC::Options::dumpAllOptions(stderr);
1112 if (needToExit)
1113 exit(EXIT_SUCCESS);
1114}
1115
1116int jscmain(int argc, char** argv)
1117{
1118 // Note that the options parsing can affect VM creation, and thus
1119 // comes first.
1120 CommandLine options(argc, argv);
1121 VM* vm = VM::create(LargeHeap).leakRef();
1122 int result;
1123 {
1124 JSLockHolder locker(vm);
1125
1126 if (options.m_profile && !vm->m_perBytecodeProfiler)
1127 vm->m_perBytecodeProfiler = adoptPtr(new Profiler::Database(*vm));
1128
1129 GlobalObject* globalObject = GlobalObject::create(*vm, GlobalObject::createStructure(*vm, jsNull()), options.m_arguments);
1130 bool success = runWithScripts(globalObject, options.m_scripts, options.m_dump);
1131 if (options.m_interactive && success)
1132 runInteractive(globalObject);
1133
1134 result = success ? 0 : 3;
1135
1136 if (options.m_exitCode)
1137 printf("jsc exiting %d\n", result);
1138
1139 if (options.m_profile) {
1140 if (!vm->m_perBytecodeProfiler->save(options.m_profilerOutput.utf8().data()))
1141 fprintf(stderr, "could not save profiler output.\n");
1142 }
1143 }
1144
1145 return result;
1146}
1147
1148static bool fillBufferWithContentsOfFile(const String& fileName, Vector<char>& buffer)
1149{
1150 FILE* f = fopen(fileName.utf8().data(), "r");
1151 if (!f) {
1152 fprintf(stderr, "Could not open file: %s\n", fileName.utf8().data());
1153 return false;
1154 }
1155
1156 size_t bufferSize = 0;
1157 size_t bufferCapacity = 1024;
1158
1159 buffer.resize(bufferCapacity);
1160
1161 while (!feof(f) && !ferror(f)) {
1162 bufferSize += fread(buffer.data() + bufferSize, 1, bufferCapacity - bufferSize, f);
1163 if (bufferSize == bufferCapacity) { // guarantees space for trailing '\0'
1164 bufferCapacity *= 2;
1165 buffer.resize(bufferCapacity);
1166 }
1167 }
1168 fclose(f);
1169 buffer[bufferSize] = '\0';
1170
1171 if (buffer[0] == '#' && buffer[1] == '!')
1172 buffer[0] = buffer[1] = '/';
1173
1174 return true;
1175}
Note: See TracBrowser for help on using the repository browser.