Files
react-native/ReactCommon/hermes/inspector/chrome/cli/main.cpp
T
cpojer d7f5153cd8 Add Hermes support to React Native on Android (#25613)
Summary:
Yesterday we shipped hermesengine.dev as part of the current 0.60 release. This PR brings those changes to master.

## Changelog

[General] [Added] - Added support for Hermes
Pull Request resolved: https://github.com/facebook/react-native/pull/25613

Test Plan:
* CI is green both on GitHub and at FB
* Creating a new app from source can use Hermes on Android

Reviewed By: cpojer

Differential Revision: D16221777

Pulled By: willholen

fbshipit-source-id: aa6be10537863039cb666292465ba2e1d44b64ef
2019-07-25 23:05:53 -07:00

198 lines
4.7 KiB
C++

// Copyright 2004-present Facebook. All Rights Reserved.
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <memory>
#include <thread>
#include <utility>
#include <getopt.h>
#include <folly/json.h>
#include <hermes/hermes.h>
#include <hermes/inspector/RuntimeAdapter.h>
#include <hermes/inspector/chrome/Connection.h>
using ::facebook::react::IRemoteConnection;
namespace fbhermes = ::facebook::hermes;
static const char *usageMessage = R"(hermes-chrome-debug-server script.js
Uses Hermes to evaluate script.js within a debugging session. The process will
wait for Chrome DevTools Protocol requests on stdin and writes responses and
events to stdout.
This can be used with a WebSocket bridge to host a Chrome DevTools Protocol
debug server. For instance, running this:
websocketd --port=9999 hermes-chrome-debug-server script.js
will run a WebSocket server on port 9999 that debugs script.js in Hermes. Chrome
can connect to this debugging session using a URL like this:
chrome-devtools://devtools/bundled/inspector.html?experiments=false&v8only=true&ws=127.0.0.1:9999
Options:
-l, --log: path to a file with pretty-printed protocol logs
-h, --help: this message
)";
static void usage() {
fputs(usageMessage, stderr);
exit(1);
}
static std::string prettify(const std::string &str) {
try {
folly::dynamic obj = folly::parseJson(str);
return folly::toPrettyJson(obj);
} catch (...) {
// pass
}
return str;
}
static FILE *logFile = stderr;
static void setLogFilePath(const char *path) {
logFile = fopen(path, "w");
if (logFile == nullptr) {
perror("fopen couldn't open log file");
exit(1);
}
}
static void log(const std::string &str, bool isReq) {
fprintf(logFile, "%s %s\n\n", isReq ? "=>" : "<=", prettify(str).c_str());
}
static void logRequest(const std::string &str) {
log(str, true);
}
static void sendResponse(const std::string &str) {
log(str, false);
printf("%s\n", str.c_str());
}
static std::string readScriptSource(const char *path) {
std::ifstream stream(path);
return std::string{std::istreambuf_iterator<char>(stream),
std::istreambuf_iterator<char>()};
}
static std::string getUrl(const char *path) {
char absPath[PATH_MAX] = {};
realpath(path, absPath);
return std::string("file://") + absPath;
}
static bool handleScriptSourceRequest(
const std::string &reqStr,
const std::string &scriptSource) {
auto req = folly::parseJson(reqStr);
if (req.at("method") == "Debugger.getScriptSource") {
folly::dynamic result = folly::dynamic::object;
result["scriptSource"] = scriptSource;
folly::dynamic resp = folly::dynamic::object;
resp["id"] = req.at("id");
resp["result"] = std::move(result);
sendResponse(folly::toJson(resp));
return true;
}
return false;
}
class RemoteConnection : public IRemoteConnection {
public:
void onMessage(std::string message) override {
sendResponse(message);
}
void onDisconnect() override {}
};
static void runDebuggerLoop(
fbhermes::inspector::chrome::Connection &conn,
std::string scriptSource) {
conn.connect(std::make_unique<RemoteConnection>());
std::string line;
while (std::getline(std::cin, line)) {
logRequest(line);
if (!handleScriptSourceRequest(line, scriptSource)) {
conn.sendMessage(line);
}
}
}
static void runScript(const std::string &scriptSource, const std::string &url) {
std::shared_ptr<fbhermes::HermesRuntime> runtime(
fbhermes::makeHermesRuntime());
auto adapter =
std::make_unique<fbhermes::inspector::SharedRuntimeAdapter>(runtime);
fbhermes::inspector::chrome::Connection conn(
std::move(adapter), "hermes-chrome-debug-server");
std::thread debuggerLoop(runDebuggerLoop, std::ref(conn), scriptSource);
fbhermes::HermesRuntime::DebugFlags flags{};
runtime->debugJavaScript(scriptSource, url, flags);
debuggerLoop.join();
}
int main(int argc, char **argv) {
const char *shortOpts = "l:h";
const option longOpts[] = {{"log", 1, nullptr, 'l'},
{"help", 0, nullptr, 'h'},
{nullptr, 0, nullptr, 0}};
while (true) {
int opt = getopt_long(argc, argv, shortOpts, longOpts, nullptr);
if (opt == -1) {
break;
}
switch (opt) {
case 'l':
setLogFilePath(optarg);
break;
case 'h':
usage();
break;
default:
fprintf(stderr, "Unrecognized option: %c\n", opt);
usage();
break;
}
}
setbuf(logFile, nullptr);
setbuf(stdout, nullptr);
if (optind + 1 != argc) {
usage();
}
const char *path = argv[optind];
std::string scriptSource = readScriptSource(path);
std::string url = getUrl(path);
runScript(scriptSource, url);
fclose(logFile);
return 0;
}