diff options
author | Konstantin Tokarev <annulen@yandex.ru> | 2016-08-25 19:20:41 +0300 |
---|---|---|
committer | Konstantin Tokarev <annulen@yandex.ru> | 2017-02-02 12:30:55 +0000 |
commit | 6882a04fb36642862b11efe514251d32070c3d65 (patch) | |
tree | b7959826000b061fd5ccc7512035c7478742f7b0 /Source/JavaScriptCore/tools/FunctionOverrides.cpp | |
parent | ab6df191029eeeb0b0f16f127d553265659f739e (diff) | |
download | qtwebkit-6882a04fb36642862b11efe514251d32070c3d65.tar.gz |
Imported QtWebKit TP3 (git b57bc6801f1876c3220d5a4bfea33d620d477443)
Change-Id: I3b1d8a2808782c9f34d50240000e20cb38d3680f
Reviewed-by: Konstantin Tokarev <annulen@yandex.ru>
Diffstat (limited to 'Source/JavaScriptCore/tools/FunctionOverrides.cpp')
-rw-r--r-- | Source/JavaScriptCore/tools/FunctionOverrides.cpp | 250 |
1 files changed, 250 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/tools/FunctionOverrides.cpp b/Source/JavaScriptCore/tools/FunctionOverrides.cpp new file mode 100644 index 000000000..2e328c167 --- /dev/null +++ b/Source/JavaScriptCore/tools/FunctionOverrides.cpp @@ -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().toString(); + 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.view().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 + |