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(WTF::numberOfProcessorCores(), 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 (WTF::numberOfProcessorCores() <= 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::useDFGJIT() = false;
396 Options::useFTLJIT() = false;
397 Options::useDOMJIT() = false;
398#endif
399#if !ENABLE(YARR_JIT)
400 Options::useRegExpJIT() = false;
401#endif
402#if !ENABLE(CONCURRENT_JS)
403 Options::useConcurrentJIT() = false;
404#endif
405#if !ENABLE(DFG_JIT)
406 Options::useDFGJIT() = false;
407 Options::useFTLJIT() = false;
408#endif
409#if !ENABLE(FTL_JIT)
410 Options::useFTLJIT() = false;
411#endif
412
413#if !CPU(X86_64) && !CPU(ARM64)
414 Options::useConcurrentGC() = false;
415#endif
416
417#if ENABLE(JIT) && CPU(X86)
418 // Disable JIT on IA-32 if SSE2 is not present
419 if (!MacroAssemblerX86::supportsFloatingPoint())
420 Options::useJIT() = false;
421#endif
422
423 if (!Options::useJIT()) {
424 Options::useSigillCrashAnalyzer() = false;
425 Options::useWebAssembly() = false;
426 }
427
428 if (!Options::useWebAssembly())
429 Options::useFastTLSForWasmContext() = false;
430
431 if (Options::dumpDisassembly()
432 || Options::dumpDFGDisassembly()
433 || Options::dumpFTLDisassembly()
434 || Options::dumpBytecodeAtDFGTime()
435 || Options::dumpGraphAtEachPhase()
436 || Options::dumpDFGGraphAtEachPhase()
437 || Options::dumpDFGFTLGraphAtEachPhase()
438 || Options::dumpB3GraphAtEachPhase()
439 || Options::dumpAirGraphAtEachPhase()
440 || Options::verboseCompilation()
441 || Options::verboseFTLCompilation()
442 || Options::logCompilationChanges()
443 || Options::validateGraph()
444 || Options::validateGraphAtEachPhase()
445 || Options::verboseOSR()
446 || Options::verboseCompilationQueue()
447 || Options::reportCompileTimes()
448 || Options::reportBaselineCompileTimes()
449 || Options::reportDFGCompileTimes()
450 || Options::reportFTLCompileTimes()
451 || Options::logPhaseTimes()
452 || Options::verboseCFA()
453 || Options::verboseDFGFailure()
454 || Options::verboseFTLFailure()
455 || Options::dumpRandomizingFuzzerAgentPredictions())
456 Options::alwaysComputeHash() = true;
457
458 if (!Options::useConcurrentGC())
459 Options::collectContinuously() = false;
460
461 if (Option(Options::jitPolicyScaleID).isOverridden())
462 scaleJITPolicy();
463
464 if (Options::forceEagerCompilation()) {
465 Options::thresholdForJITAfterWarmUp() = 10;
466 Options::thresholdForJITSoon() = 10;
467 Options::thresholdForOptimizeAfterWarmUp() = 20;
468 Options::thresholdForOptimizeAfterLongWarmUp() = 20;
469 Options::thresholdForOptimizeSoon() = 20;
470 Options::thresholdForFTLOptimizeAfterWarmUp() = 20;
471 Options::thresholdForFTLOptimizeSoon() = 20;
472 Options::maximumEvalCacheableSourceLength() = 150000;
473 Options::useConcurrentJIT() = false;
474 }
475 if (Options::useMaximalFlushInsertionPhase()) {
476 Options::useOSREntryToDFG() = false;
477 Options::useOSREntryToFTL() = false;
478 }
479
480#if ENABLE(SEPARATED_WX_HEAP)
481 // Override globally for now. Longer term we'll just make the default
482 // be to have this option enabled, and have platforms that don't support
483 // it just silently use a single mapping.
484 Options::useSeparatedWXHeap() = true;
485#else
486 Options::useSeparatedWXHeap() = false;
487#endif
488
489 if (Options::alwaysUseShadowChicken())
490 Options::maximumInliningDepth() = 1;
491
492 // Compute the maximum value of the reoptimization retry counter. This is simply
493 // the largest value at which we don't overflow the execute counter, when using it
494 // to left-shift the execution counter by this amount. Currently the value ends
495 // up being 18, so this loop is not so terrible; it probably takes up ~100 cycles
496 // total on a 32-bit processor.
497 Options::reoptimizationRetryCounterMax() = 0;
498 while ((static_cast<int64_t>(Options::thresholdForOptimizeAfterLongWarmUp()) << (Options::reoptimizationRetryCounterMax() + 1)) <= static_cast<int64_t>(std::numeric_limits<int32_t>::max()))
499 Options::reoptimizationRetryCounterMax()++;
500
501 ASSERT((static_cast<int64_t>(Options::thresholdForOptimizeAfterLongWarmUp()) << Options::reoptimizationRetryCounterMax()) > 0);
502 ASSERT((static_cast<int64_t>(Options::thresholdForOptimizeAfterLongWarmUp()) << Options::reoptimizationRetryCounterMax()) <= static_cast<int64_t>(std::numeric_limits<int32_t>::max()));
503
504#if !defined(NDEBUG)
505 if (Options::maxSingleAllocationSize())
506 fastSetMaxSingleAllocationSize(Options::maxSingleAllocationSize());
507 else
508 fastSetMaxSingleAllocationSize(std::numeric_limits<size_t>::max());
509#endif
510
511 if (Options::useZombieMode()) {
512 Options::sweepSynchronously() = true;
513 Options::scribbleFreeCells() = true;
514 }
515
516 if (Options::reservedZoneSize() < minimumReservedZoneSize)
517 Options::reservedZoneSize() = minimumReservedZoneSize;
518 if (Options::softReservedZoneSize() < Options::reservedZoneSize() + minimumReservedZoneSize)
519 Options::softReservedZoneSize() = Options::reservedZoneSize() + minimumReservedZoneSize;
520
521#if USE(JSVALUE32_64)
522 // FIXME: Make probe OSR exit work on 32-bit:
523 // https://bugs.webkit.org/show_bug.cgi?id=177956
524 Options::useProbeOSRExit() = false;
525#endif
526
527 if (!Options::useCodeCache())
528 Options::diskCachePath() = nullptr;
529}
530
531void Options::initialize()
532{
533 static std::once_flag initializeOptionsOnceFlag;
534
535 std::call_once(
536 initializeOptionsOnceFlag,
537 [] {
538 // Initialize each of the options with their default values:
539#define FOR_EACH_OPTION(type_, name_, defaultValue_, availability_, description_) \
540 name_() = defaultValue_; \
541 name_##Default() = defaultValue_;
542 JSC_OPTIONS(FOR_EACH_OPTION)
543#undef FOR_EACH_OPTION
544
545 overrideDefaults();
546
547 // Allow environment vars to override options if applicable.
548 // The evn var should be the name of the option prefixed with
549 // "JSC_".
550#if PLATFORM(COCOA)
551 bool hasBadOptions = false;
552 for (char** envp = *_NSGetEnviron(); *envp; envp++) {
553 const char* env = *envp;
554 if (!strncmp("JSC_", env, 4)) {
555 if (!Options::setOption(&env[4])) {
556 dataLog("ERROR: invalid option: ", *envp, "\n");
557 hasBadOptions = true;
558 }
559 }
560 }
561 if (hasBadOptions && Options::validateOptions())
562 CRASH();
563#else // PLATFORM(COCOA)
564#define FOR_EACH_OPTION(type_, name_, defaultValue_, availability_, description_) \
565 overrideOptionWithHeuristic(name_(), name_##ID, "JSC_" #name_, Availability::availability_);
566 JSC_OPTIONS(FOR_EACH_OPTION)
567#undef FOR_EACH_OPTION
568#endif // PLATFORM(COCOA)
569
570#define FOR_EACH_OPTION(aliasedName_, unaliasedName_, equivalence_) \
571 overrideAliasedOptionWithHeuristic("JSC_" #aliasedName_);
572 JSC_ALIASED_OPTIONS(FOR_EACH_OPTION)
573#undef FOR_EACH_OPTION
574
575#if 0
576 ; // Deconfuse editors that do auto indentation
577#endif
578
579 correctOptions();
580
581 recomputeDependentOptions();
582
583 // Do range checks where needed and make corrections to the options:
584 ASSERT(Options::thresholdForOptimizeAfterLongWarmUp() >= Options::thresholdForOptimizeAfterWarmUp());
585 ASSERT(Options::thresholdForOptimizeAfterWarmUp() >= Options::thresholdForOptimizeSoon());
586 ASSERT(Options::thresholdForOptimizeAfterWarmUp() >= 0);
587 ASSERT(Options::criticalGCMemoryThreshold() > 0.0 && Options::criticalGCMemoryThreshold() < 1.0);
588
589 dumpOptionsIfNeeded();
590 ensureOptionsAreCoherent();
591
592#if HAVE(MACH_EXCEPTIONS)
593 if (Options::useMachForExceptions())
594 handleSignalsWithMach();
595#endif
596
597#if ASAN_ENABLED && OS(LINUX) && ENABLE(WEBASSEMBLY_FAST_MEMORY)
598 if (Options::useWebAssemblyFastMemory()) {
599 const char* asanOptions = getenv("ASAN_OPTIONS");
600 bool okToUseWebAssemblyFastMemory = asanOptions
601 && (strstr(asanOptions, "allow_user_segv_handler=1") || strstr(asanOptions, "handle_segv=0"));
602 if (!okToUseWebAssemblyFastMemory) {
603 dataLogLn("WARNING: ASAN interferes with JSC signal handlers; useWebAssemblyFastMemory will be disabled.");
604 Options::useWebAssemblyFastMemory() = false;
605 }
606 }
607#endif
608 });
609}
610
611void Options::dumpOptionsIfNeeded()
612{
613 if (Options::dumpOptions()) {
614 DumpLevel level = static_cast<DumpLevel>(Options::dumpOptions());
615 if (level > DumpLevel::Verbose)
616 level = DumpLevel::Verbose;
617
618 const char* title = nullptr;
619 switch (level) {
620 case DumpLevel::None:
621 break;
622 case DumpLevel::Overridden:
623 title = "Overridden JSC options:";
624 break;
625 case DumpLevel::All:
626 title = "All JSC options:";
627 break;
628 case DumpLevel::Verbose:
629 title = "All JSC options with descriptions:";
630 break;
631 }
632
633 StringBuilder builder;
634 dumpAllOptions(builder, level, title, nullptr, " ", "\n", DumpDefaults);
635 dataLog(builder.toString());
636 }
637}
638
639static bool isSeparator(char c)
640{
641 return isASCIISpace(c) || (c == ',');
642}
643
644bool Options::setOptions(const char* optionsStr)
645{
646 Vector<char*> options;
647
648 size_t length = strlen(optionsStr);
649 char* optionsStrCopy = WTF::fastStrDup(optionsStr);
650 char* end = optionsStrCopy + length;
651 char* p = optionsStrCopy;
652
653 while (p < end) {
654 // Skip separators (white space or commas).
655 while (p < end && isSeparator(*p))
656 p++;
657 if (p == end)
658 break;
659
660 char* optionStart = p;
661 p = strstr(p, "=");
662 if (!p) {
663 dataLogF("'=' not found in option string: %p\n", optionStart);
664 WTF::fastFree(optionsStrCopy);
665 return false;
666 }
667 p++;
668
669 char* valueBegin = p;
670 bool hasStringValue = false;
671 const int minStringLength = 2; // The min is an empty string i.e. 2 double quotes.
672 if ((p + minStringLength < end) && (*p == '"')) {
673 p = strstr(p + 1, "\"");
674 if (!p) {
675 dataLogF("Missing trailing '\"' in option string: %p\n", optionStart);
676 WTF::fastFree(optionsStrCopy);
677 return false; // End of string not found.
678 }
679 hasStringValue = true;
680 }
681
682 // Find next separator (white space or commas).
683 while (p < end && !isSeparator(*p))
684 p++;
685 if (!p)
686 p = end; // No more " " separator. Hence, this is the last arg.
687
688 // If we have a well-formed string value, strip the quotes.
689 if (hasStringValue) {
690 char* valueEnd = p;
691 ASSERT((*valueBegin == '"') && ((valueEnd - valueBegin) >= minStringLength) && (valueEnd[-1] == '"'));
692 memmove(valueBegin, valueBegin + 1, valueEnd - valueBegin - minStringLength);
693 valueEnd[-minStringLength] = '\0';
694 }
695
696 // Strip leading -- if present.
697 if ((p - optionStart > 2) && optionStart[0] == '-' && optionStart[1] == '-')
698 optionStart += 2;
699
700 *p++ = '\0';
701 options.append(optionStart);
702 }
703
704 bool success = true;
705 for (auto& option : options) {
706 bool optionSuccess = setOption(option);
707 if (!optionSuccess) {
708 dataLogF("Failed to set option : %s\n", option);
709 success = false;
710 }
711 }
712
713 correctOptions();
714
715 recomputeDependentOptions();
716
717 dumpOptionsIfNeeded();
718
719 ensureOptionsAreCoherent();
720
721 WTF::fastFree(optionsStrCopy);
722
723 return success;
724}
725
726// Parses a single command line option in the format "<optionName>=<value>"
727// (no spaces allowed) and set the specified option if appropriate.
728bool Options::setOptionWithoutAlias(const char* arg)
729{
730 // arg should look like this:
731 // <jscOptionName>=<appropriate value>
732 const char* equalStr = strchr(arg, '=');
733 if (!equalStr)
734 return false;
735
736 const char* valueStr = equalStr + 1;
737
738 // For each option, check if the specify arg is a match. If so, set the arg
739 // if the value makes sense. Otherwise, move on to checking the next option.
740#define FOR_EACH_OPTION(type_, name_, defaultValue_, availability_, description_) \
741 if (strlen(#name_) == static_cast<size_t>(equalStr - arg) \
742 && !strncmp(arg, #name_, equalStr - arg)) { \
743 if (Availability::availability_ != Availability::Normal \
744 && !isAvailable(name_##ID, Availability::availability_)) \
745 return false; \
746 type_ value; \
747 value = (defaultValue_); \
748 bool success = parse(valueStr, value); \
749 if (success) { \
750 name_() = value; \
751 correctOptions(); \
752 recomputeDependentOptions(); \
753 return true; \
754 } \
755 return false; \
756 }
757
758 JSC_OPTIONS(FOR_EACH_OPTION)
759#undef FOR_EACH_OPTION
760
761 return false; // No option matched.
762}
763
764static bool invertBoolOptionValue(const char* valueStr, const char*& invertedValueStr)
765{
766 bool boolValue;
767 if (!parse(valueStr, boolValue))
768 return false;
769 invertedValueStr = boolValue ? "false" : "true";
770 return true;
771}
772
773
774bool Options::setAliasedOption(const char* arg)
775{
776 // arg should look like this:
777 // <jscOptionName>=<appropriate value>
778 const char* equalStr = strchr(arg, '=');
779 if (!equalStr)
780 return false;
781
782 IGNORE_WARNINGS_BEGIN("tautological-compare")
783
784 // For each option, check if the specify arg is a match. If so, set the arg
785 // if the value makes sense. Otherwise, move on to checking the next option.
786#define FOR_EACH_OPTION(aliasedName_, unaliasedName_, equivalence) \
787 if (strlen(#aliasedName_) == static_cast<size_t>(equalStr - arg) \
788 && !strncmp(arg, #aliasedName_, equalStr - arg)) { \
789 String unaliasedOption(#unaliasedName_); \
790 if (equivalence == SameOption) \
791 unaliasedOption = unaliasedOption + equalStr; \
792 else { \
793 ASSERT(equivalence == InvertedOption); \
794 const char* invertedValueStr = nullptr; \
795 if (!invertBoolOptionValue(equalStr + 1, invertedValueStr)) \
796 return false; \
797 unaliasedOption = unaliasedOption + "=" + invertedValueStr; \
798 } \
799 return setOptionWithoutAlias(unaliasedOption.utf8().data()); \
800 }
801
802 JSC_ALIASED_OPTIONS(FOR_EACH_OPTION)
803#undef FOR_EACH_OPTION
804
805 IGNORE_WARNINGS_END
806
807 return false; // No option matched.
808}
809
810bool Options::setOption(const char* arg)
811{
812 bool success = setOptionWithoutAlias(arg);
813 if (success)
814 return true;
815 return setAliasedOption(arg);
816}
817
818
819void Options::dumpAllOptions(StringBuilder& builder, DumpLevel level, const char* title,
820 const char* separator, const char* optionHeader, const char* optionFooter, DumpDefaultsOption dumpDefaultsOption)
821{
822 if (title) {
823 builder.append(title);
824 builder.append('\n');
825 }
826
827 for (int id = 0; id < numberOfOptions; id++) {
828 if (separator && id)
829 builder.append(separator);
830 dumpOption(builder, level, static_cast<ID>(id), optionHeader, optionFooter, dumpDefaultsOption);
831 }
832}
833
834void Options::dumpAllOptionsInALine(StringBuilder& builder)
835{
836 dumpAllOptions(builder, DumpLevel::All, nullptr, " ", nullptr, nullptr, DontDumpDefaults);
837}
838
839void Options::dumpAllOptions(FILE* stream, DumpLevel level, const char* title)
840{
841 StringBuilder builder;
842 dumpAllOptions(builder, level, title, nullptr, " ", "\n", DumpDefaults);
843 fprintf(stream, "%s", builder.toString().utf8().data());
844}
845
846void Options::dumpOption(StringBuilder& builder, DumpLevel level, Options::ID id,
847 const char* header, const char* footer, DumpDefaultsOption dumpDefaultsOption)
848{
849 if (id >= numberOfOptions)
850 return; // Illegal option.
851
852 Option option(id);
853 Availability availability = option.availability();
854 if (availability != Availability::Normal && !isAvailable(id, availability))
855 return;
856
857 bool wasOverridden = option.isOverridden();
858 bool needsDescription = (level == DumpLevel::Verbose && option.description());
859
860 if (level == DumpLevel::Overridden && !wasOverridden)
861 return;
862
863 if (header)
864 builder.append(header);
865 builder.append(option.name());
866 builder.append('=');
867 option.dump(builder);
868
869 if (wasOverridden && (dumpDefaultsOption == DumpDefaults)) {
870 builder.appendLiteral(" (default: ");
871 option.defaultOption().dump(builder);
872 builder.appendLiteral(")");
873 }
874
875 if (needsDescription) {
876 builder.appendLiteral(" ... ");
877 builder.append(option.description());
878 }
879
880 builder.append(footer);
881}
882
883void Options::ensureOptionsAreCoherent()
884{
885 bool coherent = true;
886 if (!(useLLInt() || useJIT())) {
887 coherent = false;
888 dataLog("INCOHERENT OPTIONS: at least one of useLLInt or useJIT must be true\n");
889 }
890 if (!coherent)
891 CRASH();
892}
893
894void Option::dump(StringBuilder& builder) const
895{
896 switch (type()) {
897 case Options::Type::boolType:
898 builder.append(m_entry.boolVal ? "true" : "false");
899 break;
900 case Options::Type::unsignedType:
901 builder.appendNumber(m_entry.unsignedVal);
902 break;
903 case Options::Type::sizeType:
904 builder.appendNumber(m_entry.sizeVal);
905 break;
906 case Options::Type::doubleType:
907 builder.appendFixedPrecisionNumber(m_entry.doubleVal);
908 break;
909 case Options::Type::int32Type:
910 builder.appendNumber(m_entry.int32Val);
911 break;
912 case Options::Type::optionRangeType:
913 builder.append(m_entry.optionRangeVal.rangeString());
914 break;
915 case Options::Type::optionStringType: {
916 const char* option = m_entry.optionStringVal;
917 if (!option)
918 option = "";
919 builder.append('"');
920 builder.append(option);
921 builder.append('"');
922 break;
923 }
924 case Options::Type::gcLogLevelType: {
925 builder.append(GCLogging::levelAsString(m_entry.gcLogLevelVal));
926 break;
927 }
928 }
929}
930
931bool Option::operator==(const Option& other) const
932{
933 switch (type()) {
934 case Options::Type::boolType:
935 return m_entry.boolVal == other.m_entry.boolVal;
936 case Options::Type::unsignedType:
937 return m_entry.unsignedVal == other.m_entry.unsignedVal;
938 case Options::Type::sizeType:
939 return m_entry.sizeVal == other.m_entry.sizeVal;
940 case Options::Type::doubleType:
941 return (m_entry.doubleVal == other.m_entry.doubleVal) || (std::isnan(m_entry.doubleVal) && std::isnan(other.m_entry.doubleVal));
942 case Options::Type::int32Type:
943 return m_entry.int32Val == other.m_entry.int32Val;
944 case Options::Type::optionRangeType:
945 return m_entry.optionRangeVal.rangeString() == other.m_entry.optionRangeVal.rangeString();
946 case Options::Type::optionStringType:
947 return (m_entry.optionStringVal == other.m_entry.optionStringVal)
948 || (m_entry.optionStringVal && other.m_entry.optionStringVal && !strcmp(m_entry.optionStringVal, other.m_entry.optionStringVal));
949 case Options::Type::gcLogLevelType:
950 return m_entry.gcLogLevelVal == other.m_entry.gcLogLevelVal;
951 }
952 return false;
953}
954
955} // namespace JSC
956
957