Add ability to configure JSC options from a file
https://bugs.webkit.org/show_bug.cgi?id=168914

Reviewed by Filip Pizlo.

Added the ability to set options and DataLog file location via a configuration file.
Source/JavaScriptCore:

The configuration file is specified with the --configFile option to JSC or the
JSC_configFile environment variable.

The file format allows for options conditionally dependent on various attributes.
Currently those attributes are the process name, parent process name and build
type (Release or Debug).  In this patch, the parent process type is not set.
That will be set up in WebKit code with a follow up patch.

Here is an example config file:

    logFile = "/tmp/jscLog.%pid.txt"

    jscOptions {
        dumpOptions = 2
    }

    build == "Debug" {
        jscOptions {
            useConcurrentJIT = false
            dumpDisassembly = true
        }
    }

    build == "Release" && processName == "jsc" {
        jscOptions {
            asyncDisassembly = true
        }
    }

Eliminated the prior options file code.

* CMakeLists.txt:
* JavaScriptCore.xcodeproj/project.pbxproj:
* jsc.cpp:
(jscmain):
* runtime/ConfigFile.cpp: Added.
(JSC::ConfigFileScanner::ConfigFileScanner):
(JSC::ConfigFileScanner::start):
(JSC::ConfigFileScanner::lineNumber):
(JSC::ConfigFileScanner::currentBuffer):
(JSC::ConfigFileScanner::atFileEnd):
(JSC::ConfigFileScanner::tryConsume):
(JSC::ConfigFileScanner::tryConsumeString):
(JSC::ConfigFileScanner::tryConsumeUpto):
(JSC::ConfigFileScanner::fillBufferIfNeeded):
(JSC::ConfigFileScanner::fillBuffer):
(JSC::ConfigFile::ConfigFile):
(JSC::ConfigFile::setProcessName):
(JSC::ConfigFile::setParentProcessName):
(JSC::ConfigFile::parse):
* runtime/ConfigFile.h: Added.
* runtime/Options.cpp:
(JSC::Options::initialize):
(JSC::Options::setOptions):
* runtime/Options.h:

Source/WTF:

The pathname can include the printf style "%pid", which will be replaced with the
current process id.

* wtf/DataLog.cpp:
(WTF::initializeLogFileOnce):
(WTF::setDataFile):
* wtf/DataLog.h:


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@213151 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/CMakeLists.txt b/Source/JavaScriptCore/CMakeLists.txt
index 7975df0..c05bee7 100644
--- a/Source/JavaScriptCore/CMakeLists.txt
+++ b/Source/JavaScriptCore/CMakeLists.txt
@@ -690,6 +690,7 @@
     runtime/CommonSlowPathsExceptions.cpp
     runtime/CompilationResult.cpp
     runtime/Completion.cpp
+    runtime/ConfigFile.cpp
     runtime/ConsoleClient.cpp
     runtime/ConsoleObject.cpp
     runtime/ConstantMode.cpp
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index 67665ad..e094768 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,67 @@
+2017-02-28  Michael Saboff  <msaboff@apple.com>
+
+        Add ability to configure JSC options from a file
+        https://bugs.webkit.org/show_bug.cgi?id=168914
+
+        Reviewed by Filip Pizlo.
+
+        Added the ability to set options and DataLog file location via a configuration file.
+        The configuration file is specified with the --configFile option to JSC or the
+        JSC_configFile environment variable.
+
+        The file format allows for options conditionally dependent on various attributes.
+        Currently those attributes are the process name, parent process name and build
+        type (Release or Debug).  In this patch, the parent process type is not set.
+        That will be set up in WebKit code with a follow up patch.
+
+        Here is an example config file:
+
+            logFile = "/tmp/jscLog.%pid.txt"
+
+            jscOptions {
+                dumpOptions = 2
+            }
+
+            build == "Debug" {
+                jscOptions {
+                    useConcurrentJIT = false
+                    dumpDisassembly = true
+                }
+            }
+
+            build == "Release" && processName == "jsc" {
+                jscOptions {
+                    asyncDisassembly = true
+                }
+            }
+
+        Eliminated the prior options file code.
+
+        * CMakeLists.txt:
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * jsc.cpp:
+        (jscmain):
+        * runtime/ConfigFile.cpp: Added.
+        (JSC::ConfigFileScanner::ConfigFileScanner):
+        (JSC::ConfigFileScanner::start):
+        (JSC::ConfigFileScanner::lineNumber):
+        (JSC::ConfigFileScanner::currentBuffer):
+        (JSC::ConfigFileScanner::atFileEnd):
+        (JSC::ConfigFileScanner::tryConsume):
+        (JSC::ConfigFileScanner::tryConsumeString):
+        (JSC::ConfigFileScanner::tryConsumeUpto):
+        (JSC::ConfigFileScanner::fillBufferIfNeeded):
+        (JSC::ConfigFileScanner::fillBuffer):
+        (JSC::ConfigFile::ConfigFile):
+        (JSC::ConfigFile::setProcessName):
+        (JSC::ConfigFile::setParentProcessName):
+        (JSC::ConfigFile::parse):
+        * runtime/ConfigFile.h: Added.
+        * runtime/Options.cpp:
+        (JSC::Options::initialize):
+        (JSC::Options::setOptions):
+        * runtime/Options.h:
+
 2017-02-27  Alex Christensen  <achristensen@webkit.org>
 
         Begin enabling WebRTC on 64-bit
diff --git a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
index 5a70b0e..92b8af1 100644
--- a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
+++ b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
@@ -1386,6 +1386,8 @@
 		655EB29B10CE2581001A990E /* NodesCodegen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 655EB29A10CE2581001A990E /* NodesCodegen.cpp */; };
 		657CF45819BF6662004ACBF2 /* JSCallee.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 657CF45619BF6662004ACBF2 /* JSCallee.cpp */; };
 		657CF45919BF6662004ACBF2 /* JSCallee.h in Headers */ = {isa = PBXBuildFile; fileRef = 657CF45719BF6662004ACBF2 /* JSCallee.h */; settings = {ATTRIBUTES = (Private, ); }; };
+		658824AF1E5CFDB000FB7359 /* ConfigFile.h in Headers */ = {isa = PBXBuildFile; fileRef = 658824AE1E5CFDB000FB7359 /* ConfigFile.h */; };
+		658824B11E5CFDF400FB7359 /* ConfigFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 658824B01E5CFDF400FB7359 /* ConfigFile.cpp */; };
 		658D3A5619638268003C45D6 /* VMEntryRecord.h in Headers */ = {isa = PBXBuildFile; fileRef = 658D3A5519638268003C45D6 /* VMEntryRecord.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		65B8392E1BACAD360044E824 /* CachedRecovery.h in Headers */ = {isa = PBXBuildFile; fileRef = 65B8392C1BACA92A0044E824 /* CachedRecovery.h */; };
 		65B8392F1BACAD6A0044E824 /* CachedRecovery.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 65B8392D1BACA9D30044E824 /* CachedRecovery.cpp */; };
@@ -3870,6 +3872,8 @@
 		657CF45619BF6662004ACBF2 /* JSCallee.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSCallee.cpp; sourceTree = "<group>"; };
 		657CF45719BF6662004ACBF2 /* JSCallee.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSCallee.h; sourceTree = "<group>"; };
 		65860177185A8F5E00030EEE /* MaxFrameExtentForSlowPathCall.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MaxFrameExtentForSlowPathCall.h; sourceTree = "<group>"; };
+		658824AE1E5CFDB000FB7359 /* ConfigFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ConfigFile.h; sourceTree = "<group>"; };
+		658824B01E5CFDF400FB7359 /* ConfigFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ConfigFile.cpp; sourceTree = "<group>"; };
 		658D3A5519638268003C45D6 /* VMEntryRecord.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = VMEntryRecord.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
 		65987F2C167FE84B003C2F8D /* DFGOSRExitCompilationInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGOSRExitCompilationInfo.h; path = dfg/DFGOSRExitCompilationInfo.h; sourceTree = "<group>"; };
 		65987F2F16828A7E003C2F8D /* UnusedPointer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UnusedPointer.h; sourceTree = "<group>"; };
@@ -6354,6 +6358,8 @@
 				A7E5A3A61797432D00E893C0 /* CompilationResult.h */,
 				969A09220ED1E09C00F1F681 /* Completion.cpp */,
 				F5BB2BC5030F772101FCFE1D /* Completion.h */,
+				658824B01E5CFDF400FB7359 /* ConfigFile.cpp */,
+				658824AE1E5CFDB000FB7359 /* ConfigFile.h */,
 				0FDB2CE9174896C7007B3C1B /* ConcurrentJSLock.h */,
 				A5B6A74C18C6DBA600F11E91 /* ConsoleClient.cpp */,
 				A53CE08918BC21C300BEDF76 /* ConsoleClient.h */,
@@ -8364,6 +8370,7 @@
 				A7D89CFE17A0B8CC00773AD8 /* DFGOSRAvailabilityAnalysisPhase.h in Headers */,
 				0FD82E57141DAF1000179C94 /* DFGOSREntry.h in Headers */,
 				0FD8A32617D51F5700CA2C40 /* DFGOSREntrypointCreationPhase.h in Headers */,
+				658824AF1E5CFDB000FB7359 /* ConfigFile.h in Headers */,
 				0FC0976A1468A6F700CF2442 /* DFGOSRExit.h in Headers */,
 				0F235BEC17178E7300690C7F /* DFGOSRExitBase.h in Headers */,
 				0FFB921C16D02F110055A5DB /* DFGOSRExitCompilationInfo.h in Headers */,
@@ -10377,6 +10384,7 @@
 				140B7D1D0DC69AF7009C42B8 /* JSLexicalEnvironment.cpp in Sources */,
 				14280875107EC13E0013E7B2 /* JSLock.cpp in Sources */,
 				C25D709B16DE99F400FCA6BC /* JSManagedValue.mm in Sources */,
+				658824B11E5CFDF400FB7359 /* ConfigFile.cpp in Sources */,
 				A700874117CBE8EB00C3E643 /* JSMap.cpp in Sources */,
 				A74DEF95182D991400522C22 /* JSMapIterator.cpp in Sources */,
 				E3D239C81B829C1C00BBEF67 /* JSModuleEnvironment.cpp in Sources */,
diff --git a/Source/JavaScriptCore/jsc.cpp b/Source/JavaScriptCore/jsc.cpp
index aedf42e..80e1d50 100644
--- a/Source/JavaScriptCore/jsc.cpp
+++ b/Source/JavaScriptCore/jsc.cpp
@@ -29,6 +29,7 @@
 #include "ButterflyInlines.h"
 #include "CodeBlock.h"
 #include "Completion.h"
+#include "ConfigFile.h"
 #include "DOMJITGetterSetter.h"
 #include "DOMJITPatchpoint.h"
 #include "DOMJITPatchpointParams.h"
@@ -3762,6 +3763,12 @@
     // comes first.
     CommandLine options(argc, argv);
 
+    if (Options::configFile()) {
+        ConfigFile configFile(Options::configFile());
+        configFile.setProcessName("jsc");
+        configFile.parse();
+    }
+
     // Initialize JSC before getting VM.
     WTF::initializeMainThread();
     JSC::initializeThreading();
diff --git a/Source/JavaScriptCore/runtime/ConfigFile.cpp b/Source/JavaScriptCore/runtime/ConfigFile.cpp
new file mode 100644
index 0000000..cd61d95
--- /dev/null
+++ b/Source/JavaScriptCore/runtime/ConfigFile.cpp
@@ -0,0 +1,413 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ConfigFile.h"
+
+#include "Options.h"
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <wtf/ASCIICType.h>
+#include <wtf/DataLog.h>
+#include <wtf/StringExtras.h>
+#include <wtf/text/StringBuilder.h>
+
+namespace JSC {
+
+static const size_t s_processNameMax = 128;
+#if PLATFORM(WIN)
+static const size_t s_maxPathLength = 260; // Windows value for "MAX_PATH"
+#else
+static const size_t s_maxPathLength = PATH_MAX;
+#endif
+char ConfigFile::s_processName[s_processNameMax + 1] = { 0 };
+char ConfigFile::s_parentProcessName[s_processNameMax + 1] = { 0 };
+
+class ConfigFileScanner {
+public:
+    ConfigFileScanner(const char* filename)
+        : m_filename(filename)
+        , m_lineNumber(0)
+    {
+        m_srcPtr = &m_buffer[0];
+        m_bufferEnd = &m_buffer[0];
+    }
+
+    bool start()
+    {
+        m_file = fopen(m_filename, "r");
+        if (!m_file) {
+            dataLogF("Failed to open file JSC Config file '%s'.\n", m_filename);
+            return false;
+        }
+
+        return true;
+    }
+
+    unsigned lineNumber()
+    {
+        return m_lineNumber;
+    }
+
+    const char* currentBuffer()
+    {
+        if (!m_srcPtr || m_srcPtr == m_bufferEnd)
+            return "";
+
+        return m_srcPtr;
+    }
+
+    bool atFileEnd()
+    {
+        if (!fillBufferIfNeeded())
+            return true;
+
+        return false;
+    }
+
+    bool tryConsume(char c)
+    {
+        if (!fillBufferIfNeeded())
+            return false;
+
+        if (c == *m_srcPtr) {
+            m_srcPtr++;
+            return true;
+        }
+
+        return false;
+    }
+
+    template <size_t length>
+    bool tryConsume(const char (&token) [length])
+    {
+        if (!fillBufferIfNeeded())
+            return false;
+
+        size_t tokenLength = length - 1;
+        if (!strncmp(m_srcPtr, token, tokenLength)) {
+            m_srcPtr += tokenLength;
+            return true;
+        }
+
+        return false;
+    }
+
+    char* tryConsumeString()
+    {
+        if (!fillBufferIfNeeded())
+            return nullptr;
+
+        if (*m_srcPtr != '"')
+            return nullptr;
+
+        char* stringStart = ++m_srcPtr;
+
+        char* stringEnd = strchr(m_srcPtr, '"');
+        if (stringEnd) {
+            *stringEnd = '\0';
+            m_srcPtr = stringEnd + 1;
+            return stringStart;
+        }
+
+        return nullptr;
+    }
+
+    char* tryConsumeUpto(bool& foundChar, char c)
+    {
+        if (!fillBufferIfNeeded())
+            return nullptr;
+
+        char* start = m_srcPtr;
+        foundChar = false;
+
+        char* cPosition = strchr(m_srcPtr, c);
+        if (cPosition) {
+            *cPosition = '\0';
+            m_srcPtr = cPosition + 1;
+            foundChar = true;
+        } else
+            m_srcPtr = m_bufferEnd;
+
+        return start;
+    }
+
+private:
+    bool fillBufferIfNeeded()
+    {
+        if (!m_srcPtr)
+            return false;
+
+        while (true) {
+            while (m_srcPtr != m_bufferEnd && isASCIISpace(*m_srcPtr))
+                m_srcPtr++;
+
+            if (m_srcPtr != m_bufferEnd)
+                break;
+
+            if (!fillBuffer())
+                return false;
+        }
+
+        return true;
+    }
+
+    bool fillBuffer()
+    {
+        do {
+            m_srcPtr = fgets(m_buffer, sizeof(m_buffer), m_file);
+            if (!m_srcPtr) {
+                fclose(m_file);
+                return false;
+            }
+
+            m_lineNumber++;
+
+            m_bufferEnd = strchr(m_srcPtr, '#');
+
+            if (m_bufferEnd)
+                *m_bufferEnd = '\0';
+            else {
+                m_bufferEnd = m_srcPtr + strlen(m_srcPtr);
+                if (m_bufferEnd > m_srcPtr && m_bufferEnd[-1] == '\n') {
+                    m_bufferEnd--;
+                    *m_bufferEnd = '\0';
+                }
+            }
+        } while (m_bufferEnd == m_srcPtr);
+
+        return true;
+    }
+
+    const char* m_filename;
+    unsigned m_lineNumber;
+    FILE* m_file;
+    char m_buffer[BUFSIZ];
+    char* m_srcPtr;
+    char* m_bufferEnd;
+};
+
+ConfigFile::ConfigFile(const char* filename)
+    : m_filename(filename)
+{
+}
+
+void ConfigFile::setProcessName(const char* processName)
+{
+    strncpy(s_processName, processName, s_processNameMax);
+}
+
+void ConfigFile::setParentProcessName(const char* parentProcessName)
+{
+    strncpy(s_parentProcessName, parentProcessName, s_processNameMax);
+}
+
+void ConfigFile::parse()
+{
+    enum StatementNesting { TopLevelStatment, NestedStatement, NestedStatementFailedCriteria };
+    enum ParseResult { ParseOK, ParseError, NestedStatementDone };
+
+    ConfigFileScanner scanner(m_filename);
+
+    if (!scanner.start())
+        return;
+
+    char logPathname[s_maxPathLength + 1] = { 0 };
+
+    StringBuilder jscOptionsBuilder;
+
+    auto parseLogFile = [&](StatementNesting statementNesting) {
+        char* filename = nullptr;
+        if (scanner.tryConsume('=') && (filename = scanner.tryConsumeString())) {
+            if (statementNesting != NestedStatementFailedCriteria)
+                strncpy(logPathname, filename, s_maxPathLength);
+
+            return ParseOK;
+        }
+
+        return ParseError;
+    };
+
+    auto parseJSCOptions = [&](StatementNesting statementNesting) {
+        if (scanner.tryConsume('{')) {
+            StringBuilder builder;
+
+            bool foundClosingBrace = false;
+            char* currentLine = nullptr;
+
+            while ((currentLine = scanner.tryConsumeUpto(foundClosingBrace, '}'))) {
+                char* p = currentLine;
+
+                do {
+                    if (foundClosingBrace && !*p)
+                        break;
+
+                    char* optionNameStart = p;
+
+                    while (*p && !isASCIISpace(*p) && *p != '=')
+                        p++;
+
+                    builder.append(optionNameStart, p - optionNameStart);
+
+                    while (*p && isASCIISpace(*p) && *p != '=')
+                        p++;
+
+                    if (!*p)
+                        return ParseError;
+                    p++; // Advance past the '='
+
+                    builder.append('=');
+
+                    while (*p && isASCIISpace(*p))
+                        p++;
+
+                    if (!*p)
+                        return ParseError;
+
+                    char* optionValueStart = p;
+
+                    while (*p && !isASCIISpace(*p))
+                        p++;
+
+                    builder.append(optionValueStart, p - optionValueStart);
+                    builder.append('\n');
+
+                    while (*p && isASCIISpace(*p))
+                        p++;
+                } while (*p);
+
+                if (foundClosingBrace)
+                    break;
+            }
+
+            if (statementNesting != NestedStatementFailedCriteria)
+                jscOptionsBuilder.append(builder);
+
+            return ParseOK;
+        }
+
+        return ParseError;
+    };
+
+    auto parseNestedStatement = [&](StatementNesting statementNesting) {
+        if (scanner.tryConsume("jscOptions"))
+            return parseJSCOptions(statementNesting);
+
+        if (scanner.tryConsume("logFile"))
+            return parseLogFile(statementNesting);
+
+        if (scanner.tryConsume('}'))
+            return NestedStatementDone;
+
+        return ParseError;
+    };
+
+    auto parsePredicate = [&](bool& predicateMatches, const char* matchValue) {
+        char* predicateValue = nullptr;
+        if (scanner.tryConsume("==")
+            && (predicateValue = scanner.tryConsumeString()) && matchValue) {
+                predicateMatches = !strcmp(predicateValue, matchValue);
+                return true;
+        }
+
+        return false;
+    };
+
+    auto parseConditionalBlock = [&](StatementNesting statementNesting) {
+        if (statementNesting == NestedStatement) {
+            StatementNesting subNesting = NestedStatement;
+
+            while (true) {
+                bool predicateMatches;
+                const char* actualValue = nullptr;
+
+                if (scanner.tryConsume("processName"))
+                    actualValue = s_processName;
+                else if (scanner.tryConsume("parentProcessName"))
+                    actualValue = s_parentProcessName;
+                else if (scanner.tryConsume("build"))
+#ifndef NDEBUG
+                    actualValue = "Debug";
+#else
+                    actualValue = "Release";
+#endif
+                else
+                    return ParseError;
+
+                if (parsePredicate(predicateMatches, actualValue)) {
+                    if (!predicateMatches)
+                        subNesting = NestedStatementFailedCriteria;
+
+                    if (!scanner.tryConsume("&&"))
+                        break;
+                }
+            }
+
+            if (!scanner.tryConsume('{'))
+                return ParseError;
+
+            ParseResult parseResult = ParseOK;
+            while (parseResult == ParseOK && !scanner.atFileEnd())
+                parseResult = parseNestedStatement(subNesting);
+
+            if (parseResult == NestedStatementDone)
+                return ParseOK;
+        }
+
+        return ParseError;
+    };
+
+    auto parseStatement = [&](StatementNesting statementNesting) {
+        if (scanner.tryConsume("jscOptions"))
+            return parseJSCOptions(statementNesting);
+
+        if (scanner.tryConsume("logFile"))
+            return parseLogFile(statementNesting);
+
+        if (statementNesting == TopLevelStatment)
+            return parseConditionalBlock(NestedStatement);
+
+        return ParseError;
+    };
+
+    ParseResult parseResult = ParseOK;
+
+    while (parseResult == ParseOK && !scanner.atFileEnd())
+        parseResult = parseStatement(TopLevelStatment);
+
+    if (parseResult == ParseOK) {
+        if (strlen(logPathname))
+            WTF::setDataFile(logPathname);
+
+        if (!jscOptionsBuilder.isEmpty()) {
+            const char* optionsStr = jscOptionsBuilder.toString().utf8().data();
+            Options::setOptions(optionsStr);
+        }
+    } else
+        WTF::dataLogF("Error in JSC Config file on or near line %u, parsing '%s'\n", scanner.lineNumber(), scanner.currentBuffer());
+}
+
+} // namespace JSC
diff --git a/Source/JavaScriptCore/runtime/ConfigFile.h b/Source/JavaScriptCore/runtime/ConfigFile.h
new file mode 100644
index 0000000..a8964f4
--- /dev/null
+++ b/Source/JavaScriptCore/runtime/ConfigFile.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+namespace JSC {
+
+class ConfigFile {
+public:
+    JS_EXPORT_PRIVATE ConfigFile(const char*);
+
+    JS_EXPORT_PRIVATE static void setProcessName(const char*);
+    JS_EXPORT_PRIVATE static void setParentProcessName(const char*);
+    JS_EXPORT_PRIVATE void parse();
+
+private:
+    static char s_processName[];
+    static char s_parentProcessName[];
+
+    const char* m_filename;
+};
+
+} // namespace JSC
diff --git a/Source/JavaScriptCore/runtime/Options.cpp b/Source/JavaScriptCore/runtime/Options.cpp
index f1c2f13..247b856 100644
--- a/Source/JavaScriptCore/runtime/Options.cpp
+++ b/Source/JavaScriptCore/runtime/Options.cpp
@@ -51,9 +51,6 @@
 #include "MacroAssemblerX86.h"
 #endif
 
-#define USE_OPTIONS_FILE 0
-#define OPTIONS_FILENAME "/tmp/jsc.options"
-
 namespace JSC {
 
 namespace {
@@ -496,31 +493,6 @@
                 ; // Deconfuse editors that do auto indentation
 #endif
     
-#if USE(OPTIONS_FILE)
-            {
-                const char* filename = OPTIONS_FILENAME;
-                FILE* optionsFile = fopen(filename, "r");
-                if (!optionsFile) {
-                    dataLogF("Failed to open file %s. Did you add the file-read-data entitlement to WebProcess.sb?\n", filename);
-                    return;
-                }
-                
-                StringBuilder builder;
-                char* line;
-                char buffer[BUFSIZ];
-                while ((line = fgets(buffer, sizeof(buffer), optionsFile)))
-                    builder.append(buffer);
-                
-                const char* optionsStr = builder.toString().utf8().data();
-                dataLogF("Setting options: %s\n", optionsStr);
-                setOptions(optionsStr);
-                
-                int result = fclose(optionsFile);
-                if (result)
-                    dataLogF("Failed to close file %s: %s\n", filename, strerror(errno));
-            }
-#endif
-
             recomputeDependentOptions();
 
             // Do range checks where needed and make corrections to the options:
@@ -628,7 +600,12 @@
         }
     }
 
+    recomputeDependentOptions();
+
     dumpOptionsIfNeeded();
+
+    ensureOptionsAreCoherent();
+
     return success;
 }
 
diff --git a/Source/JavaScriptCore/runtime/Options.h b/Source/JavaScriptCore/runtime/Options.h
index c4bf8fa..01adf07 100644
--- a/Source/JavaScriptCore/runtime/Options.h
+++ b/Source/JavaScriptCore/runtime/Options.h
@@ -103,6 +103,7 @@
 #define JSC_OPTIONS(v) \
     v(bool, validateOptions, false, Normal, "crashes if mis-typed JSC options were passed to the VM") \
     v(unsigned, dumpOptions, 0, Normal, "dumps JSC options (0 = None, 1 = Overridden only, 2 = All, 3 = Verbose)") \
+    v(optionString, configFile, nullptr, Normal, "file to configure JSC options and logging location") \
     \
     v(bool, useLLInt,  true, Normal, "allows the LLINT to be used if true") \
     v(bool, useJIT,    true, Normal, "allows the baseline JIT to be used if true") \
diff --git a/Source/WTF/ChangeLog b/Source/WTF/ChangeLog
index d3bd44f..15fdf01 100644
--- a/Source/WTF/ChangeLog
+++ b/Source/WTF/ChangeLog
@@ -1,3 +1,19 @@
+2017-02-28  Michael Saboff  <msaboff@apple.com>
+
+        Add ability to configure JSC options from a file
+        https://bugs.webkit.org/show_bug.cgi?id=168914
+
+        Reviewed by Filip Pizlo.
+
+        Added the ability to set options and DataLog file location via a configuration file.
+        The pathname can include the printf style "%pid", which will be replaced with the
+        current process id.
+
+        * wtf/DataLog.cpp:
+        (WTF::initializeLogFileOnce):
+        (WTF::setDataFile):
+        * wtf/DataLog.h:
+
 2017-02-27  Andy Estes  <aestes@apple.com>
 
         [iOS] Enable file replacement
diff --git a/Source/WTF/wtf/DataLog.cpp b/Source/WTF/wtf/DataLog.cpp
index cab1414..1c421cf4 100644
--- a/Source/WTF/wtf/DataLog.cpp
+++ b/Source/WTF/wtf/DataLog.cpp
@@ -50,30 +50,22 @@
 
 namespace WTF {
 
-static PrintStream* s_file;
+static const size_t maxPathLength = 1024;
 
+static PrintStream* s_file;
 static uint64_t s_fileData[(sizeof(FilePrintStream) + 7) / 8];
 static uint64_t s_lockedFileData[(sizeof(LockedPrintStream) + 7) / 8];
 
 static void initializeLogFileOnce()
 {
-    FilePrintStream* file = nullptr;
-    
+    const char* filename = nullptr;
+
+    if (s_file)
+        return;
+
 #if DATA_LOG_TO_FILE
-    const long maxPathLength = 1024;
-
-    char filenameSuffix[maxPathLength + 1];
-
-#if PLATFORM(WIN)
-    _snprintf(filenameSuffix, sizeof(filenameSuffix), ".%d.txt", GetCurrentProcessId());
-#else
-    snprintf(filenameSuffix, sizeof(filenameSuffix), ".%d.txt", getpid());
-#endif
-
 #if DATA_LOG_TO_DARWIN_TEMP_DIR
     char filenameBuffer[maxPathLength + 1];
-    unsigned suffixLength = strlen(filenameSuffix);
-
 #if defined(DATA_LOG_FILENAME)
     char* logBasename = strrchr(DATA_LOG_FILENAME, '/');
     if (!logBasename)
@@ -82,13 +74,11 @@
     const char* logBasename = "WTFLog";
 #endif
 
-    const char* filename = nullptr;
-
     bool success = confstr(_CS_DARWIN_USER_TEMP_DIR, filenameBuffer, sizeof(filenameBuffer));
     if (success) {
         // FIXME: Assert that the path ends with a slash instead of adding a slash if it does not exist
         // once <rdar://problem/23579077> is fixed in all iOS Simulator versions that we use.
-        size_t lastComponentLength = strlen(logBasename) + suffixLength;
+        size_t lastComponentLength = strlen(logBasename) + 20; // More than enough for ".<pid>.txt"
         size_t dirnameLength = strlen(filenameBuffer);
         bool shouldAddPathSeparator = filenameBuffer[dirnameLength - 1] != '/' && logBasename[0] != '/';
         if (lastComponentLength + shouldAddPathSeparator <= sizeof(filenameBuffer) - dirnameLength - 1) {
@@ -99,36 +89,23 @@
         }
     }
 #elif defined(DATA_LOG_FILENAME)
-    const char* filename = DATA_LOG_FILENAME;
+    filename = DATA_LOG_FILENAME;
 #else
-    const char* filename = getenv("WTF_DATA_LOG_FILENAME");
+    filename = getenv("WTF_DATA_LOG_FILENAME");
 #endif
     char actualFilename[maxPathLength + 1];
 
-    if (filename) {
+    if (filename && !strstr(filename, "%pid")) {
 #if PLATFORM(WIN)
-        _snprintf(actualFilename, sizeof(actualFilename), "%s%s", filename, filenameSuffix);
+        _snprintf(actualFilename, sizeof(actualFilename), "%s.%%pid.txt", filename);
 #else
-        snprintf(actualFilename, sizeof(actualFilename), "%s%s", filename, filenameSuffix);
+        snprintf(actualFilename, sizeof(actualFilename), "%s.%%pid.txt", filename);
 #endif
-        
-        file = FilePrintStream::open(actualFilename, "w").release();
-        if (file)
-            WTFLogAlways("*** DataLog output to \"%s\" ***\n", actualFilename);
-        else
-            WTFLogAlways("Warning: Could not open DataLog file %s for writing.\n", actualFilename);
+        filename = actualFilename;
     }
 #endif // DATA_LOG_TO_FILE
-    
-    if (!file) {
-        // Use placement new; this makes it easier to use dataLog() to debug
-        // fastMalloc.
-        file = new (s_fileData) FilePrintStream(stderr, FilePrintStream::Borrow);
-    }
-    
-    setvbuf(file->file(), 0, _IONBF, 0); // Prefer unbuffered output, so that we get a full log upon crash or deadlock.
-    
-    s_file = new (s_lockedFileData) LockedPrintStream(std::unique_ptr<FilePrintStream>(file));
+
+    setDataFile(filename);
 }
 
 static void initializeLogFile()
@@ -141,6 +118,60 @@
         });
 }
 
+void setDataFile(const char* path)
+{
+    FilePrintStream* file = nullptr;
+    char formattedPath[maxPathLength + 1];
+    const char* pathToOpen = path;
+
+    if (path) {
+        const char* pidFormat = strstr(path, "%pid");
+        if (pidFormat) {
+            size_t leadingPathLength = pidFormat - path;
+            size_t pathCharactersAvailable = std::min(maxPathLength, leadingPathLength);
+            strncpy(formattedPath, path, pathCharactersAvailable);
+            char* nextDest = formattedPath + pathCharactersAvailable;
+            pathCharactersAvailable = maxPathLength - pathCharactersAvailable;
+            if (pathCharactersAvailable) {
+                int pidTextLength;
+#if PLATFORM(WIN)
+                pidTextLength = _snprintf(nextDest, pathCharactersAvailable, "%d", GetCurrentProcessId());
+#else
+                pidTextLength = snprintf(nextDest, pathCharactersAvailable, "%d", getpid());
+#endif
+                if (pidTextLength < 0 || static_cast<size_t>(pidTextLength) >= pathCharactersAvailable)
+                    pathCharactersAvailable = 0;
+                else {
+                    pathCharactersAvailable -= static_cast<size_t>(pidTextLength);
+                    nextDest += pidTextLength;
+                    strncpy(nextDest, pidFormat + 4, pathCharactersAvailable);
+                }
+            }
+            formattedPath[maxPathLength] = '\0';
+            pathToOpen = formattedPath;
+        }
+
+        file = FilePrintStream::open(pathToOpen, "w").release();
+        if (file)
+            WTFLogAlways("*** DataLog output to \"%s\" ***\n", pathToOpen);
+        else
+            WTFLogAlways("Warning: Could not open DataLog file %s for writing.\n", pathToOpen);
+    }
+
+    if (!file) {
+        // Use placement new; this makes it easier to use dataLog() to debug
+        // fastMalloc.
+        file = new (s_fileData) FilePrintStream(stderr, FilePrintStream::Borrow);
+    }
+
+    setvbuf(file->file(), 0, _IONBF, 0); // Prefer unbuffered output, so that we get a full log upon crash or deadlock.
+
+    if (s_file)
+        s_file->flush();
+
+    s_file = new (s_lockedFileData) LockedPrintStream(std::unique_ptr<FilePrintStream>(file));
+}
+
 PrintStream& dataFile()
 {
     initializeLogFile();
diff --git a/Source/WTF/wtf/DataLog.h b/Source/WTF/wtf/DataLog.h
index 13f913b..103424e 100644
--- a/Source/WTF/wtf/DataLog.h
+++ b/Source/WTF/wtf/DataLog.h
@@ -34,6 +34,7 @@
 namespace WTF {
 
 WTF_EXPORT_PRIVATE PrintStream& dataFile();
+WTF_EXPORT_PRIVATE void setDataFile(const char* path);
 
 WTF_EXPORT_PRIVATE void dataLogFV(const char* format, va_list) WTF_ATTRIBUTE_PRINTF(1, 0);
 WTF_EXPORT_PRIVATE void dataLogF(const char* format, ...) WTF_ATTRIBUTE_PRINTF(1, 2);