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