1/*
2 * Copyright (C) 2011-2019 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "Options.h"
28
29#include "AssemblerCommon.h"
30#include "CPU.h"
31#include "LLIntCommon.h"
32#include "MinimumReservedZoneSize.h"
33#include "SigillCrashAnalyzer.h"
34#include <algorithm>
35#include <limits>
36#include <math.h>
37#include <mutex>
38#include <stdlib.h>
39#include <string.h>
40#include <wtf/ASCIICType.h>
41#include <wtf/Compiler.h>
42#include <wtf/DataLog.h>
43#include <wtf/NumberOfCores.h>
44#include <wtf/PointerPreparations.h>
45#include <wtf/StdLibExtras.h>
46#include <wtf/text/StringBuilder.h>
47#include <wtf/threads/Signals.h>
48
49#if PLATFORM(COCOA)
50#include <crt_externs.h>
51#endif
52
53#if ENABLE(JIT)
54#include "MacroAssembler.h"
55#endif
56
57namespace JSC {
58
59namespace {
60#ifdef NDEBUG
61bool restrictedOptionsEnabled = false;
62#else
63bool restrictedOptionsEnabled = true;
64#endif
65}
66
67void Options::enableRestrictedOptions(bool enableOrNot)
68{
69 restrictedOptionsEnabled = enableOrNot;
70}
71
72static bool parse(const char* string, bool& value)
73{
74 if (equalLettersIgnoringASCIICase(string, "true") || equalLettersIgnoringASCIICase(string, "yes") || !strcmp(string, "1")) {
75 value = true;
76 return true;
77 }
78 if (equalLettersIgnoringASCIICase(string, "false") || equalLettersIgnoringASCIICase(string, "no") || !strcmp(string, "0")) {
79 value = false;
80 return true;
81 }
82 return false;
83}
84
85static bool parse(const char* string, int32_t& value)
86{
87 return sscanf(string, "%d", &value) == 1;
88}
89
90static bool parse(const char* string, unsigned& value)
91{
92 return sscanf(string, "%u", &value) == 1;
93}
94
95static bool UNUSED_FUNCTION parse(const char* string, unsigned long& value)
96{
97 return sscanf(string, "%lu", &value);
98}
99
100static bool UNUSED_FUNCTION parse(const char* string, unsigned long long& value)
101{
102 return sscanf(string, "%llu", &value);
103}
104
105static bool parse(const char* string, double& value)
106{
107 return sscanf(string, "%lf", &value) == 1;
108}
109
110static bool parse(const char* string, OptionRange& value)
111{
112 return value.init(string);
113}
114
115static bool parse(const char* string, const char*& value)
116{
117 if (!strlen(string)) {
118 value = nullptr;
119 return true;
120 }
121
122 // FIXME <https://webkit.org/b/169057>: This could leak if this option is set more than once.
123 // Given that Options are typically used for testing, this isn't considered to be a problem.
124 value = WTF::fastStrDup(string);
125 return true;
126}
127
128static bool parse(const char* string, GCLogging::Level& value)
129{
130 if (equalLettersIgnoringASCIICase(string, "none") || equalLettersIgnoringASCIICase(string, "no") || equalLettersIgnoringASCIICase(string, "false") || !strcmp(string, "0")) {
131 value = GCLogging::None;
132 return true;
133 }
134
135 if (equalLettersIgnoringASCIICase(string, "basic") || equalLettersIgnoringASCIICase(string, "yes") || equalLettersIgnoringASCIICase(string, "true") || !strcmp(string, "1")) {
136 value = GCLogging::Basic;
137 return true;
138 }
139
140 if (equalLettersIgnoringASCIICase(string, "verbose") || !strcmp(string, "2")) {
141 value = GCLogging::Verbose;
142 return true;
143 }
144
145 return false;
146}
147
148bool Options::isAvailable(Options::ID id, Options::Availability availability)
149{
150 if (availability == Availability::Restricted)
151 return restrictedOptionsEnabled;
152 ASSERT(availability == Availability::Configurable);
153
154 UNUSED_PARAM(id);
155#if !defined(NDEBUG)
156 if (id == maxSingleAllocationSizeID)
157 return true;
158#endif
159#if OS(DARWIN)
160 if (id == useSigillCrashAnalyzerID)
161 return true;
162#endif
163#if ENABLE(ASSEMBLER) && OS(LINUX)
164 if (id == logJITCodeForPerfID)
165 return true;
166#endif
167 if (id == traceLLIntExecutionID)
168 return !!LLINT_TRACING;
169 if (id == traceLLIntSlowPathID)
170 return !!LLINT_TRACING;
171 return false;
172}
173
174template<typename T>
175bool overrideOptionWithHeuristic(T& variable, Options::ID id, const char* name, Options::Availability availability)
176{
177 bool available = (availability == Options::Availability::Normal)
178 || Options::isAvailable(id, availability);
179
180 const char* stringValue = getenv(name);
181 if (!stringValue)
182 return false;
183
184 if (available && parse(stringValue, variable))
185 return true;
186
187 fprintf(stderr, "WARNING: failed to parse %s=%s\n", name, stringValue);
188 return false;
189}
190
191bool Options::overrideAliasedOptionWithHeuristic(const char* name)
192{
193 const char* stringValue = getenv(name);
194 if (!stringValue)
195 return false;
196
197 String aliasedOption;
198 aliasedOption = String(&name[4]) + "=" + stringValue;
199 if (Options::setOption(aliasedOption.utf8().data()))
200 return true;
201
202 fprintf(stderr, "WARNING: failed to parse %s=%s\n", name, stringValue);
203 return false;
204}
205
206static unsigned computeNumberOfWorkerThreads(int maxNumberOfWorkerThreads, int minimum = 1)
207{
208 int cpusToUse = std::min(kernTCSMAwareNumberOfProcessorCores(), maxNumberOfWorkerThreads);
209
210 // Be paranoid, it is the OS we're dealing with, after all.
211 ASSERT(cpusToUse >= 1);
212 return std::max(cpusToUse, minimum);
213}
214
215static int32_t computePriorityDeltaOfWorkerThreads(int32_t twoCorePriorityDelta, int32_t multiCorePriorityDelta)
216{
217 if (kernTCSMAwareNumberOfProcessorCores() <= 2)
218 return twoCorePriorityDelta;
219
220 return multiCorePriorityDelta;
221}
222
223static bool jitEnabledByDefault()
224{
225 return is32Bit() || isAddress64Bit();
226}
227
228static unsigned computeNumberOfGCMarkers(unsigned maxNumberOfGCMarkers)
229{
230 return computeNumberOfWorkerThreads(maxNumberOfGCMarkers);
231}
232
233const char* const OptionRange::s_nullRangeStr = "<null>";
234
235bool OptionRange::init(const char* rangeString)
236{
237 // rangeString should be in the form of [!]<low>[:<high>]
238 // where low and high are unsigned
239
240 bool invert = false;
241
242 if (!rangeString) {
243 m_state = InitError;
244 return false;
245 }
246
247 if (!strcmp(rangeString, s_nullRangeStr)) {
248 m_state = Uninitialized;
249 return true;
250 }
251
252 const char* p = rangeString;
253
254 if (*p == '!') {
255 invert = true;
256 p++;
257 }
258
259 int scanResult = sscanf(p, " %u:%u", &m_lowLimit, &m_highLimit);
260
261 if (!scanResult || scanResult == EOF) {
262 m_state = InitError;
263 return false;
264 }
265
266 if (scanResult == 1)
267 m_highLimit = m_lowLimit;
268
269 if (m_lowLimit > m_highLimit) {
270 m_state = InitError;
271 return false;
272 }
273
274 // FIXME <https://webkit.org/b/169057>: This could leak if this particular option is set more than once.
275 // Given that these options are used for testing, this isn't considered to be problem.
276 m_rangeString = WTF::fastStrDup(rangeString);
277 m_state = invert ? Inverted : Normal;
278
279 return true;
280}
281
282bool OptionRange::isInRange(unsigned count)
283{
284 if (m_state < Normal)
285 return true;
286
287 if ((m_lowLimit <= count) && (count <= m_highLimit))
288 return m_state == Normal ? true : false;
289
290 return m_state == Normal ? false : true;
291}
292
293void OptionRange::dump(PrintStream& out) const
294{
295 out.print(m_rangeString);
296}
297
298Options::Entry Options::s_options[Options::numberOfOptions];
299Options::Entry Options::s_defaultOptions[Options::numberOfOptions];
300
301// Realize the names for each of the options:
302const Options::EntryInfo Options::s_optionsInfo[Options::numberOfOptions] = {
303#define FOR_EACH_OPTION(type_, name_, defaultValue_, availability_, description_) \
304 { #name_, description_, Options::Type::type_##Type, Availability::availability_ },
305 JSC_OPTIONS(FOR_EACH_OPTION)
306#undef FOR_EACH_OPTION
307};
308
309static void scaleJITPolicy()
310{
311 auto& scaleFactor = Options::jitPolicyScale();
312 if (scaleFactor > 1.0)
313 scaleFactor = 1.0;
314 else if (scaleFactor < 0.0)
315 scaleFactor = 0.0;
316
317 struct OptionToScale {
318 Options::ID id;
319 int32_t minVal;
320 };
321
322 static const OptionToScale optionsToScale[] = {
323 { Options::thresholdForJITAfterWarmUpID, 0 },
324 { Options::thresholdForJITSoonID, 0 },
325 { Options::thresholdForOptimizeAfterWarmUpID, 1 },
326 { Options::thresholdForOptimizeAfterLongWarmUpID, 1 },
327 { Options::thresholdForOptimizeSoonID, 1 },
328 { Options::thresholdForFTLOptimizeSoonID, 2 },
329 { Options::thresholdForFTLOptimizeAfterWarmUpID, 2 }
330 };
331
332 const int numberOfOptionsToScale = sizeof(optionsToScale) / sizeof(OptionToScale);
333 for (int i = 0; i < numberOfOptionsToScale; i++) {
334 Option option(optionsToScale[i].id);
335 ASSERT(option.type() == Options::Type::int32Type);
336 option.int32Val() *= scaleFactor;
337 option.int32Val() = std::max(option.int32Val(), optionsToScale[i].minVal);
338 }
339}
340
341static void overrideDefaults()
342{
343#if !PLATFORM(IOS_FAMILY)
344 if (WTF::numberOfProcessorCores() < 4)
345#endif
346 {
347 Options::maximumMutatorUtilization() = 0.6;
348 Options::concurrentGCMaxHeadroom() = 1.4;
349 Options::minimumGCPauseMS() = 1;
350 Options::useStochasticMutatorScheduler() = false;
351 if (WTF::numberOfProcessorCores() <= 1)
352 Options::gcIncrementScale() = 1;
353 else
354 Options::gcIncrementScale() = 0;
355 }
356
357#if PLATFORM(IOS_FAMILY)
358 // On iOS, we control heap growth using process memory footprint. Therefore these values can be agressive.
359 Options::smallHeapRAMFraction() = 0.8;
360 Options::mediumHeapRAMFraction() = 0.9;
361
362#if !PLATFORM(WATCHOS) && defined(__LP64__)
363 Options::useSigillCrashAnalyzer() = true;
364#endif
365#endif
366
367#if !ENABLE(SIGNAL_BASED_VM_TRAPS)
368 Options::usePollingTraps() = true;
369#endif
370
371#if !ENABLE(WEBASSEMBLY_FAST_MEMORY)
372 Options::useWebAssemblyFastMemory() = false;
373#endif
374
375#if !HAVE(MACH_EXCEPTIONS)
376 Options::useMachForExceptions() = false;
377#endif
378}
379
380static void correctOptions()
381{
382 unsigned thresholdForGlobalLexicalBindingEpoch = Options::thresholdForGlobalLexicalBindingEpoch();
383 if (thresholdForGlobalLexicalBindingEpoch == 0 || thresholdForGlobalLexicalBindingEpoch == 1)
384 Options::thresholdForGlobalLexicalBindingEpoch() = UINT_MAX;
385}
386
387static void recomputeDependentOptions()
388{
389#if !defined(NDEBUG)
390 Options::validateDFGExceptionHandling() = true;
391#endif
392#if !ENABLE(JIT)
393 Options::useLLInt() = true;
394 Options::useJIT() = false;
395 Options::useBaselineJIT() = false;
396 Options::useDFGJIT() = false;
397 Options::useFTLJIT() = false;
398 Options::useDOMJIT() = false;
399 Options::useRegExpJIT() = false;
400#endif
401#if !ENABLE(CONCURRENT_JS)
402 Options::useConcurrentJIT() = false;
403#endif
404#if !ENABLE(YARR_JIT)
405 Options::useRegExpJIT() = false;
406#endif
407#if !ENABLE(DFG_JIT)
408 Options::useDFGJIT() = false;
409 Options::useFTLJIT() = false;
410#endif
411#if !ENABLE(FTL_JIT)
412 Options::useFTLJIT() = false;
413#endif
414
415#if !CPU(X86_64) && !CPU(ARM64)
416 Options::useConcurrentGC() = false;
417#endif
418
419 if (!Options::useJIT()) {
420 Options::useSigillCrashAnalyzer() = false;
421 Options::useWebAssembly() = false;
422 }
423
424 if (!Options::useWebAssembly())
425 Options::useFastTLSForWasmContext() = false;
426
427 if (Options::dumpDisassembly()
428 || Options::dumpDFGDisassembly()
429 || Options::dumpFTLDisassembly()
430 || Options::dumpBytecodeAtDFGTime()
431 || Options::dumpGraphAtEachPhase()
432 || Options::dumpDFGGraphAtEachPhase()
433 || Options::dumpDFGFTLGraphAtEachPhase()
434 || Options::dumpB3GraphAtEachPhase()
435 || Options::dumpAirGraphAtEachPhase()
436 || Options::verboseCompilation()
437 || Options::verboseFTLCompilation()
438 || Options::logCompilationChanges()
439 || Options::validateGraph()
440 || Options::validateGraphAtEachPhase()
441 || Options::verboseOSR()
442 || Options::verboseCompilationQueue()
443 || Options::reportCompileTimes()
444 || Options::reportBaselineCompileTimes()
445 || Options::reportDFGCompileTimes()
446 || Options::reportFTLCompileTimes()
447 || Options::logPhaseTimes()
448 || Options::verboseCFA()
449 || Options::verboseDFGFailure()
450 || Options::verboseFTLFailure()
451 || Options::dumpRandomizingFuzzerAgentPredictions())
452 Options::alwaysComputeHash() = true;
453
454 if (!Options::useConcurrentGC())
455 Options::collectContinuously() = false;
456
457 if (Option(Options::jitPolicyScaleID).isOverridden())
458 scaleJITPolicy();
459
460 if (Options::forceEagerCompilation()) {
461 Options::thresholdForJITAfterWarmUp() = 10;
462 Options::thresholdForJITSoon() = 10;
463 Options::thresholdForOptimizeAfterWarmUp() = 20;
464 Options::thresholdForOptimizeAfterLongWarmUp() = 20;
465 Options::thresholdForOptimizeSoon() = 20;
466 Options::thresholdForFTLOptimizeAfterWarmUp() = 20;
467 Options::thresholdForFTLOptimizeSoon() = 20;
468 Options::maximumEvalCacheableSourceLength() = 150000;
469 Options::useConcurrentJIT() = false;
470 }
471 if (Options::useMaximalFlushInsertionPhase()) {
472 Options::useOSREntryToDFG() = false;
473 Options::useOSREntryToFTL() = false;
474 }
475
476#if ENABLE(SEPARATED_WX_HEAP)
477 // Override globally for now. Longer term we'll just make the default
478 // be to have this option enabled, and have platforms that don't support
479 // it just silently use a single mapping.
480 Options::useSeparatedWXHeap() = true;
481#else
482 Options::useSeparatedWXHeap() = false;
483#endif
484
485 if (Options::alwaysUseShadowChicken())
486 Options::maximumInliningDepth() = 1;
487
488 // Compute the maximum value of the reoptimization retry counter. This is simply
489 // the largest value at which we don't overflow the execute counter, when using it
490 // to left-shift the execution counter by this amount. Currently the value ends
491 // up being 18, so this loop is not so terrible; it probably takes up ~100 cycles
492 // total on a 32-bit processor.
493 Options::reoptimizationRetryCounterMax() = 0;
494 while ((static_cast<int64_t>(Options::thresholdForOptimizeAfterLongWarmUp()) << (Options::reoptimizationRetryCounterMax() + 1)) <= static_cast<int64_t>(std::numeric_limits<int32_t>::max()))
495 Options::reoptimizationRetryCounterMax()++;
496
497 ASSERT((static_cast<int64_t>(Options::thresholdForOptimizeAfterLongWarmUp()) << Options::reoptimizationRetryCounterMax()) > 0);
498 ASSERT((static_cast<int64_t>(Options::thresholdForOptimizeAfterLongWarmUp()) << Options::reoptimizationRetryCounterMax()) <= static_cast<int64_t>(std::numeric_limits<int32_t>::max()));
499
500#if !defined(NDEBUG)
501 if (Options::maxSingleAllocationSize())
502 fastSetMaxSingleAllocationSize(Options::maxSingleAllocationSize());
503 else
504 fastSetMaxSingleAllocationSize(std::numeric_limits<size_t>::max());
505#endif
506
507 if (Options::useZombieMode()) {
508 Options::sweepSynchronously() = true;
509 Options::scribbleFreeCells() = true;
510 }
511
512 if (Options::reservedZoneSize() < minimumReservedZoneSize)
513 Options::reservedZoneSize() = minimumReservedZoneSize;
514 if (Options::softReservedZoneSize() < Options::reservedZoneSize() + minimumReservedZoneSize)
515 Options::softReservedZoneSize() = Options::reservedZoneSize() + minimumReservedZoneSize;
516
517#if USE(JSVALUE32_64)
518 // FIXME: Make probe OSR exit work on 32-bit:
519 // https://bugs.webkit.org/show_bug.cgi?id=177956
520 Options::useProbeOSRExit() = false;
521#endif
522
523 if (!Options::useCodeCache())
524 Options::diskCachePath() = nullptr;
525}
526
527void Options::initialize()
528{
529 static std::once_flag initializeOptionsOnceFlag;
530
531 std::call_once(
532 initializeOptionsOnceFlag,
533 [] {
534 // Initialize each of the options with their default values:
535#define FOR_EACH_OPTION(type_, name_, defaultValue_, availability_, description_) \
536 name_() = defaultValue_; \
537 name_##Default() = defaultValue_;
538 JSC_OPTIONS(FOR_EACH_OPTION)
539#undef FOR_EACH_OPTION
540
541 overrideDefaults();
542
543 // Allow environment vars to override options if applicable.
544 // The evn var should be the name of the option prefixed with
545 // "JSC_".
546#if PLATFORM(COCOA)
547 bool hasBadOptions = false;
548 for (char** envp = *_NSGetEnviron(); *envp; envp++) {
549 const char* env = *envp;
550 if (!strncmp("JSC_", env, 4)) {
551 if (!Options::setOption(&env[4])) {
552 dataLog("ERROR: invalid option: ", *envp, "\n");
553 hasBadOptions = true;
554 }
555 }
556 }
557 if (hasBadOptions && Options::validateOptions())
558 CRASH();
559#else // PLATFORM(COCOA)
560#define FOR_EACH_OPTION(type_, name_, defaultValue_, availability_, description_) \
561 overrideOptionWithHeuristic(name_(), name_##ID, "JSC_" #name_, Availability::availability_);
562 JSC_OPTIONS(FOR_EACH_OPTION)
563#undef FOR_EACH_OPTION
564#endif // PLATFORM(COCOA)
565
566#define FOR_EACH_OPTION(aliasedName_, unaliasedName_, equivalence_) \
567 overrideAliasedOptionWithHeuristic("JSC_" #aliasedName_);
568 JSC_ALIASED_OPTIONS(FOR_EACH_OPTION)
569#undef FOR_EACH_OPTION
570
571#if 0
572 ; // Deconfuse editors that do auto indentation
573#endif
574
575 correctOptions();
576
577 recomputeDependentOptions();
578
579 // Do range checks where needed and make corrections to the options:
580 ASSERT(Options::thresholdForOptimizeAfterLongWarmUp() >= Options::thresholdForOptimizeAfterWarmUp());
581 ASSERT(Options::thresholdForOptimizeAfterWarmUp() >= Options::thresholdForOptimizeSoon());
582 ASSERT(Options::thresholdForOptimizeAfterWarmUp() >= 0);
583 ASSERT(Options::criticalGCMemoryThreshold() > 0.0 && Options::criticalGCMemoryThreshold() < 1.0);
584
585 dumpOptionsIfNeeded();
586 ensureOptionsAreCoherent();
587
588#if HAVE(MACH_EXCEPTIONS)
589 if (Options::useMachForExceptions())
590 handleSignalsWithMach();
591#endif
592
593#if ASAN_ENABLED && OS(LINUX) && ENABLE(WEBASSEMBLY_FAST_MEMORY)
594 if (Options::useWebAssemblyFastMemory()) {
595 const char* asanOptions = getenv("ASAN_OPTIONS");
596 bool okToUseWebAssemblyFastMemory = asanOptions
597 && (strstr(asanOptions, "allow_user_segv_handler=1") || strstr(asanOptions, "handle_segv=0"));
598 if (!okToUseWebAssemblyFastMemory) {
599 dataLogLn("WARNING: ASAN interferes with JSC signal handlers; useWebAssemblyFastMemory will be disabled.");
600 Options::useWebAssemblyFastMemory() = false;
601 }
602 }
603#endif
604 });
605}
606
607void Options::dumpOptionsIfNeeded()
608{
609 if (Options::dumpOptions()) {
610 DumpLevel level = static_cast<DumpLevel>(Options::dumpOptions());
611 if (level > DumpLevel::Verbose)
612 level = DumpLevel::Verbose;
613
614 const char* title = nullptr;
615 switch (level) {
616 case DumpLevel::None:
617 break;
618 case DumpLevel::Overridden:
619 title = "Overridden JSC options:";
620 break;
621 case DumpLevel::All:
622 title = "All JSC options:";
623 break;
624 case DumpLevel::Verbose:
625 title = "All JSC options with descriptions:";
626 break;
627 }
628
629 StringBuilder builder;
630 dumpAllOptions(builder, level, title, nullptr, " ", "\n", DumpDefaults);
631 dataLog(builder.toString());
632 }
633}
634
635static bool isSeparator(char c)
636{
637 return isASCIISpace(c) || (c == ',');
638}
639
640bool Options::setOptions(const char* optionsStr)
641{
642 Vector<char*> options;
643
644 size_t length = strlen(optionsStr);
645 char* optionsStrCopy = WTF::fastStrDup(optionsStr);
646 char* end = optionsStrCopy + length;
647 char* p = optionsStrCopy;
648
649 while (p < end) {
650 // Skip separators (white space or commas).
651 while (p < end && isSeparator(*p))
652 p++;
653 if (p == end)
654 break;
655
656 char* optionStart = p;
657 p = strstr(p, "=");
658 if (!p) {
659 dataLogF("'=' not found in option string: %p\n", optionStart);
660 WTF::fastFree(optionsStrCopy);
661 return false;
662 }
663 p++;
664
665 char* valueBegin = p;
666 bool hasStringValue = false;
667 const int minStringLength = 2; // The min is an empty string i.e. 2 double quotes.
668 if ((p + minStringLength < end) && (*p == '"')) {
669 p = strstr(p + 1, "\"");
670 if (!p) {
671 dataLogF("Missing trailing '\"' in option string: %p\n", optionStart);
672 WTF::fastFree(optionsStrCopy);
673 return false; // End of string not found.
674 }
675 hasStringValue = true;
676 }
677
678 // Find next separator (white space or commas).
679 while (p < end && !isSeparator(*p))
680 p++;
681 if (!p)
682 p = end; // No more " " separator. Hence, this is the last arg.
683
684 // If we have a well-formed string value, strip the quotes.
685 if (hasStringValue) {
686 char* valueEnd = p;
687 ASSERT((*valueBegin == '"') && ((valueEnd - valueBegin) >= minStringLength) && (valueEnd[-1] == '"'));
688 memmove(valueBegin, valueBegin + 1, valueEnd - valueBegin - minStringLength);
689 valueEnd[-minStringLength] = '\0';
690 }
691
692 // Strip leading -- if present.
693 if ((p - optionStart > 2) && optionStart[0] == '-' && optionStart[1] == '-')
694 optionStart += 2;
695
696 *p++ = '\0';
697 options.append(optionStart);
698 }
699
700 bool success = true;
701 for (auto& option : options) {
702 bool optionSuccess = setOption(option);
703 if (!optionSuccess) {
704 dataLogF("Failed to set option : %s\n", option);
705 success = false;
706 }
707 }
708
709 correctOptions();
710
711 recomputeDependentOptions();
712
713 dumpOptionsIfNeeded();
714
715 ensureOptionsAreCoherent();
716
717 WTF::fastFree(optionsStrCopy);
718
719 return success;
720}
721
722// Parses a single command line option in the format "<optionName>=<value>"
723// (no spaces allowed) and set the specified option if appropriate.
724bool Options::setOptionWithoutAlias(const char* arg)
725{
726 // arg should look like this:
727 // <jscOptionName>=<appropriate value>
728 const char* equalStr = strchr(arg, '=');
729 if (!equalStr)
730 return false;
731
732 const char* valueStr = equalStr + 1;
733
734 // For each option, check if the specify arg is a match. If so, set the arg
735 // if the value makes sense. Otherwise, move on to checking the next option.
736#define FOR_EACH_OPTION(type_, name_, defaultValue_, availability_, description_) \
737 if (strlen(#name_) == static_cast<size_t>(equalStr - arg) \
738 && !strncmp(arg, #name_, equalStr - arg)) { \
739 if (Availability::availability_ != Availability::Normal \
740 && !isAvailable(name_##ID, Availability::availability_)) \
741 return false; \
742 type_ value; \
743 value = (defaultValue_); \
744 bool success = parse(valueStr, value); \
745 if (success) { \
746 name_() = value; \
747 correctOptions(); \
748 recomputeDependentOptions(); \
749 return true; \
750 } \
751 return false; \
752 }
753
754 JSC_OPTIONS(FOR_EACH_OPTION)
755#undef FOR_EACH_OPTION
756
757 return false; // No option matched.
758}
759
760static bool invertBoolOptionValue(const char* valueStr, const char*& invertedValueStr)
761{
762 bool boolValue;
763 if (!parse(valueStr, boolValue))
764 return false;
765 invertedValueStr = boolValue ? "false" : "true";
766 return true;
767}
768
769
770bool Options::setAliasedOption(const char* arg)
771{
772 // arg should look like this:
773 // <jscOptionName>=<appropriate value>
774 const char* equalStr = strchr(arg, '=');
775 if (!equalStr)
776 return false;
777
778 IGNORE_WARNINGS_BEGIN("tautological-compare")
779
780 // For each option, check if the specify arg is a match. If so, set the arg
781 // if the value makes sense. Otherwise, move on to checking the next option.
782#define FOR_EACH_OPTION(aliasedName_, unaliasedName_, equivalence) \
783 if (strlen(#aliasedName_) == static_cast<size_t>(equalStr - arg) \
784 && !strncmp(arg, #aliasedName_, equalStr - arg)) { \
785 String unaliasedOption(#unaliasedName_); \
786 if (equivalence == SameOption) \
787 unaliasedOption = unaliasedOption + equalStr; \
788 else { \
789 ASSERT(equivalence == InvertedOption); \
790 const char* invertedValueStr = nullptr; \
791 if (!invertBoolOptionValue(equalStr + 1, invertedValueStr)) \
792 return false; \
793 unaliasedOption = unaliasedOption + "=" + invertedValueStr; \
794 } \
795 return setOptionWithoutAlias(unaliasedOption.utf8().data()); \
796 }
797
798 JSC_ALIASED_OPTIONS(FOR_EACH_OPTION)
799#undef FOR_EACH_OPTION
800
801 IGNORE_WARNINGS_END
802
803 return false; // No option matched.
804}
805
806bool Options::setOption(const char* arg)
807{
808 bool success = setOptionWithoutAlias(arg);
809 if (success)
810 return true;
811 return setAliasedOption(arg);
812}
813
814
815void Options::dumpAllOptions(StringBuilder& builder, DumpLevel level, const char* title,
816 const char* separator, const char* optionHeader, const char* optionFooter, DumpDefaultsOption dumpDefaultsOption)
817{
818 if (title) {
819 builder.append(title);
820 builder.append('\n');
821 }
822
823 for (int id = 0; id < numberOfOptions; id++) {
824 if (separator && id)
825 builder.append(separator);
826 dumpOption(builder, level, static_cast<ID>(id), optionHeader, optionFooter, dumpDefaultsOption);
827 }
828}
829
830void Options::dumpAllOptionsInALine(StringBuilder& builder)
831{
832 dumpAllOptions(builder, DumpLevel::All, nullptr, " ", nullptr, nullptr, DontDumpDefaults);
833}
834
835void Options::dumpAllOptions(FILE* stream, DumpLevel level, const char* title)
836{
837 StringBuilder builder;
838 dumpAllOptions(builder, level, title, nullptr, " ", "\n", DumpDefaults);
839 fprintf(stream, "%s", builder.toString().utf8().data());
840}
841
842void Options::dumpOption(StringBuilder& builder, DumpLevel level, Options::ID id,
843 const char* header, const char* footer, DumpDefaultsOption dumpDefaultsOption)
844{
845 if (id >= numberOfOptions)
846 return; // Illegal option.
847
848 Option option(id);
849 Availability availability = option.availability();
850 if (availability != Availability::Normal && !isAvailable(id, availability))
851 return;
852
853 bool wasOverridden = option.isOverridden();
854 bool needsDescription = (level == DumpLevel::Verbose && option.description());
855
856 if (level == DumpLevel::Overridden && !wasOverridden)
857 return;
858
859 if (header)
860 builder.append(header);
861 builder.append(option.name());
862 builder.append('=');
863 option.dump(builder);
864
865 if (wasOverridden && (dumpDefaultsOption == DumpDefaults)) {
866 builder.appendLiteral(" (default: ");
867 option.defaultOption().dump(builder);
868 builder.appendLiteral(")");
869 }
870
871 if (needsDescription) {
872 builder.appendLiteral(" ... ");
873 builder.append(option.description());
874 }
875
876 builder.append(footer);
877}
878
879void Options::ensureOptionsAreCoherent()
880{
881 bool coherent = true;
882 if (!(useLLInt() || useJIT())) {
883 coherent = false;
884 dataLog("INCOHERENT OPTIONS: at least one of useLLInt or useJIT must be true\n");
885 }
886 if (!coherent)
887 CRASH();
888}
889
890void Option::dump(StringBuilder& builder) const
891{
892 switch (type()) {
893 case Options::Type::boolType:
894 builder.append(m_entry.boolVal ? "true" : "false");
895 break;
896 case Options::Type::unsignedType:
897 builder.appendNumber(m_entry.unsignedVal);
898 break;
899 case Options::Type::sizeType:
900 builder.appendNumber(m_entry.sizeVal);
901 break;
902 case Options::Type::doubleType:
903 builder.appendFixedPrecisionNumber(m_entry.doubleVal);
904 break;
905 case Options::Type::int32Type:
906 builder.appendNumber(m_entry.int32Val);
907 break;
908 case Options::Type::optionRangeType:
909 builder.append(m_entry.optionRangeVal.rangeString());
910 break;
911 case Options::Type::optionStringType: {
912 const char* option = m_entry.optionStringVal;
913 if (!option)
914 option = "";
915 builder.append('"');
916 builder.append(option);
917 builder.append('"');
918 break;
919 }
920 case Options::Type::gcLogLevelType: {
921 builder.append(GCLogging::levelAsString(m_entry.gcLogLevelVal));
922 break;
923 }
924 }
925}
926
927bool Option::operator==(const Option& other) const
928{
929 switch (type()) {
930 case Options::Type::boolType:
931 return m_entry.boolVal == other.m_entry.boolVal;
932 case Options::Type::unsignedType:
933 return m_entry.unsignedVal == other.m_entry.unsignedVal;
934 case Options::Type::sizeType:
935 return m_entry.sizeVal == other.m_entry.sizeVal;
936 case Options::Type::doubleType:
937 return (m_entry.doubleVal == other.m_entry.doubleVal) || (std::isnan(m_entry.doubleVal) && std::isnan(other.m_entry.doubleVal));
938 case Options::Type::int32Type:
939 return m_entry.int32Val == other.m_entry.int32Val;
940 case Options::Type::optionRangeType:
941 return m_entry.optionRangeVal.rangeString() == other.m_entry.optionRangeVal.rangeString();
942 case Options::Type::optionStringType:
943 return (m_entry.optionStringVal == other.m_entry.optionStringVal)
944 || (m_entry.optionStringVal && other.m_entry.optionStringVal && !strcmp(m_entry.optionStringVal, other.m_entry.optionStringVal));
945 case Options::Type::gcLogLevelType:
946 return m_entry.gcLogLevelVal == other.m_entry.gcLogLevelVal;
947 }
948 return false;
949}
950
951} // namespace JSC
952
953