Add new FileSourceProvider

Summary: Add a new interface to JSC that allows loading a file lazily from disk, i.e. using mmap, instead of loading the whole file upfront and copying into the VM.

Reviewed By: michalgr

Differential Revision: D3534042

fbshipit-source-id: 98b193cc7b7e33248073e2556ea94ce3391507c7
This commit is contained in:
Tadeu Zagallo
2016-07-11 06:52:06 -07:00
committed by Facebook Github Bot 9
parent e5650560c0
commit 5d06918d07
11 changed files with 291 additions and 50 deletions

View File

@@ -7,6 +7,8 @@
#include <string>
#include <vector>
#include <sys/mman.h>
#include <folly/dynamic.h>
#include "JSModulesUnbundle.h"
@@ -134,6 +136,68 @@ private:
size_t m_size;
};
class JSBigMmapString : public JSBigString {
public:
enum class Encoding {
Unknown,
Ascii,
Utf8,
Utf16,
};
JSBigMmapString(int fd, size_t size, const uint8_t sha1[20], Encoding encoding) :
m_fd(fd),
m_size(size),
m_encoding(encoding),
m_str(nullptr)
{
memcpy(m_hash, sha1, 20);
}
~JSBigMmapString() {
if (m_str) {
CHECK(munmap((void *)m_str, m_size) != -1);
}
close(m_fd);
}
bool isAscii() const override {
return m_encoding == Encoding::Ascii;
}
const char* c_str() const override {
if (!m_str) {
m_str = (const char *)mmap(0, m_size, PROT_READ, MAP_SHARED, m_fd, 0);
CHECK(m_str != MAP_FAILED);
}
return m_str;
}
size_t size() const override {
return m_size;
}
int fd() const {
return m_fd;
}
const uint8_t* hash() const {
return m_hash;
}
Encoding encoding() const {
return m_encoding;
}
private:
int m_fd;
size_t m_size;
uint8_t m_hash[20];
Encoding m_encoding;
mutable const char *m_str;
};
class JSExecutor {
public:
/**

View File

@@ -253,10 +253,33 @@ void JSCExecutor::terminateOnJSVMThread() {
m_context = nullptr;
}
#ifdef WITH_FBJSCEXTENSIONS
static void loadApplicationSource(
const JSGlobalContextRef context,
const JSBigMmapString* script,
const std::string& sourceURL) {
String jsSourceURL(sourceURL.c_str());
bool is8bit = script->encoding() == JSBigMmapString::Encoding::Ascii || script->encoding() == JSBigMmapString::Encoding::Utf8;
JSSourceCodeRef sourceCode = JSCreateSourceCode(script->fd(), script->size(), jsSourceURL, script->hash(), is8bit);
evaluateSourceCode(context, sourceCode, jsSourceURL);
JSReleaseSourceCode(sourceCode);
}
#endif
void JSCExecutor::loadApplicationScript(std::unique_ptr<const JSBigString> script, std::string sourceURL) throw(JSException) {
SystraceSection s("JSCExecutor::loadApplicationScript",
"sourceURL", sourceURL);
#ifdef WITH_FBJSCEXTENSIONS
if (auto source = dynamic_cast<const JSBigMmapString *>(script.get())) {
loadApplicationSource(m_context, source, sourceURL);
bindBridge();
flush();
ReactMarker::logMarker("CREATE_REACT_CONTEXT_END");
return;
}
#endif
#ifdef WITH_FBSYSTRACE
fbsystrace_begin_section(
TRACE_TAG_REACT_CXX_BRIDGE,

View File

@@ -44,47 +44,63 @@ JSValueRef evaluateScript(JSContextRef context, JSStringRef script, JSStringRef
JSValueRef exn, result;
result = JSEvaluateScript(context, script, NULL, source, 0, &exn);
if (result == nullptr) {
Value exception = Value(context, exn);
std::string exceptionText = exception.toString().str();
// The null/empty-ness of source tells us if the JS came from a
// file/resource, or was a constructed statement. The location
// info will include that source, if any.
std::string locationInfo = source != nullptr ? String::ref(source).str() : "";
Object exObject = exception.asObject();
auto line = exObject.getProperty("line");
if (line != nullptr && line.isNumber()) {
if (locationInfo.empty() && line.asInteger() != 1) {
// If there is a non-trivial line number, but there was no
// location info, we include a placeholder, and the line
// number.
locationInfo = folly::to<std::string>("<unknown file>:", line.asInteger());
} else if (!locationInfo.empty()) {
// If there is location info, we always include the line
// number, regardless of its value.
locationInfo += folly::to<std::string>(":", line.asInteger());
}
}
if (!locationInfo.empty()) {
exceptionText += " (" + locationInfo + ")";
}
LOG(ERROR) << "Got JS Exception: " << exceptionText;
Value jsStack = exObject.getProperty("stack");
if (jsStack.isNull() || !jsStack.isString()) {
throwJSExecutionException("%s", exceptionText.c_str());
} else {
LOG(ERROR) << "Got JS Stack: " << jsStack.toString().str();
throwJSExecutionExceptionWithStack(
exceptionText.c_str(), jsStack.toString().str().c_str());
}
formatAndThrowJSException(context, exn, source);
}
return result;
}
#if WITH_FBJSCEXTENSIONS
JSValueRef evaluateSourceCode(JSContextRef context, JSSourceCodeRef source, JSStringRef sourceURL) {
JSValueRef exn, result;
result = JSEvaluateSourceCode(context, source, NULL, &exn);
if (result == nullptr) {
formatAndThrowJSException(context, exn, sourceURL);
}
return result;
}
#endif
void formatAndThrowJSException(JSContextRef context, JSValueRef exn, JSStringRef source) {
Value exception = Value(context, exn);
std::string exceptionText = exception.toString().str();
// The null/empty-ness of source tells us if the JS came from a
// file/resource, or was a constructed statement. The location
// info will include that source, if any.
std::string locationInfo = source != nullptr ? String::ref(source).str() : "";
Object exObject = exception.asObject();
auto line = exObject.getProperty("line");
if (line != nullptr && line.isNumber()) {
if (locationInfo.empty() && line.asInteger() != 1) {
// If there is a non-trivial line number, but there was no
// location info, we include a placeholder, and the line
// number.
locationInfo = folly::to<std::string>("<unknown file>:", line.asInteger());
} else if (!locationInfo.empty()) {
// If there is location info, we always include the line
// number, regardless of its value.
locationInfo += folly::to<std::string>(":", line.asInteger());
}
}
if (!locationInfo.empty()) {
exceptionText += " (" + locationInfo + ")";
}
LOG(ERROR) << "Got JS Exception: " << exceptionText;
Value jsStack = exObject.getProperty("stack");
if (jsStack.isNull() || !jsStack.isString()) {
throwJSExecutionException("%s", exceptionText.c_str());
} else {
LOG(ERROR) << "Got JS Stack: " << jsStack.toString().str();
throwJSExecutionExceptionWithStack(
exceptionText.c_str(), jsStack.toString().str().c_str());
}
}
JSValueRef makeJSError(JSContextRef ctx, const char *error) {
JSValueRef nestedException = nullptr;
JSValueRef args[] = { Value(ctx, String(error)) };

View File

@@ -49,6 +49,18 @@ JSValueRef evaluateScript(
JSStringRef script,
JSStringRef sourceURL);
#if WITH_FBJSCEXTENSIONS
JSValueRef evaluateSourceCode(
JSContextRef ctx,
JSSourceCodeRef source,
JSStringRef sourceURL);
#endif
void formatAndThrowJSException(
JSContextRef ctx,
JSValueRef exn,
JSStringRef sourceURL);
JSValueRef makeJSError(JSContextRef ctx, const char *error);
JSValueRef translatePendingCppExceptionToJSError(JSContextRef ctx, const char *exceptionLocation);