Skip to content

Commit

Permalink
Add JSI API and JSIDynamic glue to OSS React Native
Browse files Browse the repository at this point in the history
Summary: This will help abstract the JS engine from React Native

Reviewed By: hramos

Differential Revision: D9328237

fbshipit-source-id: 7b34f55f28e43d83ba24d22e83e836c92ca737a9
  • Loading branch information
mhorowitz authored and facebook-github-bot committed Oct 18, 2018
1 parent c95fdb0 commit e337bca
Show file tree
Hide file tree
Showing 8 changed files with 2,052 additions and 1 deletion.
61 changes: 61 additions & 0 deletions ReactCommon/jsi/BUCK
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# BUILD FILE SYNTAX: SKYLARK

load("//tools/build_defs/oss:rn_defs.bzl", "react_native_xplat_dep", "rn_xplat_cxx_library")

rn_xplat_cxx_library(
name = "jsi",
srcs = [
"jsi.cpp",
],
header_namespace = "jsi",
exported_headers = [
"instrumentation.h",
"jsi.h",
"jsi-inl.h",
],
compiler_flags = [
"-O3",
"-fexceptions",
"-frtti",
"-std=c++14",
"-Wall",
"-Werror",
"-Wextra",
"-Wcast-qual",
"-Wdelete-non-virtual-dtor",
"-Wwrite-strings",
],
cxx_compiler_flags = [
"-Wglobal-constructors",
"-Wmissing-prototypes",
],
fbobjc_compiler_flags = [
"-Wglobal-constructors",
"-Wmissing-prototypes",
],
visibility = ["PUBLIC"],
)

rn_xplat_cxx_library(
name = "JSIDynamic",
srcs = [
"JSIDynamic.cpp",
],
header_namespace = "jsi",
exported_headers = [
"JSIDynamic.h",
],
compiler_flags = [
"-fexceptions",
"-frtti",
],
fbobjc_force_static = True,
visibility = [
"PUBLIC",
],
xcode_public_headers_symlinks = True,
deps = [
"xplat//folly:molly",
react_native_xplat_dep("jsi:jsi"),
],
)
93 changes: 93 additions & 0 deletions ReactCommon/jsi/JSIDynamic.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// Copyright 2004-present Facebook. All Rights Reserved.

#include "JSIDynamic.h"

#include <folly/dynamic.h>
#include <jsi/jsi.h>

using namespace facebook::jsi;

namespace facebook {
namespace jsi {

Value valueFromDynamic(Runtime& runtime, const folly::dynamic& dyn) {
switch (dyn.type()) {
case folly::dynamic::NULLT:
return Value::null();
case folly::dynamic::ARRAY: {
Array ret = Array(runtime, dyn.size());
for (size_t i = 0; i < dyn.size(); ++i) {
ret.setValueAtIndex(runtime, i, valueFromDynamic(runtime, dyn[i]));
}
return std::move(ret);
}
case folly::dynamic::BOOL:
return dyn.getBool();
case folly::dynamic::DOUBLE:
return dyn.getDouble();
case folly::dynamic::INT64:
// Can't use asDouble() here. If the int64 value is too bit to be
// represented precisely as a double, folly will throw an
// exception.
return (double)dyn.getInt();
case folly::dynamic::OBJECT: {
Object ret(runtime);
for (const auto& element : dyn.items()) {
Value value = valueFromDynamic(runtime, element.second);
if (element.first.isNumber() || element.first.isString()) {
ret.setProperty(runtime, element.first.asString().c_str(), value);
}
}
return std::move(ret);
}
case folly::dynamic::STRING:
return String::createFromUtf8(runtime, dyn.getString());
}
CHECK(false);
}

folly::dynamic dynamicFromValue(Runtime& runtime, const Value& value) {
if (value.isUndefined() || value.isNull()) {
return nullptr;
} else if (value.isBool()) {
return value.getBool();
} else if (value.isNumber()) {
return value.getNumber();
} else if (value.isString()) {
return value.getString(runtime).utf8(runtime);
} else {
Object obj = value.getObject(runtime);
if (obj.isArray(runtime)) {
Array array = obj.getArray(runtime);
folly::dynamic ret = folly::dynamic::array();
for (size_t i = 0; i < array.size(runtime); ++i) {
ret.push_back(dynamicFromValue(runtime, array.getValueAtIndex(runtime, i)));
}
return ret;
} else if (obj.isFunction(runtime)) {
throw JSError(runtime, "JS Functions are not convertible to dynamic");
} else {
folly::dynamic ret = folly::dynamic::object();
Array names = obj.getPropertyNames(runtime);
for (size_t i = 0; i < names.size(runtime); ++i) {
String name = names.getValueAtIndex(runtime, i).getString(runtime);
Value prop = obj.getProperty(runtime, name);
if (prop.isUndefined()) {
continue;
}
// The JSC conversion uses JSON.stringify, which substitutes
// null for a function, so we do the same here. Just dropping
// the pair might also work, but would require more testing.
if (prop.isObject() && prop.getObject(runtime).isFunction(runtime)) {
prop = Value::null();
}
ret.insert(
name.utf8(runtime), dynamicFromValue(runtime, std::move(prop)));
}
return ret;
}
}
}

}
}
18 changes: 18 additions & 0 deletions ReactCommon/jsi/JSIDynamic.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright 2004-present Facebook. All Rights Reserved.

#pragma once

#include <folly/dynamic.h>
#include <jsi/jsi.h>

namespace facebook {
namespace jsi {

facebook::jsi::Value valueFromDynamic(
facebook::jsi::Runtime& runtime, const folly::dynamic& dyn);

folly::dynamic dynamicFromValue(facebook::jsi::Runtime& runtime,
const facebook::jsi::Value& value);

}
}
73 changes: 73 additions & 0 deletions ReactCommon/jsi/instrumentation.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright 2004-present Facebook. All Rights Reserved.

#pragma once

#include <string>

#include <jsi/jsi.h>

namespace facebook {
namespace jsi {

/// Methods for starting and collecting instrumentation, an \c Instrumentation
/// instance is associated with a particular \c Runtime instance, which it
/// controls the instrumentation of.
class Instrumentation {
public:
virtual ~Instrumentation() = default;

/// Returns GC statistics as a JSON-encoded string, with an object containing
/// "type" and "version" fields outermost. "type" is a string, unique to a
/// particular implementation of \c jsi::Instrumentation, and "version" is a
/// number to indicate any revision to that implementation and its output
/// format.
///
/// \pre This call can only be made on the instrumentation instance of a
/// runtime initialised to collect GC statistics.
///
/// \post All cumulative measurements mentioned in the output are accumulated
/// across the entire lifetime of the Runtime.
///
/// \return the GC statistics collected so far, as a JSON-encoded string.
virtual std::string getRecordedGCStats() = 0;

/// Request statistics about the current state of the runtime's heap. This
/// function can be called at any time, and should produce information that is
/// correct at the instant it is called (i.e, not stale).
///
/// \return a jsi Value containing whichever statistics the runtime supports
/// for its heap.
virtual Value getHeapInfo(bool includeExpensive) = 0;

/// perform a full garbage collection
virtual void collectGarbage() = 0;

/// Captures the heap to a file
///
/// \param path to save the heap capture
///
/// \param compact Whether the JSON should be compact or pretty
///
/// \return true iff the heap capture succeeded
virtual bool createSnapshotToFile(const std::string& path, bool compact) = 0;

/// Write a trace of bridge traffic to the given file name.
virtual void writeBridgeTrafficTraceToFile(
const std::string& fileName) const = 0;

/// Write basic block profile trace to the given file name.
virtual void writeBasicBlockProfileTraceToFile(
const std::string& fileName) const = 0;

/// Enable sampling profiler.
virtual void enableSamplingProfiler() const = 0;

/// Dump sampled stack trace to the given file name.
virtual void dumpSampledTraceToFile(const std::string& fileName) const = 0;

/// Dump external profiler symbols to the given file name.
virtual void dumpProfilerSymbolsToFile(const std::string& fileName) const = 0;
};

} // namespace jsi
} // namespace facebook
Loading

0 comments on commit e337bca

Please sign in to comment.