mirror of
https://github.com/darlinghq/darling-JavaScriptCore.git
synced 2025-04-17 06:20:04 +00:00
268 lines
10 KiB
C++
268 lines
10 KiB
C++
/*
|
|
* Copyright (C) 2015-2016 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 "Options.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);
|
|
}
|
|
|
|
void FunctionOverrides::reinstallOverrides()
|
|
{
|
|
FunctionOverrides& overrides = FunctionOverrides::overrides();
|
|
const char* overridesFileName = Options::functionOverrides();
|
|
overrides.clear();
|
|
overrides.parseOverridesInFile(overridesFileName);
|
|
}
|
|
|
|
static void initializeOverrideInfo(const SourceCode& origCode, const String& newBody, FunctionOverrides::OverrideInfo& info)
|
|
{
|
|
String origProviderStr = origCode.provider()->source().toString();
|
|
unsigned origStart = origCode.startOffset();
|
|
unsigned origFunctionStart = origProviderStr.reverseFind("function", origStart);
|
|
unsigned origBraceStart = origProviderStr.find("{", origStart);
|
|
unsigned headerLength = origBraceStart - origFunctionStart;
|
|
String origHeader = origProviderStr.substring(origFunctionStart, headerLength);
|
|
|
|
String newProviderStr;
|
|
newProviderStr.append(origHeader);
|
|
newProviderStr.append(newBody);
|
|
|
|
Ref<SourceProvider> newProvider = StringSourceProvider::create(newProviderStr, SourceOrigin { "<overridden>" }, URL({ }, "<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(WTFMove(newProvider), info.parametersStartOffset, info.typeProfilingEndOffset + 1, 1, 1);
|
|
}
|
|
|
|
bool FunctionOverrides::initializeOverrideFor(const SourceCode& origCode, FunctionOverrides::OverrideInfo& result)
|
|
{
|
|
ASSERT(Options::functionOverrides());
|
|
FunctionOverrides& overrides = FunctionOverrides::overrides();
|
|
|
|
String sourceString = origCode.view().toString();
|
|
size_t sourceBodyStart = sourceString.find('{');
|
|
if (sourceBodyStart == notFound)
|
|
return false;
|
|
String sourceBodyString = sourceString.substring(sourceBodyStart);
|
|
|
|
auto it = overrides.m_entries.find(sourceBodyString);
|
|
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);
|
|
|
|
CString terminatorCString = terminatorString.ascii();
|
|
const char* terminator = terminatorCString.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.appendCharacters(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
|
|
|