1/*
2 * Copyright (C) 1999-2000 Harri Porten ([email protected])
3 * Copyright (C) 2004-2018 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 "ArrayBuffer.h"
26#include "ArrayPrototype.h"
27#include "BuiltinNames.h"
28#include "ButterflyInlines.h"
29#include "BytecodeCacheError.h"
30#include "CatchScope.h"
31#include "CodeBlock.h"
32#include "CodeCache.h"
33#include "Completion.h"
34#include "ConfigFile.h"
35#include "Disassembler.h"
36#include "Exception.h"
37#include "ExceptionHelpers.h"
38#include "HeapProfiler.h"
39#include "HeapSnapshotBuilder.h"
40#include "InitializeThreading.h"
41#include "Interpreter.h"
42#include "JIT.h"
43#include "JSArray.h"
44#include "JSArrayBuffer.h"
45#include "JSBigInt.h"
46#include "JSCInlines.h"
47#include "JSFunction.h"
48#include "JSInternalPromise.h"
49#include "JSInternalPromiseDeferred.h"
50#include "JSLock.h"
51#include "JSModuleLoader.h"
52#include "JSNativeStdFunction.h"
53#include "JSONObject.h"
54#include "JSSourceCode.h"
55#include "JSString.h"
56#include "JSTypedArrays.h"
57#include "JSWebAssemblyInstance.h"
58#include "JSWebAssemblyMemory.h"
59#include "LLIntThunks.h"
60#include "ObjectConstructor.h"
61#include "ParserError.h"
62#include "ProfilerDatabase.h"
63#include "PromiseDeferredTimer.h"
64#include "ProtoCallFrame.h"
65#include "ReleaseHeapAccessScope.h"
66#include "SamplingProfiler.h"
67#include "StackVisitor.h"
68#include "StructureInlines.h"
69#include "StructureRareDataInlines.h"
70#include "SuperSampler.h"
71#include "TestRunnerUtils.h"
72#include "TypedArrayInlines.h"
73#include "WasmCapabilities.h"
74#include "WasmContext.h"
75#include "WasmFaultSignalHandler.h"
76#include "WasmMemory.h"
77#include <locale.h>
78#include <math.h>
79#include <stdio.h>
80#include <stdlib.h>
81#include <string.h>
82#include <sys/stat.h>
83#include <sys/types.h>
84#include <thread>
85#include <type_traits>
86#include <wtf/Box.h>
87#include <wtf/CommaPrinter.h>
88#include <wtf/MainThread.h>
89#include <wtf/MemoryPressureHandler.h>
90#include <wtf/MonotonicTime.h>
91#include <wtf/NeverDestroyed.h>
92#include <wtf/Scope.h>
93#include <wtf/StringPrintStream.h>
94#include <wtf/URL.h>
95#include <wtf/WallTime.h>
96#include <wtf/text/StringBuilder.h>
97#include <wtf/text/StringConcatenateNumbers.h>
98
99#if OS(WINDOWS)
100#include <direct.h>
101#include <fcntl.h>
102#include <io.h>
103#else
104#include <unistd.h>
105#endif
106
107#if PLATFORM(COCOA)
108#include <crt_externs.h>
109#endif
110
111#if HAVE(READLINE)
112// readline/history.h has a Function typedef which conflicts with the WTF::Function template from WTF/Forward.h
113// We #define it to something else to avoid this conflict.
114#define Function ReadlineFunction
115#include <readline/history.h>
116#include <readline/readline.h>
117#undef Function
118#endif
119
120#if HAVE(SYS_TIME_H)
121#include <sys/time.h>
122#endif
123
124#if HAVE(SIGNAL_H)
125#include <signal.h>
126#endif
127
128#if COMPILER(MSVC)
129#include <crtdbg.h>
130#include <mmsystem.h>
131#include <windows.h>
132#endif
133
134#if PLATFORM(IOS_FAMILY) && CPU(ARM_THUMB2)
135#include <fenv.h>
136#include <arm/arch.h>
137#endif
138
139#if __has_include(<WebKitAdditions/MemoryFootprint.h>)
140#include <WebKitAdditions/MemoryFootprint.h>
141#else
142struct MemoryFootprint {
143 uint64_t current;
144 uint64_t peak;
145
146 static MemoryFootprint now()
147 {
148 return { 0L, 0L };
149 }
150
151 static void resetPeak()
152 {
153 }
154};
155#endif
156
157#if !defined(PATH_MAX)
158#define PATH_MAX 4096
159#endif
160
161using namespace JSC;
162
163namespace {
164
165NO_RETURN_WITH_VALUE static void jscExit(int status)
166{
167 waitForAsynchronousDisassembly();
168
169#if ENABLE(DFG_JIT)
170 if (DFG::isCrashing()) {
171 for (;;) {
172#if OS(WINDOWS)
173 Sleep(1000);
174#else
175 pause();
176#endif
177 }
178 }
179#endif // ENABLE(DFG_JIT)
180 exit(status);
181}
182
183class Masquerader : public JSNonFinalObject {
184public:
185 Masquerader(VM& vm, Structure* structure)
186 : Base(vm, structure)
187 {
188 }
189
190 typedef JSNonFinalObject Base;
191 static const unsigned StructureFlags = Base::StructureFlags | JSC::MasqueradesAsUndefined;
192
193 static Masquerader* create(VM& vm, JSGlobalObject* globalObject)
194 {
195 globalObject->masqueradesAsUndefinedWatchpoint()->fireAll(vm, "Masquerading object allocated");
196 Structure* structure = createStructure(vm, globalObject, jsNull());
197 Masquerader* result = new (NotNull, allocateCell<Masquerader>(vm.heap, sizeof(Masquerader))) Masquerader(vm, structure);
198 result->finishCreation(vm);
199 return result;
200 }
201
202 static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
203 {
204 return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
205 }
206
207 DECLARE_INFO;
208};
209
210const ClassInfo Masquerader::s_info = { "Masquerader", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(Masquerader) };
211static unsigned asyncTestPasses { 0 };
212static unsigned asyncTestExpectedPasses { 0 };
213
214}
215
216template<typename Vector>
217static bool fillBufferWithContentsOfFile(const String& fileName, Vector& buffer);
218static RefPtr<Uint8Array> fillBufferWithContentsOfFile(const String& fileName);
219
220class CommandLine;
221class GlobalObject;
222class Workers;
223
224template<typename Func>
225int runJSC(const CommandLine&, bool isWorker, const Func&);
226static void checkException(ExecState*, GlobalObject*, bool isLastFile, bool hasException, JSValue, CommandLine&, bool& success);
227
228class Message : public ThreadSafeRefCounted<Message> {
229public:
230 Message(ArrayBufferContents&&, int32_t);
231 ~Message();
232
233 ArrayBufferContents&& releaseContents() { return WTFMove(m_contents); }
234 int32_t index() const { return m_index; }
235
236private:
237 ArrayBufferContents m_contents;
238 int32_t m_index { 0 };
239};
240
241class Worker : public BasicRawSentinelNode<Worker> {
242public:
243 Worker(Workers&);
244 ~Worker();
245
246 void enqueue(const AbstractLocker&, RefPtr<Message>);
247 RefPtr<Message> dequeue();
248
249 static Worker& current();
250
251private:
252 static ThreadSpecific<Worker*>& currentWorker();
253
254 Workers& m_workers;
255 Deque<RefPtr<Message>> m_messages;
256};
257
258class Workers {
259 WTF_MAKE_FAST_ALLOCATED;
260 WTF_MAKE_NONCOPYABLE(Workers);
261public:
262 Workers();
263 ~Workers();
264
265 template<typename Func>
266 void broadcast(const Func&);
267
268 void report(const String&);
269 String tryGetReport();
270 String getReport();
271
272 static Workers& singleton();
273
274private:
275 friend class Worker;
276
277 Lock m_lock;
278 Condition m_condition;
279 SentinelLinkedList<Worker, BasicRawSentinelNode<Worker>> m_workers;
280 Deque<String> m_reports;
281};
282
283
284static EncodedJSValue JSC_HOST_CALL functionCreateGlobalObject(ExecState*);
285
286static EncodedJSValue JSC_HOST_CALL functionPrintStdOut(ExecState*);
287static EncodedJSValue JSC_HOST_CALL functionPrintStdErr(ExecState*);
288static EncodedJSValue JSC_HOST_CALL functionDebug(ExecState*);
289static EncodedJSValue JSC_HOST_CALL functionDescribe(ExecState*);
290static EncodedJSValue JSC_HOST_CALL functionDescribeArray(ExecState*);
291static EncodedJSValue JSC_HOST_CALL functionSleepSeconds(ExecState*);
292static EncodedJSValue JSC_HOST_CALL functionJSCStack(ExecState*);
293static EncodedJSValue JSC_HOST_CALL functionGCAndSweep(ExecState*);
294static EncodedJSValue JSC_HOST_CALL functionFullGC(ExecState*);
295static EncodedJSValue JSC_HOST_CALL functionEdenGC(ExecState*);
296static EncodedJSValue JSC_HOST_CALL functionForceGCSlowPaths(ExecState*);
297static EncodedJSValue JSC_HOST_CALL functionHeapSize(ExecState*);
298static EncodedJSValue JSC_HOST_CALL functionCreateMemoryFootprint(ExecState*);
299static EncodedJSValue JSC_HOST_CALL functionResetMemoryPeak(ExecState*);
300static EncodedJSValue JSC_HOST_CALL functionAddressOf(ExecState*);
301static EncodedJSValue JSC_HOST_CALL functionVersion(ExecState*);
302static EncodedJSValue JSC_HOST_CALL functionRun(ExecState*);
303static EncodedJSValue JSC_HOST_CALL functionRunString(ExecState*);
304static EncodedJSValue JSC_HOST_CALL functionLoad(ExecState*);
305static EncodedJSValue JSC_HOST_CALL functionLoadString(ExecState*);
306static EncodedJSValue JSC_HOST_CALL functionReadFile(ExecState*);
307static EncodedJSValue JSC_HOST_CALL functionCheckSyntax(ExecState*);
308static EncodedJSValue JSC_HOST_CALL functionReadline(ExecState*);
309static EncodedJSValue JSC_HOST_CALL functionPreciseTime(ExecState*);
310static EncodedJSValue JSC_HOST_CALL functionNeverInlineFunction(ExecState*);
311static EncodedJSValue JSC_HOST_CALL functionNoDFG(ExecState*);
312static EncodedJSValue JSC_HOST_CALL functionNoFTL(ExecState*);
313static EncodedJSValue JSC_HOST_CALL functionNoOSRExitFuzzing(ExecState*);
314static EncodedJSValue JSC_HOST_CALL functionOptimizeNextInvocation(ExecState*);
315static EncodedJSValue JSC_HOST_CALL functionNumberOfDFGCompiles(ExecState*);
316static EncodedJSValue JSC_HOST_CALL functionJSCOptions(ExecState*);
317static EncodedJSValue JSC_HOST_CALL functionReoptimizationRetryCount(ExecState*);
318static EncodedJSValue JSC_HOST_CALL functionTransferArrayBuffer(ExecState*);
319static EncodedJSValue JSC_HOST_CALL functionFailNextNewCodeBlock(ExecState*);
320static NO_RETURN_WITH_VALUE EncodedJSValue JSC_HOST_CALL functionQuit(ExecState*);
321static EncodedJSValue JSC_HOST_CALL functionFalse(ExecState*);
322static EncodedJSValue JSC_HOST_CALL functionUndefined1(ExecState*);
323static EncodedJSValue JSC_HOST_CALL functionUndefined2(ExecState*);
324static EncodedJSValue JSC_HOST_CALL functionIsInt32(ExecState*);
325static EncodedJSValue JSC_HOST_CALL functionIsPureNaN(ExecState*);
326static EncodedJSValue JSC_HOST_CALL functionEffectful42(ExecState*);
327static EncodedJSValue JSC_HOST_CALL functionIdentity(ExecState*);
328static EncodedJSValue JSC_HOST_CALL functionMakeMasquerader(ExecState*);
329static EncodedJSValue JSC_HOST_CALL functionHasCustomProperties(ExecState*);
330static EncodedJSValue JSC_HOST_CALL functionDumpTypesForAllVariables(ExecState*);
331static EncodedJSValue JSC_HOST_CALL functionDrainMicrotasks(ExecState*);
332static EncodedJSValue JSC_HOST_CALL functionReleaseWeakRefs(ExecState*);
333static EncodedJSValue JSC_HOST_CALL functionIs32BitPlatform(ExecState*);
334static EncodedJSValue JSC_HOST_CALL functionCheckModuleSyntax(ExecState*);
335static EncodedJSValue JSC_HOST_CALL functionPlatformSupportsSamplingProfiler(ExecState*);
336static EncodedJSValue JSC_HOST_CALL functionGenerateHeapSnapshot(ExecState*);
337static EncodedJSValue JSC_HOST_CALL functionGenerateHeapSnapshotForGCDebugging(ExecState*);
338static EncodedJSValue JSC_HOST_CALL functionResetSuperSamplerState(ExecState*);
339static EncodedJSValue JSC_HOST_CALL functionEnsureArrayStorage(ExecState*);
340#if ENABLE(SAMPLING_PROFILER)
341static EncodedJSValue JSC_HOST_CALL functionStartSamplingProfiler(ExecState*);
342static EncodedJSValue JSC_HOST_CALL functionSamplingProfilerStackTraces(ExecState*);
343#endif
344
345static EncodedJSValue JSC_HOST_CALL functionMaxArguments(ExecState*);
346static EncodedJSValue JSC_HOST_CALL functionAsyncTestStart(ExecState*);
347static EncodedJSValue JSC_HOST_CALL functionAsyncTestPassed(ExecState*);
348
349#if ENABLE(WEBASSEMBLY)
350static EncodedJSValue JSC_HOST_CALL functionWebAssemblyMemoryMode(ExecState*);
351#endif
352
353#if ENABLE(SAMPLING_FLAGS)
354static EncodedJSValue JSC_HOST_CALL functionSetSamplingFlags(ExecState*);
355static EncodedJSValue JSC_HOST_CALL functionClearSamplingFlags(ExecState*);
356#endif
357
358static EncodedJSValue JSC_HOST_CALL functionGetRandomSeed(ExecState*);
359static EncodedJSValue JSC_HOST_CALL functionSetRandomSeed(ExecState*);
360static EncodedJSValue JSC_HOST_CALL functionIsRope(ExecState*);
361static EncodedJSValue JSC_HOST_CALL functionCallerSourceOrigin(ExecState*);
362static EncodedJSValue JSC_HOST_CALL functionDollarCreateRealm(ExecState*);
363static EncodedJSValue JSC_HOST_CALL functionDollarDetachArrayBuffer(ExecState*);
364static EncodedJSValue JSC_HOST_CALL functionDollarEvalScript(ExecState*);
365static EncodedJSValue JSC_HOST_CALL functionDollarAgentStart(ExecState*);
366static EncodedJSValue JSC_HOST_CALL functionDollarAgentReceiveBroadcast(ExecState*);
367static EncodedJSValue JSC_HOST_CALL functionDollarAgentReport(ExecState*);
368static EncodedJSValue JSC_HOST_CALL functionDollarAgentSleep(ExecState*);
369static EncodedJSValue JSC_HOST_CALL functionDollarAgentBroadcast(ExecState*);
370static EncodedJSValue JSC_HOST_CALL functionDollarAgentGetReport(ExecState*);
371static EncodedJSValue JSC_HOST_CALL functionDollarAgentLeaving(ExecState*);
372static EncodedJSValue JSC_HOST_CALL functionDollarAgentMonotonicNow(ExecState*);
373static EncodedJSValue JSC_HOST_CALL functionWaitForReport(ExecState*);
374static EncodedJSValue JSC_HOST_CALL functionHeapCapacity(ExecState*);
375static EncodedJSValue JSC_HOST_CALL functionFlashHeapAccess(ExecState*);
376static EncodedJSValue JSC_HOST_CALL functionDisableRichSourceInfo(ExecState*);
377static EncodedJSValue JSC_HOST_CALL functionMallocInALoop(ExecState*);
378static EncodedJSValue JSC_HOST_CALL functionTotalCompileTime(ExecState*);
379
380struct Script {
381 enum class StrictMode {
382 Strict,
383 Sloppy
384 };
385
386 enum class ScriptType {
387 Script,
388 Module
389 };
390
391 enum class CodeSource {
392 File,
393 CommandLine
394 };
395
396 StrictMode strictMode;
397 CodeSource codeSource;
398 ScriptType scriptType;
399 char* argument;
400
401 Script(StrictMode strictMode, CodeSource codeSource, ScriptType scriptType, char *argument)
402 : strictMode(strictMode)
403 , codeSource(codeSource)
404 , scriptType(scriptType)
405 , argument(argument)
406 {
407 if (strictMode == StrictMode::Strict)
408 ASSERT(codeSource == CodeSource::File);
409 }
410};
411
412class CommandLine {
413public:
414 CommandLine(int argc, char** argv)
415 {
416 parseArguments(argc, argv);
417 }
418
419 Vector<Script> m_scripts;
420 Vector<String> m_arguments;
421 String m_profilerOutput;
422 String m_uncaughtExceptionName;
423 bool m_interactive { false };
424 bool m_dump { false };
425 bool m_module { false };
426 bool m_exitCode { false };
427 bool m_destroyVM { false };
428 bool m_profile { false };
429 bool m_treatWatchdogExceptionAsSuccess { false };
430 bool m_alwaysDumpUncaughtException { false };
431 bool m_dumpMemoryFootprint { false };
432 bool m_dumpSamplingProfilerData { false };
433 bool m_enableRemoteDebugging { false };
434
435 void parseArguments(int, char**);
436};
437
438static const char interactivePrompt[] = ">>> ";
439
440class StopWatch {
441public:
442 void start();
443 void stop();
444 long getElapsedMS(); // call stop() first
445
446private:
447 MonotonicTime m_startTime;
448 MonotonicTime m_stopTime;
449};
450
451void StopWatch::start()
452{
453 m_startTime = MonotonicTime::now();
454}
455
456void StopWatch::stop()
457{
458 m_stopTime = MonotonicTime::now();
459}
460
461long StopWatch::getElapsedMS()
462{
463 return (m_stopTime - m_startTime).millisecondsAs<long>();
464}
465
466template<typename Vector>
467static inline String stringFromUTF(const Vector& utf8)
468{
469 return String::fromUTF8WithLatin1Fallback(utf8.data(), utf8.size());
470}
471
472class GlobalObject : public JSGlobalObject {
473private:
474 GlobalObject(VM&, Structure*);
475
476public:
477 typedef JSGlobalObject Base;
478
479 static GlobalObject* create(VM& vm, Structure* structure, const Vector<String>& arguments)
480 {
481 GlobalObject* object = new (NotNull, allocateCell<GlobalObject>(vm.heap)) GlobalObject(vm, structure);
482 object->finishCreation(vm, arguments);
483 return object;
484 }
485
486 static const bool needsDestruction = false;
487
488 DECLARE_INFO;
489 static const GlobalObjectMethodTable s_globalObjectMethodTable;
490
491 static Structure* createStructure(VM& vm, JSValue prototype)
492 {
493 return Structure::create(vm, 0, prototype, TypeInfo(GlobalObjectType, StructureFlags), info());
494 }
495
496 static RuntimeFlags javaScriptRuntimeFlags(const JSGlobalObject*) { return RuntimeFlags::createAllEnabled(); }
497
498protected:
499 void finishCreation(VM& vm, const Vector<String>& arguments)
500 {
501 Base::finishCreation(vm);
502
503 addFunction(vm, "debug", functionDebug, 1);
504 addFunction(vm, "describe", functionDescribe, 1);
505 addFunction(vm, "describeArray", functionDescribeArray, 1);
506 addFunction(vm, "print", functionPrintStdOut, 1);
507 addFunction(vm, "printErr", functionPrintStdErr, 1);
508 addFunction(vm, "quit", functionQuit, 0);
509 addFunction(vm, "gc", functionGCAndSweep, 0);
510 addFunction(vm, "fullGC", functionFullGC, 0);
511 addFunction(vm, "edenGC", functionEdenGC, 0);
512 addFunction(vm, "forceGCSlowPaths", functionForceGCSlowPaths, 0);
513 addFunction(vm, "gcHeapSize", functionHeapSize, 0);
514 addFunction(vm, "MemoryFootprint", functionCreateMemoryFootprint, 0);
515 addFunction(vm, "resetMemoryPeak", functionResetMemoryPeak, 0);
516 addFunction(vm, "addressOf", functionAddressOf, 1);
517 addFunction(vm, "version", functionVersion, 1);
518 addFunction(vm, "run", functionRun, 1);
519 addFunction(vm, "runString", functionRunString, 1);
520 addFunction(vm, "load", functionLoad, 1);
521 addFunction(vm, "loadString", functionLoadString, 1);
522 addFunction(vm, "readFile", functionReadFile, 2);
523 addFunction(vm, "read", functionReadFile, 2);
524 addFunction(vm, "checkSyntax", functionCheckSyntax, 1);
525 addFunction(vm, "sleepSeconds", functionSleepSeconds, 1);
526 addFunction(vm, "jscStack", functionJSCStack, 1);
527 addFunction(vm, "readline", functionReadline, 0);
528 addFunction(vm, "preciseTime", functionPreciseTime, 0);
529 addFunction(vm, "neverInlineFunction", functionNeverInlineFunction, 1);
530 addFunction(vm, "noInline", functionNeverInlineFunction, 1);
531 addFunction(vm, "noDFG", functionNoDFG, 1);
532 addFunction(vm, "noFTL", functionNoFTL, 1);
533 addFunction(vm, "noOSRExitFuzzing", functionNoOSRExitFuzzing, 1);
534 addFunction(vm, "numberOfDFGCompiles", functionNumberOfDFGCompiles, 1);
535 addFunction(vm, "jscOptions", functionJSCOptions, 0);
536 addFunction(vm, "optimizeNextInvocation", functionOptimizeNextInvocation, 1);
537 addFunction(vm, "reoptimizationRetryCount", functionReoptimizationRetryCount, 1);
538 addFunction(vm, "transferArrayBuffer", functionTransferArrayBuffer, 1);
539 addFunction(vm, "failNextNewCodeBlock", functionFailNextNewCodeBlock, 1);
540#if ENABLE(SAMPLING_FLAGS)
541 addFunction(vm, "setSamplingFlags", functionSetSamplingFlags, 1);
542 addFunction(vm, "clearSamplingFlags", functionClearSamplingFlags, 1);
543#endif
544
545 putDirectNativeFunction(vm, this, Identifier::fromString(&vm, "OSRExit"), 0, functionUndefined1, OSRExitIntrinsic, static_cast<unsigned>(PropertyAttribute::DontEnum));
546 putDirectNativeFunction(vm, this, Identifier::fromString(&vm, "isFinalTier"), 0, functionFalse, IsFinalTierIntrinsic, static_cast<unsigned>(PropertyAttribute::DontEnum));
547 putDirectNativeFunction(vm, this, Identifier::fromString(&vm, "predictInt32"), 0, functionUndefined2, SetInt32HeapPredictionIntrinsic, static_cast<unsigned>(PropertyAttribute::DontEnum));
548 putDirectNativeFunction(vm, this, Identifier::fromString(&vm, "isInt32"), 0, functionIsInt32, CheckInt32Intrinsic, static_cast<unsigned>(PropertyAttribute::DontEnum));
549 putDirectNativeFunction(vm, this, Identifier::fromString(&vm, "isPureNaN"), 0, functionIsPureNaN, CheckInt32Intrinsic, static_cast<unsigned>(PropertyAttribute::DontEnum));
550 putDirectNativeFunction(vm, this, Identifier::fromString(&vm, "fiatInt52"), 0, functionIdentity, FiatInt52Intrinsic, static_cast<unsigned>(PropertyAttribute::DontEnum));
551
552 addFunction(vm, "effectful42", functionEffectful42, 0);
553 addFunction(vm, "makeMasquerader", functionMakeMasquerader, 0);
554 addFunction(vm, "hasCustomProperties", functionHasCustomProperties, 0);
555
556 addFunction(vm, "createGlobalObject", functionCreateGlobalObject, 0);
557
558 addFunction(vm, "dumpTypesForAllVariables", functionDumpTypesForAllVariables , 0);
559
560 addFunction(vm, "drainMicrotasks", functionDrainMicrotasks, 0);
561 addFunction(vm, "releaseWeakRefs", functionReleaseWeakRefs, 0);
562
563 addFunction(vm, "getRandomSeed", functionGetRandomSeed, 0);
564 addFunction(vm, "setRandomSeed", functionSetRandomSeed, 1);
565 addFunction(vm, "isRope", functionIsRope, 1);
566 addFunction(vm, "callerSourceOrigin", functionCallerSourceOrigin, 0);
567
568 addFunction(vm, "is32BitPlatform", functionIs32BitPlatform, 0);
569
570 addFunction(vm, "checkModuleSyntax", functionCheckModuleSyntax, 1);
571
572 addFunction(vm, "platformSupportsSamplingProfiler", functionPlatformSupportsSamplingProfiler, 0);
573 addFunction(vm, "generateHeapSnapshot", functionGenerateHeapSnapshot, 0);
574 addFunction(vm, "generateHeapSnapshotForGCDebugging", functionGenerateHeapSnapshotForGCDebugging, 0);
575 addFunction(vm, "resetSuperSamplerState", functionResetSuperSamplerState, 0);
576 addFunction(vm, "ensureArrayStorage", functionEnsureArrayStorage, 0);
577#if ENABLE(SAMPLING_PROFILER)
578 addFunction(vm, "startSamplingProfiler", functionStartSamplingProfiler, 0);
579 addFunction(vm, "samplingProfilerStackTraces", functionSamplingProfilerStackTraces, 0);
580#endif
581
582 addFunction(vm, "maxArguments", functionMaxArguments, 0);
583
584 addFunction(vm, "asyncTestStart", functionAsyncTestStart, 1);
585 addFunction(vm, "asyncTestPassed", functionAsyncTestPassed, 1);
586
587#if ENABLE(WEBASSEMBLY)
588 addFunction(vm, "WebAssemblyMemoryMode", functionWebAssemblyMemoryMode, 1);
589#endif
590
591 if (!arguments.isEmpty()) {
592 JSArray* array = constructEmptyArray(globalExec(), 0);
593 for (size_t i = 0; i < arguments.size(); ++i)
594 array->putDirectIndex(globalExec(), i, jsString(globalExec(), arguments[i]));
595 putDirect(vm, Identifier::fromString(globalExec(), "arguments"), array);
596 }
597
598 putDirect(vm, Identifier::fromString(globalExec(), "console"), jsUndefined());
599
600 Structure* plainObjectStructure = JSFinalObject::createStructure(vm, this, objectPrototype(), 0);
601
602 JSObject* dollar = JSFinalObject::create(vm, plainObjectStructure);
603 putDirect(vm, Identifier::fromString(globalExec(), "$"), dollar);
604 putDirect(vm, Identifier::fromString(globalExec(), "$262"), dollar);
605
606 addFunction(vm, dollar, "createRealm", functionDollarCreateRealm, 0);
607 addFunction(vm, dollar, "detachArrayBuffer", functionDollarDetachArrayBuffer, 1);
608 addFunction(vm, dollar, "evalScript", functionDollarEvalScript, 1);
609
610 dollar->putDirect(vm, Identifier::fromString(globalExec(), "global"), this);
611
612 JSObject* agent = JSFinalObject::create(vm, plainObjectStructure);
613 dollar->putDirect(vm, Identifier::fromString(globalExec(), "agent"), agent);
614
615 // The test262 INTERPRETING.md document says that some of these functions are just in the main
616 // thread and some are in the other threads. We just put them in all threads.
617 addFunction(vm, agent, "start", functionDollarAgentStart, 1);
618 addFunction(vm, agent, "receiveBroadcast", functionDollarAgentReceiveBroadcast, 1);
619 addFunction(vm, agent, "report", functionDollarAgentReport, 1);
620 addFunction(vm, agent, "sleep", functionDollarAgentSleep, 1);
621 addFunction(vm, agent, "broadcast", functionDollarAgentBroadcast, 1);
622 addFunction(vm, agent, "getReport", functionDollarAgentGetReport, 0);
623 addFunction(vm, agent, "leaving", functionDollarAgentLeaving, 0);
624 addFunction(vm, agent, "monotonicNow", functionDollarAgentMonotonicNow, 0);
625
626 addFunction(vm, "waitForReport", functionWaitForReport, 0);
627
628 addFunction(vm, "heapCapacity", functionHeapCapacity, 0);
629 addFunction(vm, "flashHeapAccess", functionFlashHeapAccess, 0);
630
631 addFunction(vm, "disableRichSourceInfo", functionDisableRichSourceInfo, 0);
632 addFunction(vm, "mallocInALoop", functionMallocInALoop, 0);
633 addFunction(vm, "totalCompileTime", functionTotalCompileTime, 0);
634 }
635
636 void addFunction(VM& vm, JSObject* object, const char* name, NativeFunction function, unsigned arguments)
637 {
638 Identifier identifier = Identifier::fromString(&vm, name);
639 object->putDirect(vm, identifier, JSFunction::create(vm, this, arguments, identifier.string(), function));
640 }
641
642 void addFunction(VM& vm, const char* name, NativeFunction function, unsigned arguments)
643 {
644 addFunction(vm, this, name, function, arguments);
645 }
646
647 static JSInternalPromise* moduleLoaderImportModule(JSGlobalObject*, ExecState*, JSModuleLoader*, JSString*, JSValue, const SourceOrigin&);
648 static Identifier moduleLoaderResolve(JSGlobalObject*, ExecState*, JSModuleLoader*, JSValue, JSValue, JSValue);
649 static JSInternalPromise* moduleLoaderFetch(JSGlobalObject*, ExecState*, JSModuleLoader*, JSValue, JSValue, JSValue);
650 static JSObject* moduleLoaderCreateImportMetaProperties(JSGlobalObject*, ExecState*, JSModuleLoader*, JSValue, JSModuleRecord*, JSValue);
651};
652
653static bool supportsRichSourceInfo = true;
654static bool shellSupportsRichSourceInfo(const JSGlobalObject*)
655{
656 return supportsRichSourceInfo;
657}
658
659const ClassInfo GlobalObject::s_info = { "global", &JSGlobalObject::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(GlobalObject) };
660const GlobalObjectMethodTable GlobalObject::s_globalObjectMethodTable = {
661 &shellSupportsRichSourceInfo,
662 &shouldInterruptScript,
663 &javaScriptRuntimeFlags,
664 nullptr, // queueTaskToEventLoop
665 &shouldInterruptScriptBeforeTimeout,
666 &moduleLoaderImportModule,
667 &moduleLoaderResolve,
668 &moduleLoaderFetch,
669 &moduleLoaderCreateImportMetaProperties,
670 nullptr, // moduleLoaderEvaluate
671 nullptr, // promiseRejectionTracker
672 nullptr, // defaultLanguage
673 nullptr, // compileStreaming
674 nullptr, // instantinateStreaming
675};
676
677GlobalObject::GlobalObject(VM& vm, Structure* structure)
678 : JSGlobalObject(vm, structure, &s_globalObjectMethodTable)
679{
680}
681
682static UChar pathSeparator()
683{
684#if OS(WINDOWS)
685 return '\\';
686#else
687 return '/';
688#endif
689}
690
691struct DirectoryName {
692 // In unix, it is "/". In Windows, it becomes a drive letter like "C:\"
693 String rootName;
694
695 // If the directory name is "/home/WebKit", this becomes "home/WebKit". If the directory name is "/", this becomes "".
696 String queryName;
697};
698
699struct ModuleName {
700 ModuleName(const String& moduleName);
701
702 bool startsWithRoot() const
703 {
704 return !queries.isEmpty() && queries[0].isEmpty();
705 }
706
707 Vector<String> queries;
708};
709
710ModuleName::ModuleName(const String& moduleName)
711{
712 // A module name given from code is represented as the UNIX style path. Like, `./A/B.js`.
713 queries = moduleName.splitAllowingEmptyEntries('/');
714}
715
716static Optional<DirectoryName> extractDirectoryName(const String& absolutePathToFile)
717{
718 size_t firstSeparatorPosition = absolutePathToFile.find(pathSeparator());
719 if (firstSeparatorPosition == notFound)
720 return WTF::nullopt;
721 DirectoryName directoryName;
722 directoryName.rootName = absolutePathToFile.substring(0, firstSeparatorPosition + 1); // Include the separator.
723 size_t lastSeparatorPosition = absolutePathToFile.reverseFind(pathSeparator());
724 ASSERT_WITH_MESSAGE(lastSeparatorPosition != notFound, "If the separator is not found, this function already returns when performing the forward search.");
725 if (firstSeparatorPosition == lastSeparatorPosition)
726 directoryName.queryName = StringImpl::empty();
727 else {
728 size_t queryStartPosition = firstSeparatorPosition + 1;
729 size_t queryLength = lastSeparatorPosition - queryStartPosition; // Not include the last separator.
730 directoryName.queryName = absolutePathToFile.substring(queryStartPosition, queryLength);
731 }
732 return directoryName;
733}
734
735static Optional<DirectoryName> currentWorkingDirectory()
736{
737#if OS(WINDOWS)
738 // https://msdn.microsoft.com/en-us/library/windows/desktop/aa364934.aspx
739 // https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx#maxpath
740 // The _MAX_PATH in Windows is 260. If the path of the current working directory is longer than that, _getcwd truncates the result.
741 // And other I/O functions taking a path name also truncate it. To avoid this situation,
742 //
743 // (1). When opening the file in Windows for modules, we always use the abosolute path and add "\\?\" prefix to the path name.
744 // (2). When retrieving the current working directory, use GetCurrentDirectory instead of _getcwd.
745 //
746 // In the path utility functions inside the JSC shell, we does not handle the UNC and UNCW including the network host name.
747 DWORD bufferLength = ::GetCurrentDirectoryW(0, nullptr);
748 if (!bufferLength)
749 return WTF::nullopt;
750 // In Windows, wchar_t is the UTF-16LE.
751 // https://msdn.microsoft.com/en-us/library/dd374081.aspx
752 // https://msdn.microsoft.com/en-us/library/windows/desktop/ff381407.aspx
753 Vector<wchar_t> buffer(bufferLength);
754 DWORD lengthNotIncludingNull = ::GetCurrentDirectoryW(bufferLength, buffer.data());
755 String directoryString(buffer.data(), lengthNotIncludingNull);
756 // We don't support network path like \\host\share\<path name>.
757 if (directoryString.startsWith("\\\\"))
758 return WTF::nullopt;
759#else
760 Vector<char> buffer(PATH_MAX);
761 if (!getcwd(buffer.data(), PATH_MAX))
762 return WTF::nullopt;
763 String directoryString = String::fromUTF8(buffer.data());
764#endif
765 if (directoryString.isEmpty())
766 return WTF::nullopt;
767
768 if (directoryString[directoryString.length() - 1] == pathSeparator())
769 return extractDirectoryName(directoryString);
770 // Append the seperator to represents the file name. extractDirectoryName only accepts the absolute file name.
771 return extractDirectoryName(makeString(directoryString, pathSeparator()));
772}
773
774static String resolvePath(const DirectoryName& directoryName, const ModuleName& moduleName)
775{
776 Vector<String> directoryPieces = directoryName.queryName.split(pathSeparator());
777
778 // Only first '/' is recognized as the path from the root.
779 if (moduleName.startsWithRoot())
780 directoryPieces.clear();
781
782 for (const auto& query : moduleName.queries) {
783 if (query == String(".."_s)) {
784 if (!directoryPieces.isEmpty())
785 directoryPieces.removeLast();
786 } else if (!query.isEmpty() && query != String("."_s))
787 directoryPieces.append(query);
788 }
789
790 StringBuilder builder;
791 builder.append(directoryName.rootName);
792 for (size_t i = 0; i < directoryPieces.size(); ++i) {
793 builder.append(directoryPieces[i]);
794 if (i + 1 != directoryPieces.size())
795 builder.append(pathSeparator());
796 }
797 return builder.toString();
798}
799
800static String absolutePath(const String& fileName)
801{
802 auto directoryName = currentWorkingDirectory();
803 if (!directoryName)
804 return fileName;
805 return resolvePath(directoryName.value(), ModuleName(fileName.impl()));
806}
807
808JSInternalPromise* GlobalObject::moduleLoaderImportModule(JSGlobalObject* globalObject, ExecState* exec, JSModuleLoader*, JSString* moduleNameValue, JSValue parameters, const SourceOrigin& sourceOrigin)
809{
810 VM& vm = globalObject->vm();
811 auto throwScope = DECLARE_THROW_SCOPE(vm);
812
813 auto* deferred = JSInternalPromiseDeferred::tryCreate(exec, globalObject);
814 RETURN_IF_EXCEPTION(throwScope, nullptr);
815
816 auto catchScope = DECLARE_CATCH_SCOPE(vm);
817 auto reject = [&] (JSValue rejectionReason) {
818 catchScope.clearException();
819 auto result = deferred->reject(exec, rejectionReason);
820 catchScope.clearException();
821 return result;
822 };
823
824 if (sourceOrigin.isNull())
825 return reject(createError(exec, "Could not resolve the module specifier."_s));
826
827 const auto& referrer = sourceOrigin.string();
828 const auto& moduleName = moduleNameValue->value(exec);
829 if (UNLIKELY(catchScope.exception()))
830 return reject(catchScope.exception());
831
832 auto directoryName = extractDirectoryName(referrer.impl());
833 if (!directoryName)
834 return reject(createError(exec, makeString("Could not resolve the referrer name '", String(referrer.impl()), "'.")));
835
836 auto result = JSC::importModule(exec, Identifier::fromString(&vm, resolvePath(directoryName.value(), ModuleName(moduleName))), parameters, jsUndefined());
837 if (UNLIKELY(catchScope.exception()))
838 return reject(catchScope.exception());
839 return result;
840}
841
842Identifier GlobalObject::moduleLoaderResolve(JSGlobalObject* globalObject, ExecState* exec, JSModuleLoader*, JSValue keyValue, JSValue referrerValue, JSValue)
843{
844 VM& vm = globalObject->vm();
845 auto scope = DECLARE_THROW_SCOPE(vm);
846
847 scope.releaseAssertNoException();
848 const Identifier key = keyValue.toPropertyKey(exec);
849 RETURN_IF_EXCEPTION(scope, { });
850
851 if (key.isSymbol())
852 return key;
853
854 if (referrerValue.isUndefined()) {
855 auto directoryName = currentWorkingDirectory();
856 if (!directoryName) {
857 throwException(exec, scope, createError(exec, "Could not resolve the current working directory."_s));
858 return { };
859 }
860 return Identifier::fromString(&vm, resolvePath(directoryName.value(), ModuleName(key.impl())));
861 }
862
863 const Identifier referrer = referrerValue.toPropertyKey(exec);
864 RETURN_IF_EXCEPTION(scope, { });
865
866 if (referrer.isSymbol()) {
867 auto directoryName = currentWorkingDirectory();
868 if (!directoryName) {
869 throwException(exec, scope, createError(exec, "Could not resolve the current working directory."_s));
870 return { };
871 }
872 return Identifier::fromString(&vm, resolvePath(directoryName.value(), ModuleName(key.impl())));
873 }
874
875 // If the referrer exists, we assume that the referrer is the correct absolute path.
876 auto directoryName = extractDirectoryName(referrer.impl());
877 if (!directoryName) {
878 throwException(exec, scope, createError(exec, makeString("Could not resolve the referrer name '", String(referrer.impl()), "'.")));
879 return { };
880 }
881 return Identifier::fromString(&vm, resolvePath(directoryName.value(), ModuleName(key.impl())));
882}
883
884template<typename Vector>
885static void convertShebangToJSComment(Vector& buffer)
886{
887 if (buffer.size() >= 2) {
888 if (buffer[0] == '#' && buffer[1] == '!')
889 buffer[0] = buffer[1] = '/';
890 }
891}
892
893static RefPtr<Uint8Array> fillBufferWithContentsOfFile(FILE* file)
894{
895 if (fseek(file, 0, SEEK_END) == -1)
896 return nullptr;
897 long bufferCapacity = ftell(file);
898 if (bufferCapacity == -1)
899 return nullptr;
900 if (fseek(file, 0, SEEK_SET) == -1)
901 return nullptr;
902 auto result = Uint8Array::tryCreate(bufferCapacity);
903 if (!result)
904 return nullptr;
905 size_t readSize = fread(result->data(), 1, bufferCapacity, file);
906 if (readSize != static_cast<size_t>(bufferCapacity))
907 return nullptr;
908 return result;
909}
910
911static RefPtr<Uint8Array> fillBufferWithContentsOfFile(const String& fileName)
912{
913 FILE* f = fopen(fileName.utf8().data(), "rb");
914 if (!f) {
915 fprintf(stderr, "Could not open file: %s\n", fileName.utf8().data());
916 return nullptr;
917 }
918
919 RefPtr<Uint8Array> result = fillBufferWithContentsOfFile(f);
920 fclose(f);
921
922 return result;
923}
924
925template<typename Vector>
926static bool fillBufferWithContentsOfFile(FILE* file, Vector& buffer)
927{
928 // We might have injected "use strict"; at the top.
929 size_t initialSize = buffer.size();
930 if (fseek(file, 0, SEEK_END) == -1)
931 return false;
932 long bufferCapacity = ftell(file);
933 if (bufferCapacity == -1)
934 return false;
935 if (fseek(file, 0, SEEK_SET) == -1)
936 return false;
937 buffer.resize(bufferCapacity + initialSize);
938 size_t readSize = fread(buffer.data() + initialSize, 1, buffer.size(), file);
939 return readSize == buffer.size() - initialSize;
940}
941
942static bool fillBufferWithContentsOfFile(const String& fileName, Vector<char>& buffer)
943{
944 FILE* f = fopen(fileName.utf8().data(), "rb");
945 if (!f) {
946 fprintf(stderr, "Could not open file: %s\n", fileName.utf8().data());
947 return false;
948 }
949
950 bool result = fillBufferWithContentsOfFile(f, buffer);
951 fclose(f);
952
953 return result;
954}
955
956static bool fetchScriptFromLocalFileSystem(const String& fileName, Vector<char>& buffer)
957{
958 if (!fillBufferWithContentsOfFile(fileName, buffer))
959 return false;
960 convertShebangToJSComment(buffer);
961 return true;
962}
963
964class ShellSourceProvider : public StringSourceProvider {
965public:
966 static Ref<ShellSourceProvider> create(const String& source, const SourceOrigin& sourceOrigin, URL&& url, const TextPosition& startPosition, SourceProviderSourceType sourceType)
967 {
968 return adoptRef(*new ShellSourceProvider(source, sourceOrigin, WTFMove(url), startPosition, sourceType));
969 }
970
971 ~ShellSourceProvider()
972 {
973 commitCachedBytecode();
974 }
975
976 RefPtr<CachedBytecode> cachedBytecode() const override
977 {
978 if (!m_cachedBytecode)
979 loadBytecode();
980 return m_cachedBytecode.copyRef();
981 }
982
983 void updateCache(const UnlinkedFunctionExecutable* executable, const SourceCode&, CodeSpecializationKind kind, const UnlinkedFunctionCodeBlock* codeBlock) const override
984 {
985 if (!cacheEnabled() || !m_cachedBytecode)
986 return;
987 BytecodeCacheError error;
988 RefPtr<CachedBytecode> cachedBytecode = encodeFunctionCodeBlock(*executable->vm(), codeBlock, error);
989 if (cachedBytecode && !error.isValid())
990 m_cachedBytecode->addFunctionUpdate(executable, kind, *cachedBytecode);
991 }
992
993 void cacheBytecode(const BytecodeCacheGenerator& generator) const override
994 {
995 if (!cacheEnabled())
996 return;
997 if (!m_cachedBytecode)
998 m_cachedBytecode = CachedBytecode::create();
999 auto update = generator();
1000 if (update)
1001 m_cachedBytecode->addGlobalUpdate(*update);
1002 }
1003
1004 void commitCachedBytecode() const override
1005 {
1006#if OS(DARWIN)
1007 if (!cacheEnabled() || !m_cachedBytecode || !m_cachedBytecode->hasUpdates())
1008 return;
1009
1010 auto clearBytecode = makeScopeExit([&] {
1011 m_cachedBytecode = nullptr;
1012 });
1013
1014 String filename = cachePath();
1015 int fd = open(filename.utf8().data(), O_CREAT | O_WRONLY | O_TRUNC | O_EXLOCK | O_NONBLOCK, 0666);
1016 if (fd == -1)
1017 return;
1018
1019 auto closeFD = makeScopeExit([&] {
1020 close(fd);
1021 });
1022
1023 struct stat sb;
1024 int res = fstat(fd, &sb);
1025 size_t size = static_cast<size_t>(sb.st_size);
1026 if (res || size != m_cachedBytecode->size()) {
1027 // The bytecode cache has already been updated
1028 return;
1029 }
1030
1031 if (ftruncate(fd, m_cachedBytecode->sizeForUpdate()))
1032 return;
1033
1034 m_cachedBytecode->commitUpdates([&] (off_t offset, const void* data, size_t size) {
1035 off_t result = lseek(fd, offset, SEEK_SET);
1036 ASSERT_UNUSED(result, result != -1);
1037 size_t bytesWritten = static_cast<size_t>(write(fd, data, size));
1038 ASSERT_UNUSED(bytesWritten, bytesWritten == size);
1039 });
1040#endif
1041 }
1042
1043private:
1044 String cachePath() const
1045 {
1046 if (!cacheEnabled())
1047 return static_cast<const char*>(nullptr);
1048 const char* cachePath = Options::diskCachePath();
1049 String filename = sourceOrigin().string();
1050 filename.replace('/', '_');
1051 return makeString(cachePath, '/', source().toString().hash(), '-', filename, ".bytecode-cache");
1052 }
1053
1054 void loadBytecode() const
1055 {
1056#if OS(DARWIN)
1057 if (!cacheEnabled())
1058 return;
1059
1060 String filename = cachePath();
1061 if (filename.isNull())
1062 return;
1063
1064 int fd = open(filename.utf8().data(), O_RDONLY | O_SHLOCK | O_NONBLOCK);
1065 if (fd == -1)
1066 return;
1067
1068 auto closeFD = makeScopeExit([&] {
1069 close(fd);
1070 });
1071
1072 struct stat sb;
1073 int res = fstat(fd, &sb);
1074 size_t size = static_cast<size_t>(sb.st_size);
1075 if (res || !size)
1076 return;
1077
1078 void* buffer = mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0);
1079 if (buffer == MAP_FAILED)
1080 return;
1081 m_cachedBytecode = CachedBytecode::create(buffer, size);
1082#endif
1083 }
1084
1085 ShellSourceProvider(const String& source, const SourceOrigin& sourceOrigin, URL&& url, const TextPosition& startPosition, SourceProviderSourceType sourceType)
1086 : StringSourceProvider(source, sourceOrigin, WTFMove(url), startPosition, sourceType)
1087 {
1088 }
1089
1090 static bool cacheEnabled()
1091 {
1092 static bool enabled = !!Options::diskCachePath();
1093 return enabled;
1094 }
1095
1096 mutable RefPtr<CachedBytecode> m_cachedBytecode;
1097};
1098
1099static inline SourceCode jscSource(const String& source, const SourceOrigin& sourceOrigin, URL&& url = URL(), const TextPosition& startPosition = TextPosition(), SourceProviderSourceType sourceType = SourceProviderSourceType::Program)
1100{
1101 return SourceCode(ShellSourceProvider::create(source, sourceOrigin, WTFMove(url), startPosition, sourceType), startPosition.m_line.oneBasedInt(), startPosition.m_column.oneBasedInt());
1102}
1103
1104template<typename Vector>
1105static inline SourceCode jscSource(const Vector& utf8, const SourceOrigin& sourceOrigin, const String& filename)
1106{
1107 // FIXME: This should use an absolute file URL https://bugs.webkit.org/show_bug.cgi?id=193077
1108 String str = stringFromUTF(utf8);
1109 return jscSource(str, sourceOrigin, URL({ }, filename));
1110}
1111
1112template<typename Vector>
1113static bool fetchModuleFromLocalFileSystem(const String& fileName, Vector& buffer)
1114{
1115 // We assume that fileName is always an absolute path.
1116#if OS(WINDOWS)
1117 // https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx#maxpath
1118 // Use long UNC to pass the long path name to the Windows APIs.
1119 auto pathName = makeString("\\\\?\\", fileName).wideCharacters();
1120 struct _stat status { };
1121 if (_wstat(pathName.data(), &status))
1122 return false;
1123 if ((status.st_mode & S_IFMT) != S_IFREG)
1124 return false;
1125
1126 FILE* f = _wfopen(pathName.data(), L"rb");
1127#else
1128 auto pathName = fileName.utf8();
1129 struct stat status { };
1130 if (stat(pathName.data(), &status))
1131 return false;
1132 if ((status.st_mode & S_IFMT) != S_IFREG)
1133 return false;
1134
1135 FILE* f = fopen(pathName.data(), "r");
1136#endif
1137 if (!f) {
1138 fprintf(stderr, "Could not open file: %s\n", fileName.utf8().data());
1139 return false;
1140 }
1141
1142 bool result = fillBufferWithContentsOfFile(f, buffer);
1143 if (result)
1144 convertShebangToJSComment(buffer);
1145 fclose(f);
1146
1147 return result;
1148}
1149
1150JSInternalPromise* GlobalObject::moduleLoaderFetch(JSGlobalObject* globalObject, ExecState* exec, JSModuleLoader*, JSValue key, JSValue, JSValue)
1151{
1152 VM& vm = globalObject->vm();
1153 auto throwScope = DECLARE_THROW_SCOPE(vm);
1154 JSInternalPromiseDeferred* deferred = JSInternalPromiseDeferred::tryCreate(exec, globalObject);
1155 RETURN_IF_EXCEPTION(throwScope, nullptr);
1156
1157 auto catchScope = DECLARE_CATCH_SCOPE(vm);
1158 auto reject = [&] (JSValue rejectionReason) {
1159 catchScope.clearException();
1160 auto result = deferred->reject(exec, rejectionReason);
1161 catchScope.clearException();
1162 return result;
1163 };
1164
1165 String moduleKey = key.toWTFString(exec);
1166 if (UNLIKELY(catchScope.exception()))
1167 return reject(catchScope.exception());
1168
1169 // Here, now we consider moduleKey as the fileName.
1170 Vector<uint8_t> buffer;
1171 if (!fetchModuleFromLocalFileSystem(moduleKey, buffer))
1172 return reject(createError(exec, makeString("Could not open file '", moduleKey, "'.")));
1173
1174
1175 URL moduleURL = URL({ }, moduleKey);
1176#if ENABLE(WEBASSEMBLY)
1177 // FileSystem does not have mime-type header. The JSC shell recognizes WebAssembly's magic header.
1178 if (buffer.size() >= 4) {
1179 if (buffer[0] == '\0' && buffer[1] == 'a' && buffer[2] == 's' && buffer[3] == 'm') {
1180 auto source = SourceCode(WebAssemblySourceProvider::create(WTFMove(buffer), SourceOrigin { moduleKey }, WTFMove(moduleURL)));
1181 catchScope.releaseAssertNoException();
1182 auto sourceCode = JSSourceCode::create(vm, WTFMove(source));
1183 catchScope.releaseAssertNoException();
1184 auto result = deferred->resolve(exec, sourceCode);
1185 catchScope.clearException();
1186 return result;
1187 }
1188 }
1189#endif
1190
1191 auto sourceCode = JSSourceCode::create(vm, jscSource(stringFromUTF(buffer), SourceOrigin { moduleKey }, WTFMove(moduleURL), TextPosition(), SourceProviderSourceType::Module));
1192 catchScope.releaseAssertNoException();
1193 auto result = deferred->resolve(exec, sourceCode);
1194 catchScope.clearException();
1195 return result;
1196}
1197
1198JSObject* GlobalObject::moduleLoaderCreateImportMetaProperties(JSGlobalObject* globalObject, ExecState* exec, JSModuleLoader*, JSValue key, JSModuleRecord*, JSValue)
1199{
1200 VM& vm = exec->vm();
1201 auto scope = DECLARE_THROW_SCOPE(vm);
1202
1203 JSObject* metaProperties = constructEmptyObject(exec, globalObject->nullPrototypeObjectStructure());
1204 RETURN_IF_EXCEPTION(scope, nullptr);
1205
1206 metaProperties->putDirect(vm, Identifier::fromString(&vm, "filename"), key);
1207 RETURN_IF_EXCEPTION(scope, nullptr);
1208
1209 return metaProperties;
1210}
1211
1212static CString cStringFromViewWithString(ExecState* exec, ThrowScope& scope, StringViewWithUnderlyingString& viewWithString)
1213{
1214 Expected<CString, UTF8ConversionError> expectedString = viewWithString.view.tryGetUtf8();
1215 if (expectedString)
1216 return expectedString.value();
1217 switch (expectedString.error()) {
1218 case UTF8ConversionError::OutOfMemory:
1219 throwOutOfMemoryError(exec, scope);
1220 break;
1221 case UTF8ConversionError::IllegalSource:
1222 scope.throwException(exec, createError(exec, "Illegal source encountered during UTF8 conversion"));
1223 break;
1224 case UTF8ConversionError::SourceExhausted:
1225 scope.throwException(exec, createError(exec, "Source exhausted during UTF8 conversion"));
1226 break;
1227 default:
1228 RELEASE_ASSERT_NOT_REACHED();
1229 }
1230 return { };
1231}
1232
1233static EncodedJSValue printInternal(ExecState* exec, FILE* out)
1234{
1235 VM& vm = exec->vm();
1236 auto scope = DECLARE_THROW_SCOPE(vm);
1237
1238 if (asyncTestExpectedPasses) {
1239 JSValue value = exec->argument(0);
1240 if (value.isString() && WTF::equal(asString(value)->value(exec).impl(), "Test262:AsyncTestComplete")) {
1241 asyncTestPasses++;
1242 return JSValue::encode(jsUndefined());
1243 }
1244 }
1245
1246 for (unsigned i = 0; i < exec->argumentCount(); ++i) {
1247 if (i)
1248 if (EOF == fputc(' ', out))
1249 goto fail;
1250
1251 auto viewWithString = exec->uncheckedArgument(i).toString(exec)->viewWithUnderlyingString(exec);
1252 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1253 auto string = cStringFromViewWithString(exec, scope, viewWithString);
1254 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1255 if (fprintf(out, "%s", string.data()) < 0)
1256 goto fail;
1257 }
1258
1259 fputc('\n', out);
1260fail:
1261 fflush(out);
1262 return JSValue::encode(jsUndefined());
1263}
1264
1265EncodedJSValue JSC_HOST_CALL functionPrintStdOut(ExecState* exec) { return printInternal(exec, stdout); }
1266EncodedJSValue JSC_HOST_CALL functionPrintStdErr(ExecState* exec) { return printInternal(exec, stderr); }
1267
1268EncodedJSValue JSC_HOST_CALL functionDebug(ExecState* exec)
1269{
1270 VM& vm = exec->vm();
1271 auto scope = DECLARE_THROW_SCOPE(vm);
1272 auto viewWithString = exec->argument(0).toString(exec)->viewWithUnderlyingString(exec);
1273 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1274 auto string = cStringFromViewWithString(exec, scope, viewWithString);
1275 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1276 fprintf(stderr, "--> %s\n", string.data());
1277 return JSValue::encode(jsUndefined());
1278}
1279
1280EncodedJSValue JSC_HOST_CALL functionDescribe(ExecState* exec)
1281{
1282 if (exec->argumentCount() < 1)
1283 return JSValue::encode(jsUndefined());
1284 return JSValue::encode(jsString(exec, toString(exec->argument(0))));
1285}
1286
1287EncodedJSValue JSC_HOST_CALL functionDescribeArray(ExecState* exec)
1288{
1289 if (exec->argumentCount() < 1)
1290 return JSValue::encode(jsUndefined());
1291 VM& vm = exec->vm();
1292 JSObject* object = jsDynamicCast<JSObject*>(vm, exec->argument(0));
1293 if (!object)
1294 return JSValue::encode(jsNontrivialString(exec, "<not object>"_s));
1295 return JSValue::encode(jsNontrivialString(exec, toString("<Butterfly: ", RawPointer(object->butterfly()), "; public length: ", object->getArrayLength(), "; vector length: ", object->getVectorLength(), ">")));
1296}
1297
1298EncodedJSValue JSC_HOST_CALL functionSleepSeconds(ExecState* exec)
1299{
1300 VM& vm = exec->vm();
1301 auto scope = DECLARE_THROW_SCOPE(vm);
1302
1303 if (exec->argumentCount() >= 1) {
1304 Seconds seconds = Seconds(exec->argument(0).toNumber(exec));
1305 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1306 sleep(seconds);
1307 }
1308
1309 return JSValue::encode(jsUndefined());
1310}
1311
1312class FunctionJSCStackFunctor {
1313public:
1314 FunctionJSCStackFunctor(StringBuilder& trace)
1315 : m_trace(trace)
1316 {
1317 }
1318
1319 StackVisitor::Status operator()(StackVisitor& visitor) const
1320 {
1321 m_trace.append(makeString(" ", visitor->index(), " ", visitor->toString(), '\n'));
1322 return StackVisitor::Continue;
1323 }
1324
1325private:
1326 StringBuilder& m_trace;
1327};
1328
1329EncodedJSValue JSC_HOST_CALL functionJSCStack(ExecState* exec)
1330{
1331 StringBuilder trace;
1332 trace.appendLiteral("--> Stack trace:\n");
1333
1334 FunctionJSCStackFunctor functor(trace);
1335 exec->iterate(functor);
1336 fprintf(stderr, "%s", trace.toString().utf8().data());
1337 return JSValue::encode(jsUndefined());
1338}
1339
1340EncodedJSValue JSC_HOST_CALL functionGCAndSweep(ExecState* exec)
1341{
1342 VM& vm = exec->vm();
1343 JSLockHolder lock(vm);
1344 vm.heap.collectNow(Sync, CollectionScope::Full);
1345 return JSValue::encode(jsNumber(vm.heap.sizeAfterLastFullCollection()));
1346}
1347
1348EncodedJSValue JSC_HOST_CALL functionFullGC(ExecState* exec)
1349{
1350 VM& vm = exec->vm();
1351 JSLockHolder lock(vm);
1352 vm.heap.collectSync(CollectionScope::Full);
1353 return JSValue::encode(jsNumber(vm.heap.sizeAfterLastFullCollection()));
1354}
1355
1356EncodedJSValue JSC_HOST_CALL functionEdenGC(ExecState* exec)
1357{
1358 VM& vm = exec->vm();
1359 JSLockHolder lock(vm);
1360 vm.heap.collectSync(CollectionScope::Eden);
1361 return JSValue::encode(jsNumber(vm.heap.sizeAfterLastEdenCollection()));
1362}
1363
1364EncodedJSValue JSC_HOST_CALL functionForceGCSlowPaths(ExecState*)
1365{
1366 // It's best for this to be the first thing called in the
1367 // JS program so the option is set to true before we JIT.
1368 Options::forceGCSlowPaths() = true;
1369 return JSValue::encode(jsUndefined());
1370}
1371
1372EncodedJSValue JSC_HOST_CALL functionHeapSize(ExecState* exec)
1373{
1374 VM& vm = exec->vm();
1375 JSLockHolder lock(vm);
1376 return JSValue::encode(jsNumber(vm.heap.size()));
1377}
1378
1379class JSCMemoryFootprint : public JSDestructibleObject {
1380 using Base = JSDestructibleObject;
1381public:
1382 JSCMemoryFootprint(VM& vm, Structure* structure)
1383 : Base(vm, structure)
1384 { }
1385
1386 static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
1387 {
1388 return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
1389 }
1390
1391 static JSCMemoryFootprint* create(VM& vm, JSGlobalObject* globalObject)
1392 {
1393 Structure* structure = createStructure(vm, globalObject, jsNull());
1394 JSCMemoryFootprint* footprint = new (NotNull, allocateCell<JSCMemoryFootprint>(vm.heap, sizeof(JSCMemoryFootprint))) JSCMemoryFootprint(vm, structure);
1395 footprint->finishCreation(vm);
1396 return footprint;
1397 }
1398
1399 void finishCreation(VM& vm)
1400 {
1401 Base::finishCreation(vm);
1402
1403 auto addProperty = [&] (VM& vm, const char* name, JSValue value) {
1404 JSCMemoryFootprint::addProperty(vm, name, value);
1405 };
1406
1407 MemoryFootprint footprint = MemoryFootprint::now();
1408
1409 addProperty(vm, "current", jsNumber(footprint.current));
1410 addProperty(vm, "peak", jsNumber(footprint.peak));
1411 }
1412
1413 DECLARE_INFO;
1414
1415private:
1416 void addProperty(VM& vm, const char* name, JSValue value)
1417 {
1418 Identifier identifier = Identifier::fromString(&vm, name);
1419 putDirect(vm, identifier, value);
1420 }
1421};
1422
1423const ClassInfo JSCMemoryFootprint::s_info = { "MemoryFootprint", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSCMemoryFootprint) };
1424
1425EncodedJSValue JSC_HOST_CALL functionCreateMemoryFootprint(ExecState* exec)
1426{
1427 VM& vm = exec->vm();
1428 JSLockHolder lock(vm);
1429 return JSValue::encode(JSCMemoryFootprint::create(vm, exec->lexicalGlobalObject()));
1430}
1431
1432EncodedJSValue JSC_HOST_CALL functionResetMemoryPeak(ExecState*)
1433{
1434 MemoryFootprint::resetPeak();
1435 return JSValue::encode(jsUndefined());
1436}
1437
1438// This function is not generally very helpful in 64-bit code as the tag and payload
1439// share a register. But in 32-bit JITed code the tag may not be checked if an
1440// optimization removes type checking requirements, such as in ===.
1441EncodedJSValue JSC_HOST_CALL functionAddressOf(ExecState* exec)
1442{
1443 JSValue value = exec->argument(0);
1444 if (!value.isCell())
1445 return JSValue::encode(jsUndefined());
1446 // Need to cast to uint64_t so bitwise_cast will play along.
1447 uint64_t asNumber = reinterpret_cast<uint64_t>(value.asCell());
1448 EncodedJSValue returnValue = JSValue::encode(jsNumber(bitwise_cast<double>(asNumber)));
1449 return returnValue;
1450}
1451
1452EncodedJSValue JSC_HOST_CALL functionVersion(ExecState*)
1453{
1454 // We need this function for compatibility with the Mozilla JS tests but for now
1455 // we don't actually do any version-specific handling
1456 return JSValue::encode(jsUndefined());
1457}
1458
1459EncodedJSValue JSC_HOST_CALL functionRun(ExecState* exec)
1460{
1461 VM& vm = exec->vm();
1462 auto scope = DECLARE_THROW_SCOPE(vm);
1463
1464 String fileName = exec->argument(0).toWTFString(exec);
1465 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1466 Vector<char> script;
1467 if (!fetchScriptFromLocalFileSystem(fileName, script))
1468 return JSValue::encode(throwException(exec, scope, createError(exec, "Could not open file."_s)));
1469
1470 GlobalObject* globalObject = GlobalObject::create(vm, GlobalObject::createStructure(vm, jsNull()), Vector<String>());
1471
1472 JSArray* array = constructEmptyArray(globalObject->globalExec(), 0);
1473 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1474 for (unsigned i = 1; i < exec->argumentCount(); ++i) {
1475 array->putDirectIndex(globalObject->globalExec(), i - 1, exec->uncheckedArgument(i));
1476 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1477 }
1478 globalObject->putDirect(
1479 vm, Identifier::fromString(globalObject->globalExec(), "arguments"), array);
1480
1481 NakedPtr<Exception> exception;
1482 StopWatch stopWatch;
1483 stopWatch.start();
1484 evaluate(globalObject->globalExec(), jscSource(script, SourceOrigin { absolutePath(fileName) }, fileName), JSValue(), exception);
1485 stopWatch.stop();
1486
1487 if (exception) {
1488 throwException(globalObject->globalExec(), scope, exception);
1489 return JSValue::encode(jsUndefined());
1490 }
1491
1492 return JSValue::encode(jsNumber(stopWatch.getElapsedMS()));
1493}
1494
1495EncodedJSValue JSC_HOST_CALL functionRunString(ExecState* exec)
1496{
1497 VM& vm = exec->vm();
1498 auto scope = DECLARE_THROW_SCOPE(vm);
1499
1500 String source = exec->argument(0).toWTFString(exec);
1501 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1502
1503 GlobalObject* globalObject = GlobalObject::create(vm, GlobalObject::createStructure(vm, jsNull()), Vector<String>());
1504
1505 JSArray* array = constructEmptyArray(globalObject->globalExec(), 0);
1506 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1507 for (unsigned i = 1; i < exec->argumentCount(); ++i) {
1508 array->putDirectIndex(globalObject->globalExec(), i - 1, exec->uncheckedArgument(i));
1509 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1510 }
1511 globalObject->putDirect(
1512 vm, Identifier::fromString(globalObject->globalExec(), "arguments"), array);
1513
1514 NakedPtr<Exception> exception;
1515 evaluate(globalObject->globalExec(), jscSource(source, exec->callerSourceOrigin()), JSValue(), exception);
1516
1517 if (exception) {
1518 scope.throwException(globalObject->globalExec(), exception);
1519 return JSValue::encode(jsUndefined());
1520 }
1521
1522 return JSValue::encode(globalObject);
1523}
1524
1525EncodedJSValue JSC_HOST_CALL functionLoad(ExecState* exec)
1526{
1527 VM& vm = exec->vm();
1528 auto scope = DECLARE_THROW_SCOPE(vm);
1529
1530 String fileName = exec->argument(0).toWTFString(exec);
1531 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1532 Vector<char> script;
1533 if (!fetchScriptFromLocalFileSystem(fileName, script))
1534 return JSValue::encode(throwException(exec, scope, createError(exec, "Could not open file."_s)));
1535
1536 JSGlobalObject* globalObject = exec->lexicalGlobalObject();
1537
1538 NakedPtr<Exception> evaluationException;
1539 JSValue result = evaluate(globalObject->globalExec(), jscSource(script, SourceOrigin { absolutePath(fileName) }, fileName), JSValue(), evaluationException);
1540 if (evaluationException)
1541 throwException(exec, scope, evaluationException);
1542 return JSValue::encode(result);
1543}
1544
1545EncodedJSValue JSC_HOST_CALL functionLoadString(ExecState* exec)
1546{
1547 VM& vm = exec->vm();
1548 auto scope = DECLARE_THROW_SCOPE(vm);
1549
1550 String sourceCode = exec->argument(0).toWTFString(exec);
1551 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1552 JSGlobalObject* globalObject = exec->lexicalGlobalObject();
1553
1554 NakedPtr<Exception> evaluationException;
1555 JSValue result = evaluate(globalObject->globalExec(), jscSource(sourceCode, exec->callerSourceOrigin()), JSValue(), evaluationException);
1556 if (evaluationException)
1557 throwException(exec, scope, evaluationException);
1558 return JSValue::encode(result);
1559}
1560
1561EncodedJSValue JSC_HOST_CALL functionReadFile(ExecState* exec)
1562{
1563 VM& vm = exec->vm();
1564 auto scope = DECLARE_THROW_SCOPE(vm);
1565
1566 String fileName = exec->argument(0).toWTFString(exec);
1567 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1568
1569 bool isBinary = false;
1570 if (exec->argumentCount() > 1) {
1571 String type = exec->argument(1).toWTFString(exec);
1572 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1573 if (type != "binary")
1574 return throwVMError(exec, scope, "Expected 'binary' as second argument.");
1575 isBinary = true;
1576 }
1577
1578 RefPtr<Uint8Array> content = fillBufferWithContentsOfFile(fileName);
1579 if (!content)
1580 return throwVMError(exec, scope, "Could not open file.");
1581
1582 if (!isBinary)
1583 return JSValue::encode(jsString(exec, String::fromUTF8WithLatin1Fallback(content->data(), content->length())));
1584
1585 Structure* structure = exec->lexicalGlobalObject()->typedArrayStructure(TypeUint8);
1586 JSObject* result = JSUint8Array::create(vm, structure, WTFMove(content));
1587 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1588
1589 return JSValue::encode(result);
1590}
1591
1592EncodedJSValue JSC_HOST_CALL functionCheckSyntax(ExecState* exec)
1593{
1594 VM& vm = exec->vm();
1595 auto scope = DECLARE_THROW_SCOPE(vm);
1596
1597 String fileName = exec->argument(0).toWTFString(exec);
1598 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1599 Vector<char> script;
1600 if (!fetchScriptFromLocalFileSystem(fileName, script))
1601 return JSValue::encode(throwException(exec, scope, createError(exec, "Could not open file."_s)));
1602
1603 JSGlobalObject* globalObject = exec->lexicalGlobalObject();
1604
1605 StopWatch stopWatch;
1606 stopWatch.start();
1607
1608 JSValue syntaxException;
1609 bool validSyntax = checkSyntax(globalObject->globalExec(), jscSource(script, SourceOrigin { absolutePath(fileName) }, fileName), &syntaxException);
1610 stopWatch.stop();
1611
1612 if (!validSyntax)
1613 throwException(exec, scope, syntaxException);
1614 return JSValue::encode(jsNumber(stopWatch.getElapsedMS()));
1615}
1616
1617#if ENABLE(SAMPLING_FLAGS)
1618EncodedJSValue JSC_HOST_CALL functionSetSamplingFlags(ExecState* exec)
1619{
1620 for (unsigned i = 0; i < exec->argumentCount(); ++i) {
1621 unsigned flag = static_cast<unsigned>(exec->uncheckedArgument(i).toNumber(exec));
1622 if ((flag >= 1) && (flag <= 32))
1623 SamplingFlags::setFlag(flag);
1624 }
1625 return JSValue::encode(jsNull());
1626}
1627
1628EncodedJSValue JSC_HOST_CALL functionClearSamplingFlags(ExecState* exec)
1629{
1630 for (unsigned i = 0; i < exec->argumentCount(); ++i) {
1631 unsigned flag = static_cast<unsigned>(exec->uncheckedArgument(i).toNumber(exec));
1632 if ((flag >= 1) && (flag <= 32))
1633 SamplingFlags::clearFlag(flag);
1634 }
1635 return JSValue::encode(jsNull());
1636}
1637#endif
1638
1639EncodedJSValue JSC_HOST_CALL functionGetRandomSeed(ExecState* exec)
1640{
1641 return JSValue::encode(jsNumber(exec->lexicalGlobalObject()->weakRandom().seed()));
1642}
1643
1644EncodedJSValue JSC_HOST_CALL functionSetRandomSeed(ExecState* exec)
1645{
1646 VM& vm = exec->vm();
1647 auto scope = DECLARE_THROW_SCOPE(vm);
1648
1649 unsigned seed = exec->argument(0).toUInt32(exec);
1650 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1651 exec->lexicalGlobalObject()->weakRandom().setSeed(seed);
1652 return JSValue::encode(jsUndefined());
1653}
1654
1655EncodedJSValue JSC_HOST_CALL functionIsRope(ExecState* exec)
1656{
1657 JSValue argument = exec->argument(0);
1658 if (!argument.isString())
1659 return JSValue::encode(jsBoolean(false));
1660 const StringImpl* impl = asString(argument)->tryGetValueImpl();
1661 return JSValue::encode(jsBoolean(!impl));
1662}
1663
1664EncodedJSValue JSC_HOST_CALL functionCallerSourceOrigin(ExecState* state)
1665{
1666 SourceOrigin sourceOrigin = state->callerSourceOrigin();
1667 if (sourceOrigin.isNull())
1668 return JSValue::encode(jsNull());
1669 return JSValue::encode(jsString(state, sourceOrigin.string()));
1670}
1671
1672EncodedJSValue JSC_HOST_CALL functionReadline(ExecState* exec)
1673{
1674 Vector<char, 256> line;
1675 int c;
1676 while ((c = getchar()) != EOF) {
1677 // FIXME: Should we also break on \r?
1678 if (c == '\n')
1679 break;
1680 line.append(c);
1681 }
1682 line.append('\0');
1683 return JSValue::encode(jsString(exec, line.data()));
1684}
1685
1686EncodedJSValue JSC_HOST_CALL functionPreciseTime(ExecState*)
1687{
1688 return JSValue::encode(jsNumber(WallTime::now().secondsSinceEpoch().value()));
1689}
1690
1691EncodedJSValue JSC_HOST_CALL functionNeverInlineFunction(ExecState* exec)
1692{
1693 return JSValue::encode(setNeverInline(exec));
1694}
1695
1696EncodedJSValue JSC_HOST_CALL functionNoDFG(ExecState* exec)
1697{
1698 return JSValue::encode(setNeverOptimize(exec));
1699}
1700
1701EncodedJSValue JSC_HOST_CALL functionNoFTL(ExecState* exec)
1702{
1703 if (exec->argumentCount()) {
1704 FunctionExecutable* executable = getExecutableForFunction(exec->argument(0));
1705 if (executable)
1706 executable->setNeverFTLOptimize(true);
1707 }
1708 return JSValue::encode(jsUndefined());
1709}
1710
1711EncodedJSValue JSC_HOST_CALL functionNoOSRExitFuzzing(ExecState* exec)
1712{
1713 return JSValue::encode(setCannotUseOSRExitFuzzing(exec));
1714}
1715
1716EncodedJSValue JSC_HOST_CALL functionOptimizeNextInvocation(ExecState* exec)
1717{
1718 return JSValue::encode(optimizeNextInvocation(exec));
1719}
1720
1721EncodedJSValue JSC_HOST_CALL functionNumberOfDFGCompiles(ExecState* exec)
1722{
1723 return JSValue::encode(numberOfDFGCompiles(exec));
1724}
1725
1726Message::Message(ArrayBufferContents&& contents, int32_t index)
1727 : m_contents(WTFMove(contents))
1728 , m_index(index)
1729{
1730}
1731
1732Message::~Message()
1733{
1734}
1735
1736Worker::Worker(Workers& workers)
1737 : m_workers(workers)
1738{
1739 auto locker = holdLock(m_workers.m_lock);
1740 m_workers.m_workers.append(this);
1741
1742 *currentWorker() = this;
1743}
1744
1745Worker::~Worker()
1746{
1747 auto locker = holdLock(m_workers.m_lock);
1748 RELEASE_ASSERT(isOnList());
1749 remove();
1750}
1751
1752void Worker::enqueue(const AbstractLocker&, RefPtr<Message> message)
1753{
1754 m_messages.append(message);
1755}
1756
1757RefPtr<Message> Worker::dequeue()
1758{
1759 auto locker = holdLock(m_workers.m_lock);
1760 while (m_messages.isEmpty())
1761 m_workers.m_condition.wait(m_workers.m_lock);
1762 return m_messages.takeFirst();
1763}
1764
1765Worker& Worker::current()
1766{
1767 return **currentWorker();
1768}
1769
1770ThreadSpecific<Worker*>& Worker::currentWorker()
1771{
1772 static ThreadSpecific<Worker*>* result;
1773 static std::once_flag flag;
1774 std::call_once(
1775 flag,
1776 [] () {
1777 result = new ThreadSpecific<Worker*>();
1778 });
1779 return *result;
1780}
1781
1782Workers::Workers()
1783{
1784}
1785
1786Workers::~Workers()
1787{
1788 UNREACHABLE_FOR_PLATFORM();
1789}
1790
1791template<typename Func>
1792void Workers::broadcast(const Func& func)
1793{
1794 auto locker = holdLock(m_lock);
1795 for (Worker* worker = m_workers.begin(); worker != m_workers.end(); worker = worker->next()) {
1796 if (worker != &Worker::current())
1797 func(locker, *worker);
1798 }
1799 m_condition.notifyAll();
1800}
1801
1802void Workers::report(const String& string)
1803{
1804 auto locker = holdLock(m_lock);
1805 m_reports.append(string.isolatedCopy());
1806 m_condition.notifyAll();
1807}
1808
1809String Workers::tryGetReport()
1810{
1811 auto locker = holdLock(m_lock);
1812 if (m_reports.isEmpty())
1813 return String();
1814 return m_reports.takeFirst();
1815}
1816
1817String Workers::getReport()
1818{
1819 auto locker = holdLock(m_lock);
1820 while (m_reports.isEmpty())
1821 m_condition.wait(m_lock);
1822 return m_reports.takeFirst();
1823}
1824
1825Workers& Workers::singleton()
1826{
1827 static Workers* result;
1828 static std::once_flag flag;
1829 std::call_once(
1830 flag,
1831 [] {
1832 result = new Workers();
1833 });
1834 return *result;
1835}
1836
1837EncodedJSValue JSC_HOST_CALL functionDollarCreateRealm(ExecState* exec)
1838{
1839 VM& vm = exec->vm();
1840 GlobalObject* result = GlobalObject::create(vm, GlobalObject::createStructure(vm, jsNull()), Vector<String>());
1841 return JSValue::encode(result->getDirect(vm, Identifier::fromString(exec, "$")));
1842}
1843
1844EncodedJSValue JSC_HOST_CALL functionDollarDetachArrayBuffer(ExecState* exec)
1845{
1846 return functionTransferArrayBuffer(exec);
1847}
1848
1849EncodedJSValue JSC_HOST_CALL functionDollarEvalScript(ExecState* exec)
1850{
1851 VM& vm = exec->vm();
1852 auto scope = DECLARE_THROW_SCOPE(vm);
1853
1854 String sourceCode = exec->argument(0).toWTFString(exec);
1855 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1856
1857 GlobalObject* globalObject = jsDynamicCast<GlobalObject*>(vm,
1858 exec->thisValue().get(exec, Identifier::fromString(exec, "global")));
1859 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1860 if (!globalObject)
1861 return JSValue::encode(throwException(exec, scope, createError(exec, "Expected global to point to a global object"_s)));
1862
1863 NakedPtr<Exception> evaluationException;
1864 JSValue result = evaluate(globalObject->globalExec(), jscSource(sourceCode, exec->callerSourceOrigin()), JSValue(), evaluationException);
1865 if (evaluationException)
1866 throwException(exec, scope, evaluationException);
1867 return JSValue::encode(result);
1868}
1869
1870EncodedJSValue JSC_HOST_CALL functionDollarAgentStart(ExecState* exec)
1871{
1872 VM& vm = exec->vm();
1873 auto scope = DECLARE_THROW_SCOPE(vm);
1874
1875 String sourceCode = exec->argument(0).toWTFString(exec).isolatedCopy();
1876 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1877
1878 Lock didStartLock;
1879 Condition didStartCondition;
1880 bool didStart = false;
1881
1882 Thread::create(
1883 "JSC Agent",
1884 [sourceCode, &didStartLock, &didStartCondition, &didStart] () {
1885 CommandLine commandLine(0, nullptr);
1886 commandLine.m_interactive = false;
1887 runJSC(
1888 commandLine, true,
1889 [&] (VM&, GlobalObject* globalObject, bool& success) {
1890 // Notify the thread that started us that we have registered a worker.
1891 {
1892 auto locker = holdLock(didStartLock);
1893 didStart = true;
1894 didStartCondition.notifyOne();
1895 }
1896
1897 NakedPtr<Exception> evaluationException;
1898 JSValue result;
1899 result = evaluate(globalObject->globalExec(), jscSource(sourceCode, SourceOrigin("worker"_s)), JSValue(), evaluationException);
1900 if (evaluationException)
1901 result = evaluationException->value();
1902 checkException(globalObject->globalExec(), globalObject, true, evaluationException, result, commandLine, success);
1903 if (!success)
1904 exit(1);
1905 });
1906 })->detach();
1907
1908 {
1909 auto locker = holdLock(didStartLock);
1910 while (!didStart)
1911 didStartCondition.wait(didStartLock);
1912 }
1913
1914 return JSValue::encode(jsUndefined());
1915}
1916
1917EncodedJSValue JSC_HOST_CALL functionDollarAgentReceiveBroadcast(ExecState* exec)
1918{
1919 VM& vm = exec->vm();
1920 auto scope = DECLARE_THROW_SCOPE(vm);
1921
1922 JSValue callback = exec->argument(0);
1923 CallData callData;
1924 CallType callType = getCallData(vm, callback, callData);
1925 if (callType == CallType::None)
1926 return JSValue::encode(throwException(exec, scope, createError(exec, "Expected callback"_s)));
1927
1928 RefPtr<Message> message;
1929 {
1930 ReleaseHeapAccessScope releaseAccess(vm.heap);
1931 message = Worker::current().dequeue();
1932 }
1933
1934 auto nativeBuffer = ArrayBuffer::create(message->releaseContents());
1935 ArrayBufferSharingMode sharingMode = nativeBuffer->sharingMode();
1936 JSArrayBuffer* jsBuffer = JSArrayBuffer::create(vm, exec->lexicalGlobalObject()->arrayBufferStructure(sharingMode), WTFMove(nativeBuffer));
1937
1938 MarkedArgumentBuffer args;
1939 args.append(jsBuffer);
1940 args.append(jsNumber(message->index()));
1941 if (UNLIKELY(args.hasOverflowed()))
1942 return JSValue::encode(throwOutOfMemoryError(exec, scope));
1943 RELEASE_AND_RETURN(scope, JSValue::encode(call(exec, callback, callType, callData, jsNull(), args)));
1944}
1945
1946EncodedJSValue JSC_HOST_CALL functionDollarAgentReport(ExecState* exec)
1947{
1948 VM& vm = exec->vm();
1949 auto scope = DECLARE_THROW_SCOPE(vm);
1950
1951 String report = exec->argument(0).toWTFString(exec);
1952 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1953
1954 Workers::singleton().report(report);
1955
1956 return JSValue::encode(jsUndefined());
1957}
1958
1959EncodedJSValue JSC_HOST_CALL functionDollarAgentSleep(ExecState* exec)
1960{
1961 VM& vm = exec->vm();
1962 auto scope = DECLARE_THROW_SCOPE(vm);
1963
1964 if (exec->argumentCount() >= 1) {
1965 Seconds seconds = Seconds::fromMilliseconds(exec->argument(0).toNumber(exec));
1966 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1967 sleep(seconds);
1968 }
1969 return JSValue::encode(jsUndefined());
1970}
1971
1972EncodedJSValue JSC_HOST_CALL functionDollarAgentBroadcast(ExecState* exec)
1973{
1974 VM& vm = exec->vm();
1975 auto scope = DECLARE_THROW_SCOPE(vm);
1976
1977 JSArrayBuffer* jsBuffer = jsDynamicCast<JSArrayBuffer*>(vm, exec->argument(0));
1978 if (!jsBuffer || !jsBuffer->isShared())
1979 return JSValue::encode(throwException(exec, scope, createError(exec, "Expected SharedArrayBuffer"_s)));
1980
1981 int32_t index = exec->argument(1).toInt32(exec);
1982 RETURN_IF_EXCEPTION(scope, encodedJSValue());
1983
1984 Workers::singleton().broadcast(
1985 [&] (const AbstractLocker& locker, Worker& worker) {
1986 ArrayBuffer* nativeBuffer = jsBuffer->impl();
1987 ArrayBufferContents contents;
1988 nativeBuffer->transferTo(vm, contents); // "transferTo" means "share" if the buffer is shared.
1989 RefPtr<Message> message = adoptRef(new Message(WTFMove(contents), index));
1990 worker.enqueue(locker, message);
1991 });
1992
1993 return JSValue::encode(jsUndefined());
1994}
1995
1996EncodedJSValue JSC_HOST_CALL functionDollarAgentGetReport(ExecState* exec)
1997{
1998 VM& vm = exec->vm();
1999
2000 String string = Workers::singleton().tryGetReport();
2001 if (!string)
2002 return JSValue::encode(jsNull());
2003
2004 return JSValue::encode(jsString(&vm, string));
2005}
2006
2007EncodedJSValue JSC_HOST_CALL functionDollarAgentLeaving(ExecState*)
2008{
2009 return JSValue::encode(jsUndefined());
2010}
2011
2012EncodedJSValue JSC_HOST_CALL functionDollarAgentMonotonicNow(ExecState*)
2013{
2014 return JSValue::encode(jsNumber(MonotonicTime::now().secondsSinceEpoch().milliseconds()));
2015}
2016
2017EncodedJSValue JSC_HOST_CALL functionWaitForReport(ExecState* exec)
2018{
2019 VM& vm = exec->vm();
2020
2021 String string;
2022 {
2023 ReleaseHeapAccessScope releaseAccess(vm.heap);
2024 string = Workers::singleton().getReport();
2025 }
2026 if (!string)
2027 return JSValue::encode(jsNull());
2028
2029 return JSValue::encode(jsString(&vm, string));
2030}
2031
2032EncodedJSValue JSC_HOST_CALL functionHeapCapacity(ExecState* exec)
2033{
2034 VM& vm = exec->vm();
2035 return JSValue::encode(jsNumber(vm.heap.capacity()));
2036}
2037
2038EncodedJSValue JSC_HOST_CALL functionFlashHeapAccess(ExecState* exec)
2039{
2040 VM& vm = exec->vm();
2041 auto scope = DECLARE_THROW_SCOPE(vm);
2042
2043 double sleepTimeMs = 0;
2044 if (exec->argumentCount() >= 1) {
2045 sleepTimeMs = exec->argument(0).toNumber(exec);
2046 RETURN_IF_EXCEPTION(scope, encodedJSValue());
2047 }
2048
2049 vm.heap.releaseAccess();
2050 if (sleepTimeMs)
2051 sleep(Seconds::fromMilliseconds(sleepTimeMs));
2052 vm.heap.acquireAccess();
2053 return JSValue::encode(jsUndefined());
2054}
2055
2056EncodedJSValue JSC_HOST_CALL functionDisableRichSourceInfo(ExecState*)
2057{
2058 supportsRichSourceInfo = false;
2059 return JSValue::encode(jsUndefined());
2060}
2061
2062EncodedJSValue JSC_HOST_CALL functionMallocInALoop(ExecState*)
2063{
2064 Vector<void*> ptrs;
2065 for (unsigned i = 0; i < 5000; ++i)
2066 ptrs.append(fastMalloc(1024 * 2));
2067 for (void* ptr : ptrs)
2068 fastFree(ptr);
2069 return JSValue::encode(jsUndefined());
2070}
2071
2072EncodedJSValue JSC_HOST_CALL functionTotalCompileTime(ExecState*)
2073{
2074#if ENABLE(JIT)
2075 return JSValue::encode(jsNumber(JIT::totalCompileTime().milliseconds()));
2076#else
2077 return JSValue::encode(jsNumber(0));
2078#endif
2079}
2080
2081template<typename ValueType>
2082typename std::enable_if<!std::is_fundamental<ValueType>::value>::type addOption(VM&, JSObject*, const Identifier&, ValueType) { }
2083
2084template<typename ValueType>
2085typename std::enable_if<std::is_fundamental<ValueType>::value>::type addOption(VM& vm, JSObject* optionsObject, const Identifier& identifier, ValueType value)
2086{
2087 optionsObject->putDirect(vm, identifier, JSValue(value));
2088}
2089
2090EncodedJSValue JSC_HOST_CALL functionJSCOptions(ExecState* exec)
2091{
2092 VM& vm = exec->vm();
2093 JSObject* optionsObject = constructEmptyObject(exec);
2094#define FOR_EACH_OPTION(type_, name_, defaultValue_, availability_, description_) \
2095 addOption(vm, optionsObject, Identifier::fromString(exec, #name_), Options::name_());
2096 JSC_OPTIONS(FOR_EACH_OPTION)
2097#undef FOR_EACH_OPTION
2098 return JSValue::encode(optionsObject);
2099}
2100
2101EncodedJSValue JSC_HOST_CALL functionReoptimizationRetryCount(ExecState* exec)
2102{
2103 if (exec->argumentCount() < 1)
2104 return JSValue::encode(jsUndefined());
2105
2106 CodeBlock* block = getSomeBaselineCodeBlockForFunction(exec->argument(0));
2107 if (!block)
2108 return JSValue::encode(jsNumber(0));
2109
2110 return JSValue::encode(jsNumber(block->reoptimizationRetryCounter()));
2111}
2112
2113EncodedJSValue JSC_HOST_CALL functionTransferArrayBuffer(ExecState* exec)
2114{
2115 VM& vm = exec->vm();
2116 auto scope = DECLARE_THROW_SCOPE(vm);
2117
2118 if (exec->argumentCount() < 1)
2119 return JSValue::encode(throwException(exec, scope, createError(exec, "Not enough arguments"_s)));
2120
2121 JSArrayBuffer* buffer = jsDynamicCast<JSArrayBuffer*>(vm, exec->argument(0));
2122 if (!buffer)
2123 return JSValue::encode(throwException(exec, scope, createError(exec, "Expected an array buffer"_s)));
2124
2125 ArrayBufferContents dummyContents;
2126 buffer->impl()->transferTo(vm, dummyContents);
2127
2128 return JSValue::encode(jsUndefined());
2129}
2130
2131EncodedJSValue JSC_HOST_CALL functionFailNextNewCodeBlock(ExecState* exec)
2132{
2133 VM& vm = exec->vm();
2134 vm.setFailNextNewCodeBlock();
2135 return JSValue::encode(jsUndefined());
2136}
2137
2138EncodedJSValue JSC_HOST_CALL functionQuit(ExecState* exec)
2139{
2140 VM& vm = exec->vm();
2141 vm.codeCache()->write(vm);
2142
2143 jscExit(EXIT_SUCCESS);
2144
2145#if COMPILER(MSVC)
2146 // Without this, Visual Studio will complain that this method does not return a value.
2147 return JSValue::encode(jsUndefined());
2148#endif
2149}
2150
2151EncodedJSValue JSC_HOST_CALL functionFalse(ExecState*) { return JSValue::encode(jsBoolean(false)); }
2152
2153EncodedJSValue JSC_HOST_CALL functionUndefined1(ExecState*) { return JSValue::encode(jsUndefined()); }
2154EncodedJSValue JSC_HOST_CALL functionUndefined2(ExecState*) { return JSValue::encode(jsUndefined()); }
2155EncodedJSValue JSC_HOST_CALL functionIsInt32(ExecState* exec)
2156{
2157 for (size_t i = 0; i < exec->argumentCount(); ++i) {
2158 if (!exec->argument(i).isInt32())
2159 return JSValue::encode(jsBoolean(false));
2160 }
2161 return JSValue::encode(jsBoolean(true));
2162}
2163
2164EncodedJSValue JSC_HOST_CALL functionIsPureNaN(ExecState* exec)
2165{
2166 for (size_t i = 0; i < exec->argumentCount(); ++i) {
2167 JSValue value = exec->argument(i);
2168 if (!value.isNumber())
2169 return JSValue::encode(jsBoolean(false));
2170 double number = value.asNumber();
2171 if (!std::isnan(number))
2172 return JSValue::encode(jsBoolean(false));
2173 if (isImpureNaN(number))
2174 return JSValue::encode(jsBoolean(false));
2175 }
2176 return JSValue::encode(jsBoolean(true));
2177}
2178
2179EncodedJSValue JSC_HOST_CALL functionIdentity(ExecState* exec) { return JSValue::encode(exec->argument(0)); }
2180
2181EncodedJSValue JSC_HOST_CALL functionEffectful42(ExecState*)
2182{
2183 return JSValue::encode(jsNumber(42));
2184}
2185
2186EncodedJSValue JSC_HOST_CALL functionMakeMasquerader(ExecState* exec)
2187{
2188 VM& vm = exec->vm();
2189 return JSValue::encode(Masquerader::create(vm, exec->lexicalGlobalObject()));
2190}
2191
2192EncodedJSValue JSC_HOST_CALL functionHasCustomProperties(ExecState* exec)
2193{
2194 JSValue value = exec->argument(0);
2195 if (value.isObject())
2196 return JSValue::encode(jsBoolean(asObject(value)->hasCustomProperties(exec->vm())));
2197 return JSValue::encode(jsBoolean(false));
2198}
2199
2200EncodedJSValue JSC_HOST_CALL functionDumpTypesForAllVariables(ExecState* exec)
2201{
2202 VM& vm = exec->vm();
2203 vm.dumpTypeProfilerData();
2204 return JSValue::encode(jsUndefined());
2205}
2206
2207EncodedJSValue JSC_HOST_CALL functionDrainMicrotasks(ExecState* exec)
2208{
2209 VM& vm = exec->vm();
2210 vm.drainMicrotasks();
2211 return JSValue::encode(jsUndefined());
2212}
2213
2214EncodedJSValue JSC_HOST_CALL functionReleaseWeakRefs(ExecState* exec)
2215{
2216 VM& vm = exec->vm();
2217 vm.finalizeSynchronousJSExecution();
2218 return JSValue::encode(jsUndefined());
2219}
2220
2221EncodedJSValue JSC_HOST_CALL functionIs32BitPlatform(ExecState*)
2222{
2223#if USE(JSVALUE64)
2224 return JSValue::encode(JSValue(JSC::JSValue::JSFalse));
2225#else
2226 return JSValue::encode(JSValue(JSC::JSValue::JSTrue));
2227#endif
2228}
2229
2230EncodedJSValue JSC_HOST_CALL functionCreateGlobalObject(ExecState* exec)
2231{
2232 VM& vm = exec->vm();
2233 return JSValue::encode(GlobalObject::create(vm, GlobalObject::createStructure(vm, jsNull()), Vector<String>()));
2234}
2235
2236EncodedJSValue JSC_HOST_CALL functionCheckModuleSyntax(ExecState* exec)
2237{
2238 VM& vm = exec->vm();
2239 auto scope = DECLARE_THROW_SCOPE(vm);
2240
2241 String source = exec->argument(0).toWTFString(exec);
2242 RETURN_IF_EXCEPTION(scope, encodedJSValue());
2243
2244 StopWatch stopWatch;
2245 stopWatch.start();
2246
2247 ParserError error;
2248 bool validSyntax = checkModuleSyntax(exec, jscSource(source, { }, URL(), TextPosition(), SourceProviderSourceType::Module), error);
2249 RETURN_IF_EXCEPTION(scope, encodedJSValue());
2250 stopWatch.stop();
2251
2252 if (!validSyntax)
2253 throwException(exec, scope, jsNontrivialString(exec, toString("SyntaxError: ", error.message(), ":", error.line())));
2254 return JSValue::encode(jsNumber(stopWatch.getElapsedMS()));
2255}
2256
2257EncodedJSValue JSC_HOST_CALL functionPlatformSupportsSamplingProfiler(ExecState*)
2258{
2259#if ENABLE(SAMPLING_PROFILER)
2260 return JSValue::encode(JSValue(JSC::JSValue::JSTrue));
2261#else
2262 return JSValue::encode(JSValue(JSC::JSValue::JSFalse));
2263#endif
2264}
2265
2266EncodedJSValue JSC_HOST_CALL functionGenerateHeapSnapshot(ExecState* exec)
2267{
2268 VM& vm = exec->vm();
2269 JSLockHolder lock(vm);
2270 auto scope = DECLARE_THROW_SCOPE(vm);
2271
2272 HeapSnapshotBuilder snapshotBuilder(vm.ensureHeapProfiler());
2273 snapshotBuilder.buildSnapshot();
2274
2275 String jsonString = snapshotBuilder.json();
2276 EncodedJSValue result = JSValue::encode(JSONParse(exec, jsonString));
2277 scope.releaseAssertNoException();
2278 return result;
2279}
2280
2281EncodedJSValue JSC_HOST_CALL functionGenerateHeapSnapshotForGCDebugging(ExecState* exec)
2282{
2283 VM& vm = exec->vm();
2284 JSLockHolder lock(vm);
2285 auto scope = DECLARE_THROW_SCOPE(vm);
2286 String jsonString;
2287 {
2288 DeferGCForAWhile deferGC(vm.heap); // Prevent concurrent GC from interfering with the full GC that the snapshot does.
2289
2290 HeapSnapshotBuilder snapshotBuilder(vm.ensureHeapProfiler(), HeapSnapshotBuilder::SnapshotType::GCDebuggingSnapshot);
2291 snapshotBuilder.buildSnapshot();
2292
2293 jsonString = snapshotBuilder.json();
2294 }
2295 scope.releaseAssertNoException();
2296 return JSValue::encode(jsString(&vm, jsonString));
2297}
2298
2299EncodedJSValue JSC_HOST_CALL functionResetSuperSamplerState(ExecState*)
2300{
2301 resetSuperSamplerState();
2302 return JSValue::encode(jsUndefined());
2303}
2304
2305EncodedJSValue JSC_HOST_CALL functionEnsureArrayStorage(ExecState* exec)
2306{
2307 VM& vm = exec->vm();
2308 for (unsigned i = 0; i < exec->argumentCount(); ++i) {
2309 if (JSObject* object = jsDynamicCast<JSObject*>(vm, exec->argument(i)))
2310 object->ensureArrayStorage(vm);
2311 }
2312 return JSValue::encode(jsUndefined());
2313}
2314
2315#if ENABLE(SAMPLING_PROFILER)
2316EncodedJSValue JSC_HOST_CALL functionStartSamplingProfiler(ExecState* exec)
2317{
2318 VM& vm = exec->vm();
2319 SamplingProfiler& samplingProfiler = vm.ensureSamplingProfiler(WTF::Stopwatch::create());
2320 samplingProfiler.noticeCurrentThreadAsJSCExecutionThread();
2321 samplingProfiler.start();
2322 return JSValue::encode(jsUndefined());
2323}
2324
2325EncodedJSValue JSC_HOST_CALL functionSamplingProfilerStackTraces(ExecState* exec)
2326{
2327 VM& vm = exec->vm();
2328 auto scope = DECLARE_THROW_SCOPE(vm);
2329
2330 if (!vm.samplingProfiler())
2331 return JSValue::encode(throwException(exec, scope, createError(exec, "Sampling profiler was never started"_s)));
2332
2333 String jsonString = vm.samplingProfiler()->stackTracesAsJSON();
2334 EncodedJSValue result = JSValue::encode(JSONParse(exec, jsonString));
2335 scope.releaseAssertNoException();
2336 return result;
2337}
2338#endif // ENABLE(SAMPLING_PROFILER)
2339
2340EncodedJSValue JSC_HOST_CALL functionMaxArguments(ExecState*)
2341{
2342 return JSValue::encode(jsNumber(JSC::maxArguments));
2343}
2344
2345EncodedJSValue JSC_HOST_CALL functionAsyncTestStart(ExecState* exec)
2346{
2347 VM& vm = exec->vm();
2348 auto scope = DECLARE_THROW_SCOPE(vm);
2349
2350 JSValue numberOfAsyncPasses = exec->argument(0);
2351 if (!numberOfAsyncPasses.isUInt32())
2352 return throwVMError(exec, scope, "Expected first argument to a uint32"_s);
2353
2354 asyncTestExpectedPasses += numberOfAsyncPasses.asUInt32();
2355 return encodedJSUndefined();
2356}
2357
2358EncodedJSValue JSC_HOST_CALL functionAsyncTestPassed(ExecState*)
2359{
2360 asyncTestPasses++;
2361 return encodedJSUndefined();
2362}
2363
2364#if ENABLE(WEBASSEMBLY)
2365
2366static EncodedJSValue JSC_HOST_CALL functionWebAssemblyMemoryMode(ExecState* exec)
2367{
2368 VM& vm = exec->vm();
2369 auto scope = DECLARE_THROW_SCOPE(vm);
2370
2371 if (!Wasm::isSupported())
2372 return throwVMTypeError(exec, scope, "WebAssemblyMemoryMode should only be called if the useWebAssembly option is set"_s);
2373
2374 if (JSObject* object = exec->argument(0).getObject()) {
2375 if (auto* memory = jsDynamicCast<JSWebAssemblyMemory*>(vm, object))
2376 return JSValue::encode(jsString(&vm, makeString(memory->memory().mode())));
2377 if (auto* instance = jsDynamicCast<JSWebAssemblyInstance*>(vm, object))
2378 return JSValue::encode(jsString(&vm, makeString(instance->memoryMode())));
2379 }
2380
2381 return throwVMTypeError(exec, scope, "WebAssemblyMemoryMode expects either a WebAssembly.Memory or WebAssembly.Instance"_s);
2382}
2383
2384#endif // ENABLE(WEBASSEMBLY)
2385
2386// Use SEH for Release builds only to get rid of the crash report dialog
2387// (luckily the same tests fail in Release and Debug builds so far). Need to
2388// be in a separate main function because the jscmain function requires object
2389// unwinding.
2390
2391#if COMPILER(MSVC) && !defined(_DEBUG)
2392#define TRY __try {
2393#define EXCEPT(x) } __except (EXCEPTION_EXECUTE_HANDLER) { x; }
2394#else
2395#define TRY
2396#define EXCEPT(x)
2397#endif
2398
2399int jscmain(int argc, char** argv);
2400
2401static double s_desiredTimeout;
2402static double s_timeoutMultiplier = 1.0;
2403
2404static void startTimeoutThreadIfNeeded()
2405{
2406 if (char* timeoutString = getenv("JSCTEST_timeout")) {
2407 if (sscanf(timeoutString, "%lf", &s_desiredTimeout) != 1) {
2408 dataLog("WARNING: timeout string is malformed, got ", timeoutString,
2409 " but expected a number. Not using a timeout.\n");
2410 } else {
2411 Thread::create("jsc Timeout Thread", [] () {
2412 Seconds timeoutDuration(s_desiredTimeout * s_timeoutMultiplier);
2413 sleep(timeoutDuration);
2414 dataLog("Timed out after ", timeoutDuration, " seconds!\n");
2415 CRASH();
2416 });
2417 }
2418 }
2419}
2420
2421int main(int argc, char** argv)
2422{
2423#if PLATFORM(IOS_FAMILY) && CPU(ARM_THUMB2)
2424 // Enabled IEEE754 denormal support.
2425 fenv_t env;
2426 fegetenv( &env );
2427 env.__fpscr &= ~0x01000000u;
2428 fesetenv( &env );
2429#endif
2430
2431#if OS(WINDOWS)
2432 // Cygwin calls ::SetErrorMode(SEM_FAILCRITICALERRORS), which we will inherit. This is bad for
2433 // testing/debugging, as it causes the post-mortem debugger not to be invoked. We reset the
2434 // error mode here to work around Cygwin's behavior. See <http://webkit.org/b/55222>.
2435 ::SetErrorMode(0);
2436
2437 _setmode(_fileno(stdout), _O_BINARY);
2438 _setmode(_fileno(stderr), _O_BINARY);
2439
2440#if defined(_DEBUG)
2441 _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
2442 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
2443 _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
2444 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
2445 _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
2446 _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
2447#endif
2448
2449 timeBeginPeriod(1);
2450#endif
2451
2452#if PLATFORM(GTK)
2453 if (!setlocale(LC_ALL, ""))
2454 WTFLogAlways("Locale not supported by C library.\n\tUsing the fallback 'C' locale.");
2455#endif
2456
2457 // Need to initialize WTF threading before we start any threads. Cannot initialize JSC
2458 // threading yet, since that would do somethings that we'd like to defer until after we
2459 // have a chance to parse options.
2460 WTF::initializeThreading();
2461
2462#if PLATFORM(IOS_FAMILY)
2463 Options::crashIfCantAllocateJITMemory() = true;
2464#endif
2465
2466 // We can't use destructors in the following code because it uses Windows
2467 // Structured Exception Handling
2468 int res = 0;
2469 TRY
2470 res = jscmain(argc, argv);
2471 EXCEPT(res = 3)
2472 finalizeStatsAtEndOfTesting();
2473
2474 jscExit(res);
2475}
2476
2477static void dumpException(GlobalObject* globalObject, JSValue exception)
2478{
2479 VM& vm = globalObject->vm();
2480 auto scope = DECLARE_CATCH_SCOPE(vm);
2481
2482#define CHECK_EXCEPTION() do { \
2483 if (scope.exception()) { \
2484 scope.clearException(); \
2485 return; \
2486 } \
2487 } while (false)
2488
2489 auto exceptionString = exception.toWTFString(globalObject->globalExec());
2490 Expected<CString, UTF8ConversionError> expectedCString = exceptionString.tryGetUtf8();
2491 if (expectedCString)
2492 printf("Exception: %s\n", expectedCString.value().data());
2493 else
2494 printf("Exception: <out of memory while extracting exception string>\n");
2495
2496 Identifier nameID = Identifier::fromString(globalObject->globalExec(), "name");
2497 CHECK_EXCEPTION();
2498 Identifier fileNameID = Identifier::fromString(globalObject->globalExec(), "sourceURL");
2499 CHECK_EXCEPTION();
2500 Identifier lineNumberID = Identifier::fromString(globalObject->globalExec(), "line");
2501 CHECK_EXCEPTION();
2502 Identifier stackID = Identifier::fromString(globalObject->globalExec(), "stack");
2503 CHECK_EXCEPTION();
2504
2505 JSValue nameValue = exception.get(globalObject->globalExec(), nameID);
2506 CHECK_EXCEPTION();
2507 JSValue fileNameValue = exception.get(globalObject->globalExec(), fileNameID);
2508 CHECK_EXCEPTION();
2509 JSValue lineNumberValue = exception.get(globalObject->globalExec(), lineNumberID);
2510 CHECK_EXCEPTION();
2511 JSValue stackValue = exception.get(globalObject->globalExec(), stackID);
2512 CHECK_EXCEPTION();
2513
2514 if (nameValue.toWTFString(globalObject->globalExec()) == "SyntaxError"
2515 && (!fileNameValue.isUndefinedOrNull() || !lineNumberValue.isUndefinedOrNull())) {
2516 printf(
2517 "at %s:%s\n",
2518 fileNameValue.toWTFString(globalObject->globalExec()).utf8().data(),
2519 lineNumberValue.toWTFString(globalObject->globalExec()).utf8().data());
2520 }
2521
2522 if (!stackValue.isUndefinedOrNull()) {
2523 auto stackString = stackValue.toWTFString(globalObject->globalExec());
2524 if (stackString.length())
2525 printf("%s\n", stackString.utf8().data());
2526 }
2527
2528#undef CHECK_EXCEPTION
2529}
2530
2531static bool checkUncaughtException(VM& vm, GlobalObject* globalObject, JSValue exception, CommandLine& options)
2532{
2533 const String& expectedExceptionName = options.m_uncaughtExceptionName;
2534 auto scope = DECLARE_CATCH_SCOPE(vm);
2535 scope.clearException();
2536 if (!exception) {
2537 printf("Expected uncaught exception with name '%s' but none was thrown\n", expectedExceptionName.utf8().data());
2538 return false;
2539 }
2540
2541 ExecState* exec = globalObject->globalExec();
2542 JSValue exceptionClass = globalObject->get(exec, Identifier::fromString(exec, expectedExceptionName));
2543 if (!exceptionClass.isObject() || scope.exception()) {
2544 printf("Expected uncaught exception with name '%s' but given exception class is not defined\n", expectedExceptionName.utf8().data());
2545 return false;
2546 }
2547
2548 bool isInstanceOfExpectedException = jsCast<JSObject*>(exceptionClass)->hasInstance(exec, exception);
2549 if (scope.exception()) {
2550 printf("Expected uncaught exception with name '%s' but given exception class fails performing hasInstance\n", expectedExceptionName.utf8().data());
2551 return false;
2552 }
2553 if (isInstanceOfExpectedException) {
2554 if (options.m_alwaysDumpUncaughtException)
2555 dumpException(globalObject, exception);
2556 return true;
2557 }
2558
2559 printf("Expected uncaught exception with name '%s' but exception value is not instance of this exception class\n", expectedExceptionName.utf8().data());
2560 dumpException(globalObject, exception);
2561 return false;
2562}
2563
2564static void checkException(ExecState* exec, GlobalObject* globalObject, bool isLastFile, bool hasException, JSValue value, CommandLine& options, bool& success)
2565{
2566 VM& vm = globalObject->vm();
2567
2568 if (options.m_treatWatchdogExceptionAsSuccess && value.inherits<TerminatedExecutionError>(vm)) {
2569 ASSERT(hasException);
2570 return;
2571 }
2572
2573 if (!options.m_uncaughtExceptionName || !isLastFile) {
2574 success = success && !hasException;
2575 if (options.m_dump && !hasException)
2576 printf("End: %s\n", value.toWTFString(exec).utf8().data());
2577 if (hasException)
2578 dumpException(globalObject, value);
2579 } else
2580 success = success && checkUncaughtException(vm, globalObject, (hasException) ? value : JSValue(), options);
2581}
2582
2583static void runWithOptions(GlobalObject* globalObject, CommandLine& options, bool& success)
2584{
2585 Vector<Script>& scripts = options.m_scripts;
2586 String fileName;
2587 Vector<char> scriptBuffer;
2588
2589 if (options.m_dump)
2590 JSC::Options::dumpGeneratedBytecodes() = true;
2591
2592 VM& vm = globalObject->vm();
2593 auto scope = DECLARE_CATCH_SCOPE(vm);
2594
2595#if ENABLE(SAMPLING_FLAGS)
2596 SamplingFlags::start();
2597#endif
2598
2599 for (size_t i = 0; i < scripts.size(); i++) {
2600 JSInternalPromise* promise = nullptr;
2601 bool isModule = options.m_module || scripts[i].scriptType == Script::ScriptType::Module;
2602 if (scripts[i].codeSource == Script::CodeSource::File) {
2603 fileName = scripts[i].argument;
2604 if (scripts[i].strictMode == Script::StrictMode::Strict)
2605 scriptBuffer.append("\"use strict\";\n", strlen("\"use strict\";\n"));
2606
2607 if (isModule) {
2608 promise = loadAndEvaluateModule(globalObject->globalExec(), fileName, jsUndefined(), jsUndefined());
2609 scope.releaseAssertNoException();
2610 } else {
2611 if (!fetchScriptFromLocalFileSystem(fileName, scriptBuffer)) {
2612 success = false; // fail early so we can catch missing files
2613 return;
2614 }
2615 }
2616 } else {
2617 size_t commandLineLength = strlen(scripts[i].argument);
2618 scriptBuffer.resize(commandLineLength);
2619 std::copy(scripts[i].argument, scripts[i].argument + commandLineLength, scriptBuffer.begin());
2620 fileName = "[Command Line]"_s;
2621 }
2622
2623 bool isLastFile = i == scripts.size() - 1;
2624 if (isModule) {
2625 if (!promise) {
2626 // FIXME: This should use an absolute file URL https://bugs.webkit.org/show_bug.cgi?id=193077
2627 promise = loadAndEvaluateModule(globalObject->globalExec(), jscSource(stringFromUTF(scriptBuffer), SourceOrigin { absolutePath(fileName) }, URL({ }, fileName), TextPosition(), SourceProviderSourceType::Module), jsUndefined());
2628 }
2629 scope.clearException();
2630
2631 JSFunction* fulfillHandler = JSNativeStdFunction::create(vm, globalObject, 1, String(), [&success, &options, isLastFile](ExecState* exec) {
2632 checkException(exec, jsCast<GlobalObject*>(exec->lexicalGlobalObject()), isLastFile, false, exec->argument(0), options, success);
2633 return JSValue::encode(jsUndefined());
2634 });
2635
2636 JSFunction* rejectHandler = JSNativeStdFunction::create(vm, globalObject, 1, String(), [&success, &options, isLastFile](ExecState* exec) {
2637 checkException(exec, jsCast<GlobalObject*>(exec->lexicalGlobalObject()), isLastFile, true, exec->argument(0), options, success);
2638 return JSValue::encode(jsUndefined());
2639 });
2640
2641 promise->then(globalObject->globalExec(), fulfillHandler, rejectHandler);
2642 scope.releaseAssertNoException();
2643 vm.drainMicrotasks();
2644 } else {
2645 NakedPtr<Exception> evaluationException;
2646 JSValue returnValue = evaluate(globalObject->globalExec(), jscSource(scriptBuffer, SourceOrigin { absolutePath(fileName) }, fileName), JSValue(), evaluationException);
2647 scope.assertNoException();
2648 if (evaluationException)
2649 returnValue = evaluationException->value();
2650 checkException(globalObject->globalExec(), globalObject, isLastFile, evaluationException, returnValue, options, success);
2651 }
2652
2653 scriptBuffer.clear();
2654 scope.clearException();
2655 }
2656
2657#if ENABLE(REGEXP_TRACING)
2658 vm.dumpRegExpTrace();
2659#endif
2660}
2661
2662#define RUNNING_FROM_XCODE 0
2663
2664static void runInteractive(GlobalObject* globalObject)
2665{
2666 VM& vm = globalObject->vm();
2667 auto scope = DECLARE_CATCH_SCOPE(vm);
2668
2669 Optional<DirectoryName> directoryName = currentWorkingDirectory();
2670 if (!directoryName)
2671 return;
2672 SourceOrigin sourceOrigin(resolvePath(directoryName.value(), ModuleName("interpreter")));
2673
2674 bool shouldQuit = false;
2675 while (!shouldQuit) {
2676#if HAVE(READLINE) && !RUNNING_FROM_XCODE
2677 ParserError error;
2678 String source;
2679 do {
2680 error = ParserError();
2681 char* line = readline(source.isEmpty() ? interactivePrompt : "... ");
2682 shouldQuit = !line;
2683 if (!line)
2684 break;
2685 source = source + String::fromUTF8(line);
2686 source = source + '\n';
2687 checkSyntax(vm, jscSource(source, sourceOrigin), error);
2688 if (!line[0]) {
2689 free(line);
2690 break;
2691 }
2692 add_history(line);
2693 free(line);
2694 } while (error.syntaxErrorType() == ParserError::SyntaxErrorRecoverable);
2695
2696 if (error.isValid()) {
2697 printf("%s:%d\n", error.message().utf8().data(), error.line());
2698 continue;
2699 }
2700
2701
2702 NakedPtr<Exception> evaluationException;
2703 JSValue returnValue = evaluate(globalObject->globalExec(), jscSource(source, sourceOrigin), JSValue(), evaluationException);
2704#else
2705 printf("%s", interactivePrompt);
2706 Vector<char, 256> line;
2707 int c;
2708 while ((c = getchar()) != EOF) {
2709 // FIXME: Should we also break on \r?
2710 if (c == '\n')
2711 break;
2712 line.append(c);
2713 }
2714 if (line.isEmpty())
2715 break;
2716
2717 NakedPtr<Exception> evaluationException;
2718 JSValue returnValue = evaluate(globalObject->globalExec(), jscSource(line, sourceOrigin, sourceOrigin.string()), JSValue(), evaluationException);
2719#endif
2720 if (evaluationException)
2721 printf("Exception: %s\n", evaluationException->value().toWTFString(globalObject->globalExec()).utf8().data());
2722 else
2723 printf("%s\n", returnValue.toWTFString(globalObject->globalExec()).utf8().data());
2724
2725 scope.clearException();
2726 vm.drainMicrotasks();
2727 }
2728 printf("\n");
2729}
2730
2731static NO_RETURN void printUsageStatement(bool help = false)
2732{
2733 fprintf(stderr, "Usage: jsc [options] [files] [-- arguments]\n");
2734 fprintf(stderr, " -d Dumps bytecode (debug builds only)\n");
2735 fprintf(stderr, " -e Evaluate argument as script code\n");
2736 fprintf(stderr, " -f Specifies a source file (deprecated)\n");
2737 fprintf(stderr, " -h|--help Prints this help message\n");
2738 fprintf(stderr, " -i Enables interactive mode (default if no files are specified)\n");
2739 fprintf(stderr, " -m Execute as a module\n");
2740#if HAVE(SIGNAL_H)
2741 fprintf(stderr, " -s Installs signal handlers that exit on a crash (Unix platforms only)\n");
2742#endif
2743 fprintf(stderr, " -p <file> Outputs profiling data to a file\n");
2744 fprintf(stderr, " -x Output exit code before terminating\n");
2745 fprintf(stderr, "\n");
2746 fprintf(stderr, " --sample Collects and outputs sampling profiler data\n");
2747 fprintf(stderr, " --test262-async Check that some script calls the print function with the string 'Test262:AsyncTestComplete'\n");
2748 fprintf(stderr, " --strict-file=<file> Parse the given file as if it were in strict mode (this option may be passed more than once)\n");
2749 fprintf(stderr, " --module-file=<file> Parse and evaluate the given file as module (this option may be passed more than once)\n");
2750 fprintf(stderr, " --exception=<name> Check the last script exits with an uncaught exception with the specified name\n");
2751 fprintf(stderr, " --watchdog-exception-ok Uncaught watchdog exceptions exit with success\n");
2752 fprintf(stderr, " --dumpException Dump uncaught exception text\n");
2753 fprintf(stderr, " --footprint Dump memory footprint after done executing\n");
2754 fprintf(stderr, " --options Dumps all JSC VM options and exits\n");
2755 fprintf(stderr, " --dumpOptions Dumps all non-default JSC VM options before continuing\n");
2756 fprintf(stderr, " --<jsc VM option>=<value> Sets the specified JSC VM option\n");
2757 fprintf(stderr, " --destroy-vm Destroy VM before exiting\n");
2758 fprintf(stderr, "\n");
2759 fprintf(stderr, "Files with a .mjs extension will always be evaluated as modules.\n");
2760 fprintf(stderr, "\n");
2761
2762 jscExit(help ? EXIT_SUCCESS : EXIT_FAILURE);
2763}
2764
2765static bool isMJSFile(char *filename)
2766{
2767 filename = strrchr(filename, '.');
2768
2769 if (filename)
2770 return !strcmp(filename, ".mjs");
2771
2772 return false;
2773}
2774
2775void CommandLine::parseArguments(int argc, char** argv)
2776{
2777 Options::initialize();
2778
2779 if (Options::dumpOptions()) {
2780 printf("Command line:");
2781#if PLATFORM(COCOA)
2782 for (char** envp = *_NSGetEnviron(); *envp; envp++) {
2783 const char* env = *envp;
2784 if (!strncmp("JSC_", env, 4))
2785 printf(" %s", env);
2786 }
2787#endif // PLATFORM(COCOA)
2788 for (int i = 0; i < argc; ++i)
2789 printf(" %s", argv[i]);
2790 printf("\n");
2791 }
2792
2793 int i = 1;
2794 JSC::Options::DumpLevel dumpOptionsLevel = JSC::Options::DumpLevel::None;
2795 bool needToExit = false;
2796
2797 bool hasBadJSCOptions = false;
2798 for (; i < argc; ++i) {
2799 const char* arg = argv[i];
2800 if (!strcmp(arg, "-f")) {
2801 if (++i == argc)
2802 printUsageStatement();
2803 m_scripts.append(Script(Script::StrictMode::Sloppy, Script::CodeSource::File, Script::ScriptType::Script, argv[i]));
2804 continue;
2805 }
2806 if (!strcmp(arg, "-e")) {
2807 if (++i == argc)
2808 printUsageStatement();
2809 m_scripts.append(Script(Script::StrictMode::Sloppy, Script::CodeSource::CommandLine, Script::ScriptType::Script, argv[i]));
2810 continue;
2811 }
2812 if (!strcmp(arg, "-i")) {
2813 m_interactive = true;
2814 continue;
2815 }
2816 if (!strcmp(arg, "-d")) {
2817 m_dump = true;
2818 continue;
2819 }
2820 if (!strcmp(arg, "-p")) {
2821 if (++i == argc)
2822 printUsageStatement();
2823 m_profile = true;
2824 m_profilerOutput = argv[i];
2825 continue;
2826 }
2827 if (!strcmp(arg, "-m")) {
2828 m_module = true;
2829 continue;
2830 }
2831 if (!strcmp(arg, "-s")) {
2832#if HAVE(SIGNAL_H)
2833 signal(SIGILL, _exit);
2834 signal(SIGFPE, _exit);
2835 signal(SIGBUS, _exit);
2836 signal(SIGSEGV, _exit);
2837#endif
2838 continue;
2839 }
2840 if (!strcmp(arg, "-x")) {
2841 m_exitCode = true;
2842 continue;
2843 }
2844 if (!strcmp(arg, "--")) {
2845 ++i;
2846 break;
2847 }
2848 if (!strcmp(arg, "-h") || !strcmp(arg, "--help"))
2849 printUsageStatement(true);
2850
2851 if (!strcmp(arg, "--options")) {
2852 dumpOptionsLevel = JSC::Options::DumpLevel::Verbose;
2853 needToExit = true;
2854 continue;
2855 }
2856 if (!strcmp(arg, "--dumpOptions")) {
2857 dumpOptionsLevel = JSC::Options::DumpLevel::Overridden;
2858 continue;
2859 }
2860 if (!strcmp(arg, "--sample")) {
2861 JSC::Options::useSamplingProfiler() = true;
2862 JSC::Options::collectSamplingProfilerDataForJSCShell() = true;
2863 m_dumpSamplingProfilerData = true;
2864 continue;
2865 }
2866 if (!strcmp(arg, "--destroy-vm")) {
2867 m_destroyVM = true;
2868 continue;
2869 }
2870
2871 static const char* timeoutMultiplierOptStr = "--timeoutMultiplier=";
2872 static const unsigned timeoutMultiplierOptStrLength = strlen(timeoutMultiplierOptStr);
2873 if (!strncmp(arg, timeoutMultiplierOptStr, timeoutMultiplierOptStrLength)) {
2874 const char* valueStr = &arg[timeoutMultiplierOptStrLength];
2875 if (sscanf(valueStr, "%lf", &s_timeoutMultiplier) != 1)
2876 dataLog("WARNING: --timeoutMultiplier=", valueStr, " is invalid. Expects a numeric ratio.\n");
2877 continue;
2878 }
2879
2880 if (!strcmp(arg, "--test262-async")) {
2881 asyncTestExpectedPasses++;
2882 continue;
2883 }
2884
2885 if (!strcmp(arg, "--remote-debug")) {
2886 m_enableRemoteDebugging = true;
2887 continue;
2888 }
2889
2890 static const unsigned strictFileStrLength = strlen("--strict-file=");
2891 if (!strncmp(arg, "--strict-file=", strictFileStrLength)) {
2892 m_scripts.append(Script(Script::StrictMode::Strict, Script::CodeSource::File, Script::ScriptType::Script, argv[i] + strictFileStrLength));
2893 continue;
2894 }
2895
2896 static const unsigned moduleFileStrLength = strlen("--module-file=");
2897 if (!strncmp(arg, "--module-file=", moduleFileStrLength)) {
2898 m_scripts.append(Script(Script::StrictMode::Sloppy, Script::CodeSource::File, Script::ScriptType::Module, argv[i] + moduleFileStrLength));
2899 continue;
2900 }
2901
2902 if (!strcmp(arg, "--dumpException")) {
2903 m_alwaysDumpUncaughtException = true;
2904 continue;
2905 }
2906
2907 if (!strcmp(arg, "--footprint")) {
2908 m_dumpMemoryFootprint = true;
2909 continue;
2910 }
2911
2912 static const unsigned exceptionStrLength = strlen("--exception=");
2913 if (!strncmp(arg, "--exception=", exceptionStrLength)) {
2914 m_uncaughtExceptionName = String(arg + exceptionStrLength);
2915 continue;
2916 }
2917
2918 if (!strcmp(arg, "--watchdog-exception-ok")) {
2919 m_treatWatchdogExceptionAsSuccess = true;
2920 continue;
2921 }
2922
2923 // See if the -- option is a JSC VM option.
2924 if (strstr(arg, "--") == arg) {
2925 if (!JSC::Options::setOption(&arg[2])) {
2926 hasBadJSCOptions = true;
2927 dataLog("ERROR: invalid option: ", arg, "\n");
2928 }
2929 continue;
2930 }
2931
2932 // This arg is not recognized by the VM nor by jsc. Pass it on to the
2933 // script.
2934 Script::ScriptType scriptType = isMJSFile(argv[i]) ? Script::ScriptType::Module : Script::ScriptType::Script;
2935 m_scripts.append(Script(Script::StrictMode::Sloppy, Script::CodeSource::File, scriptType, argv[i]));
2936 }
2937
2938 if (hasBadJSCOptions && JSC::Options::validateOptions())
2939 CRASH();
2940
2941 if (m_scripts.isEmpty())
2942 m_interactive = true;
2943
2944 for (; i < argc; ++i)
2945 m_arguments.append(argv[i]);
2946
2947 if (dumpOptionsLevel != JSC::Options::DumpLevel::None) {
2948 const char* optionsTitle = (dumpOptionsLevel == JSC::Options::DumpLevel::Overridden)
2949 ? "Modified JSC runtime options:"
2950 : "All JSC runtime options:";
2951 JSC::Options::dumpAllOptions(stderr, dumpOptionsLevel, optionsTitle);
2952 }
2953 JSC::Options::ensureOptionsAreCoherent();
2954 if (needToExit)
2955 jscExit(EXIT_SUCCESS);
2956}
2957
2958template<typename Func>
2959int runJSC(const CommandLine& options, bool isWorker, const Func& func)
2960{
2961 Worker worker(Workers::singleton());
2962
2963 VM& vm = VM::create(LargeHeap).leakRef();
2964 int result;
2965 bool success = true;
2966 GlobalObject* globalObject = nullptr;
2967 {
2968 JSLockHolder locker(vm);
2969
2970 if (options.m_profile && !vm.m_perBytecodeProfiler)
2971 vm.m_perBytecodeProfiler = std::make_unique<Profiler::Database>(vm);
2972
2973 globalObject = GlobalObject::create(vm, GlobalObject::createStructure(vm, jsNull()), options.m_arguments);
2974 globalObject->setRemoteDebuggingEnabled(options.m_enableRemoteDebugging);
2975 func(vm, globalObject, success);
2976 vm.drainMicrotasks();
2977 }
2978 vm.promiseDeferredTimer->runRunLoop();
2979 {
2980 JSLockHolder locker(vm);
2981 if (options.m_interactive && success)
2982 runInteractive(globalObject);
2983 }
2984
2985 result = success && (asyncTestExpectedPasses == asyncTestPasses) ? 0 : 3;
2986
2987 if (options.m_exitCode) {
2988 printf("jsc exiting %d", result);
2989 if (asyncTestExpectedPasses != asyncTestPasses)
2990 printf(" because expected: %d async test passes but got: %d async test passes", asyncTestExpectedPasses, asyncTestPasses);
2991 printf("\n");
2992 }
2993
2994 if (options.m_profile) {
2995 JSLockHolder locker(vm);
2996 if (!vm.m_perBytecodeProfiler->save(options.m_profilerOutput.utf8().data()))
2997 fprintf(stderr, "could not save profiler output.\n");
2998 }
2999
3000#if ENABLE(JIT)
3001 {
3002 JSLockHolder locker(vm);
3003 if (Options::useExceptionFuzz())
3004 printf("JSC EXCEPTION FUZZ: encountered %u checks.\n", numberOfExceptionFuzzChecks());
3005 bool fireAtEnabled =
3006 Options::fireExecutableAllocationFuzzAt() || Options::fireExecutableAllocationFuzzAtOrAfter();
3007 if (Options::useExecutableAllocationFuzz() && (!fireAtEnabled || Options::verboseExecutableAllocationFuzz()))
3008 printf("JSC EXECUTABLE ALLOCATION FUZZ: encountered %u checks.\n", numberOfExecutableAllocationFuzzChecks());
3009 if (Options::useOSRExitFuzz()) {
3010 printf("JSC OSR EXIT FUZZ: encountered %u static checks.\n", numberOfStaticOSRExitFuzzChecks());
3011 printf("JSC OSR EXIT FUZZ: encountered %u dynamic checks.\n", numberOfOSRExitFuzzChecks());
3012 }
3013
3014
3015 auto compileTimeStats = JIT::compileTimeStats();
3016 Vector<CString> compileTimeKeys;
3017 for (auto& entry : compileTimeStats)
3018 compileTimeKeys.append(entry.key);
3019 std::sort(compileTimeKeys.begin(), compileTimeKeys.end());
3020 for (const CString& key : compileTimeKeys)
3021 printf("%40s: %.3lf ms\n", key.data(), compileTimeStats.get(key).milliseconds());
3022 }
3023#endif
3024
3025 if (Options::gcAtEnd()) {
3026 // We need to hold the API lock to do a GC.
3027 JSLockHolder locker(&vm);
3028 vm.heap.collectNow(Sync, CollectionScope::Full);
3029 }
3030
3031 if (options.m_dumpSamplingProfilerData) {
3032#if ENABLE(SAMPLING_PROFILER)
3033 JSLockHolder locker(&vm);
3034 vm.samplingProfiler()->reportTopFunctions();
3035 vm.samplingProfiler()->reportTopBytecodes();
3036#else
3037 dataLog("Sampling profiler is not enabled on this platform\n");
3038#endif
3039 }
3040
3041 vm.codeCache()->write(vm);
3042
3043 if (options.m_destroyVM || isWorker) {
3044 JSLockHolder locker(vm);
3045 // This is needed because we don't want the worker's main
3046 // thread to die before its compilation threads finish.
3047 vm.deref();
3048 }
3049
3050 return result;
3051}
3052
3053int jscmain(int argc, char** argv)
3054{
3055 // Need to override and enable restricted options before we start parsing options below.
3056 Options::enableRestrictedOptions(true);
3057
3058 // Note that the options parsing can affect VM creation, and thus
3059 // comes first.
3060 CommandLine options(argc, argv);
3061
3062 processConfigFile(Options::configFile(), "jsc");
3063
3064 // Initialize JSC before getting VM.
3065 WTF::initializeMainThread();
3066 JSC::initializeThreading();
3067 startTimeoutThreadIfNeeded();
3068#if ENABLE(WEBASSEMBLY)
3069 JSC::Wasm::enableFastMemory();
3070#endif
3071 Gigacage::disableDisablingPrimitiveGigacageIfShouldBeEnabled();
3072
3073#if PLATFORM(COCOA)
3074 auto& memoryPressureHandler = MemoryPressureHandler::singleton();
3075 {
3076 dispatch_queue_t queue = dispatch_queue_create("jsc shell memory pressure handler", DISPATCH_QUEUE_SERIAL);
3077 memoryPressureHandler.setDispatchQueue(queue);
3078 dispatch_release(queue);
3079 }
3080 Box<Critical> memoryPressureCriticalState = Box<Critical>::create(Critical::No);
3081 Box<Synchronous> memoryPressureSynchronousState = Box<Synchronous>::create(Synchronous::No);
3082 memoryPressureHandler.setLowMemoryHandler([=] (Critical critical, Synchronous synchronous) {
3083 // We set these racily with respect to reading them from the JS execution thread.
3084 *memoryPressureCriticalState = critical;
3085 *memoryPressureSynchronousState = synchronous;
3086 });
3087 memoryPressureHandler.setShouldLogMemoryMemoryPressureEvents(false);
3088 memoryPressureHandler.install();
3089
3090 auto onEachMicrotaskTick = [&] (VM& vm) {
3091 if (*memoryPressureCriticalState == Critical::No)
3092 return;
3093
3094 *memoryPressureCriticalState = Critical::No;
3095 bool isSynchronous = *memoryPressureSynchronousState == Synchronous::Yes;
3096
3097 WTF::releaseFastMallocFreeMemory();
3098 vm.deleteAllCode(DeleteAllCodeIfNotCollecting);
3099
3100 if (!vm.heap.isCurrentThreadBusy()) {
3101 if (isSynchronous) {
3102 vm.heap.collectNow(Sync, CollectionScope::Full);
3103 WTF::releaseFastMallocFreeMemory();
3104 } else
3105 vm.heap.collectNowFullIfNotDoneRecently(Async);
3106 }
3107 };
3108#endif
3109
3110 int result = runJSC(
3111 options, false,
3112 [&] (VM& vm, GlobalObject* globalObject, bool& success) {
3113 UNUSED_PARAM(vm);
3114#if PLATFORM(COCOA)
3115 vm.setOnEachMicrotaskTick(WTFMove(onEachMicrotaskTick));
3116#endif
3117 runWithOptions(globalObject, options, success);
3118 });
3119
3120 printSuperSamplerState();
3121
3122 if (options.m_dumpMemoryFootprint) {
3123 MemoryFootprint footprint = MemoryFootprint::now();
3124
3125 printf("Memory Footprint:\n Current Footprint: %" PRIu64 "\n Peak Footprint: %" PRIu64 "\n", footprint.current, footprint.peak);
3126 }
3127
3128 return result;
3129}
3130
3131#if OS(WINDOWS)
3132extern "C" __declspec(dllexport) int WINAPI dllLauncherEntryPoint(int argc, const char* argv[])
3133{
3134 return main(argc, const_cast<char**>(argv));
3135}
3136#endif
3137