]> git.saurik.com Git - apple/javascriptcore.git/blobdiff - tools/FunctionOverrides.cpp
JavaScriptCore-7601.1.46.3.tar.gz
[apple/javascriptcore.git] / tools / FunctionOverrides.cpp
diff --git a/tools/FunctionOverrides.cpp b/tools/FunctionOverrides.cpp
new file mode 100644 (file)
index 0000000..2034d60
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2015 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 "FunctionOverrides.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <wtf/DataLog.h>
+#include <wtf/NeverDestroyed.h>
+#include <wtf/text/CString.h>
+#include <wtf/text/StringBuilder.h>
+#include <wtf/text/StringHash.h>
+
+namespace JSC {
+
+/*
+  The overrides file defines function bodies that we will want to override with
+  a replacement for debugging purposes. The overrides file may contain
+  'override' and 'with' clauses like these:
+
+     // Example 1: function foo1(a)
+     override !@#$%{ print("In foo1"); }!@#$%
+     with abc{
+         print("I am overridden");
+     }abc
+
+     // Example 2: function foo2(a)
+     override %%%{
+         print("foo2's body has a string with }%% in it.");
+         // Because }%% appears in the function body here, we cannot use
+         // %% or % as the delimiter. %%% is ok though.
+     }%%%
+     with %%%{
+         print("Overridden foo2");
+     }%%%
+
+  1. Comments are lines starting with //.  All comments will be ignored.
+
+  2. An 'override' clause is used to specify the original function body we
+     want to override. The with clause is used to specify the overriding
+     function body.
+
+     An 'override' clause must be followed immediately by a 'with' clause.
+
+  3. An 'override' clause must be of the form:
+         override <delimiter>{...function body...}<delimiter>
+
+     The override keyword must be at the start of the line.
+
+     <delimiter> may be any string of any ASCII characters (except for '{',
+     '}', and whitespace characters) as long as the pattern of "}<delimiter>"
+     does not appear in the function body e.g. the override clause of Example 2
+     above illustrates this.
+
+     The start and end <delimiter> must be identical.
+
+     The space between the override keyword and the start <delimiter> is
+     required.
+
+     All characters between the pair of delimiters will be considered to
+     be part of the function body string. This allows us to also work
+     with script source that are multi-lined i.e. newlines are allowed.
+     
+  4. A 'with' clause is identical in form to an 'override' clause except that
+     it uses the 'with' keyword instead of the 'override' keyword.
+ */
+
+FunctionOverrides& FunctionOverrides::overrides()
+{
+    static LazyNeverDestroyed<FunctionOverrides> overrides;
+    static std::once_flag initializeListFlag;
+    std::call_once(initializeListFlag, [] {
+        const char* overridesFileName = Options::functionOverrides();
+        overrides.construct(overridesFileName);
+    });
+    return overrides;
+}
+    
+FunctionOverrides::FunctionOverrides(const char* overridesFileName)
+{
+    parseOverridesInFile(overridesFileName);
+}
+
+static void initializeOverrideInfo(const SourceCode& origCode, const String& newBody, FunctionOverrides::OverrideInfo& info)
+{
+    String origProviderStr = origCode.provider()->source();
+    unsigned origBraceStart = origCode.startOffset();
+    unsigned origFunctionStart = origProviderStr.reverseFind("function", origBraceStart);
+    unsigned headerLength = origBraceStart - origFunctionStart;
+    String origHeader = origProviderStr.substring(origFunctionStart, headerLength);
+
+    String newProviderStr;
+    newProviderStr.append(origHeader);
+    newProviderStr.append(newBody);
+
+    RefPtr<SourceProvider> newProvider = StringSourceProvider::create(newProviderStr, "<overridden>");
+
+    info.firstLine = 1;
+    info.lineCount = 1; // Faking it. This doesn't really matter for now.
+    info.startColumn = 1;
+    info.endColumn = 1; // Faking it. This doesn't really matter for now.
+    info.parametersStartOffset = newProviderStr.find("(");
+    info.typeProfilingStartOffset = newProviderStr.find("{");
+    info.typeProfilingEndOffset = newProviderStr.length() - 1;
+
+    info.sourceCode =
+        SourceCode(newProvider.release(), info.typeProfilingStartOffset, info.typeProfilingEndOffset + 1, 1, 1);
+}
+    
+bool FunctionOverrides::initializeOverrideFor(const SourceCode& origCode, FunctionOverrides::OverrideInfo& result)
+{
+    ASSERT(Options::functionOverrides());
+    FunctionOverrides& overrides = FunctionOverrides::overrides();
+
+    auto it = overrides.m_entries.find(origCode.toString());
+    if (it == overrides.m_entries.end())
+        return false;
+
+    initializeOverrideInfo(origCode, it->value, result);
+    return true;
+}
+
+#define SYNTAX_ERROR "SYNTAX ERROR"
+#define IO_ERROR "IO ERROR"
+#define FAIL_WITH_ERROR(error, errorMessageInBrackets) \
+    do { \
+        dataLog("functionOverrides ", error, ": "); \
+        dataLog errorMessageInBrackets; \
+        exit(EXIT_FAILURE); \
+    } while (false)
+
+static bool hasDisallowedCharacters(const char* str, size_t length)
+{
+    while (length--) {
+        char c = *str++;
+        // '{' is also disallowed, but we don't need to check for it because
+        // parseClause() searches for '{' as the end of the start delimiter.
+        // As a result, the parsed delimiter string will never include '{'.
+        if (c == '}' || isASCIISpace(c))
+            return true;
+    }
+    return false;
+}
+
+static String parseClause(const char* keyword, size_t keywordLength, FILE* file, const char* line, char* buffer, size_t bufferSize)
+{
+    const char* keywordPos = strstr(line, keyword);
+    if (!keywordPos)
+        FAIL_WITH_ERROR(SYNTAX_ERROR, ("Expecting '", keyword, "' clause:\n", line, "\n"));
+    if (keywordPos != line)
+        FAIL_WITH_ERROR(SYNTAX_ERROR, ("Cannot have any characters before '", keyword, "':\n", line, "\n"));
+    if (line[keywordLength] != ' ')
+        FAIL_WITH_ERROR(SYNTAX_ERROR, ("'", keyword, "' must be followed by a ' ':\n", line, "\n"));
+
+    const char* delimiterStart = &line[keywordLength + 1];
+    const char* delimiterEnd = strstr(delimiterStart, "{");
+    if (!delimiterEnd)
+        FAIL_WITH_ERROR(SYNTAX_ERROR, ("Missing { after '", keyword, "' clause start delimiter:\n", line, "\n"));
+    
+    size_t delimiterLength = delimiterEnd - delimiterStart;
+    String delimiter(delimiterStart, delimiterLength);
+
+    if (hasDisallowedCharacters(delimiterStart, delimiterLength))
+        FAIL_WITH_ERROR(SYNTAX_ERROR, ("Delimiter '", delimiter, "' cannot have '{', '}', or whitespace:\n", line, "\n"));
+    
+    String terminatorString;
+    terminatorString.append("}");
+    terminatorString.append(delimiter);
+
+    const char* terminator = terminatorString.ascii().data();
+    line = delimiterEnd; // Start from the {.
+
+    StringBuilder builder;
+    do {
+        const char* p = strstr(line, terminator);
+        if (p) {
+            if (p[strlen(terminator)] != '\n')
+                FAIL_WITH_ERROR(SYNTAX_ERROR, ("Unexpected characters after '", keyword, "' clause end delimiter '", delimiter, "':\n", line, "\n"));
+
+            builder.append(line, p - line + 1);
+            return builder.toString();
+        }
+        builder.append(line);
+
+    } while ((line = fgets(buffer, bufferSize, file)));
+
+    FAIL_WITH_ERROR(SYNTAX_ERROR, ("'", keyword, "' clause end delimiter '", delimiter, "' not found:\n", builder.toString(), "\n", "Are you missing a '}' before the delimiter?\n"));
+}
+
+void FunctionOverrides::parseOverridesInFile(const char* fileName)
+{
+    if (!fileName)
+        return;
+    
+    FILE* file = fopen(fileName, "r");
+    if (!file)
+        FAIL_WITH_ERROR(IO_ERROR, ("Failed to open file ", fileName, ". Did you add the file-read-data entitlement to WebProcess.sb?\n"));
+
+    char* line;
+    char buffer[BUFSIZ];
+    while ((line = fgets(buffer, sizeof(buffer), file))) {
+        if (strstr(line, "//") == line)
+            continue;
+
+        if (line[0] == '\n' || line[0] == '\0')
+            continue;
+
+        size_t keywordLength;
+        
+        keywordLength = sizeof("override") - 1;
+        String keyStr = parseClause("override", keywordLength, file, line, buffer, sizeof(buffer));
+
+        line = fgets(buffer, sizeof(buffer), file);
+
+        keywordLength = sizeof("with") - 1;
+        String valueStr = parseClause("with", keywordLength, file, line, buffer, sizeof(buffer));
+
+        m_entries.add(keyStr, valueStr);
+    }
+    
+    int result = fclose(file);
+    if (result)
+        dataLogF("Failed to close file %s: %s\n", fileName, strerror(errno));
+}
+    
+} // namespace JSC
+