FunctionOverrides.cpp [plain text]
#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 {
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);
auto newProvider = StringSourceProvider::create(newProviderStr, "<overridden>");
info.firstLine = 1;
info.lineCount = 1; info.startColumn = 1;
info.endColumn = 1; 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++;
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;
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));
}
}