Add JSI API and JSIDynamic glue to OSS React Native
Summary: This will help abstract the JS engine from React Native Reviewed By: hramos Differential Revision: D9328237 fbshipit-source-id: 7b34f55f28e43d83ba24d22e83e836c92ca737a9
This commit is contained in:
parent
c95fdb0505
commit
e337bcafb0
|
@ -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"),
|
||||||
|
],
|
||||||
|
)
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
|
@ -0,0 +1,306 @@
|
||||||
|
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace facebook {
|
||||||
|
namespace jsi {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
inline Value toValue(Runtime&, std::nullptr_t) {
|
||||||
|
return Value::null();
|
||||||
|
}
|
||||||
|
inline Value toValue(Runtime&, bool b) {
|
||||||
|
return Value(b);
|
||||||
|
}
|
||||||
|
inline Value toValue(Runtime&, double d) {
|
||||||
|
return Value(d);
|
||||||
|
}
|
||||||
|
inline Value toValue(Runtime&, int i) {
|
||||||
|
return Value(i);
|
||||||
|
}
|
||||||
|
inline Value toValue(Runtime& runtime, const char* str) {
|
||||||
|
return String::createFromAscii(runtime, str);
|
||||||
|
}
|
||||||
|
inline Value toValue(Runtime& runtime, const std::string& str) {
|
||||||
|
return String::createFromAscii(runtime, str);
|
||||||
|
}
|
||||||
|
template <typename T>
|
||||||
|
inline Value toValue(Runtime& runtime, const T& other) {
|
||||||
|
static_assert(
|
||||||
|
std::is_base_of<Pointer, T>::value,
|
||||||
|
"This type cannot be converted to Value");
|
||||||
|
return Value(runtime, other);
|
||||||
|
}
|
||||||
|
inline Value toValue(Runtime& runtime, const Value& value) {
|
||||||
|
return Value(runtime, value);
|
||||||
|
}
|
||||||
|
inline Value&& toValue(Runtime&, Value&& value) {
|
||||||
|
return std::move(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline PropNameID toPropNameID(Runtime& runtime, const char* name) {
|
||||||
|
return PropNameID::forAscii(runtime, name);
|
||||||
|
}
|
||||||
|
inline PropNameID toPropNameID(Runtime& runtime, const std::string& name) {
|
||||||
|
return PropNameID::forUtf8(runtime, name);
|
||||||
|
}
|
||||||
|
inline PropNameID&& toPropNameID(Runtime&, PropNameID&& name) {
|
||||||
|
return std::move(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void throwJSError(Runtime&, const char* msg);
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline T Runtime::make(Runtime::PointerValue* pv) {
|
||||||
|
return T(pv);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const Runtime::PointerValue* Runtime::getPointerValue(
|
||||||
|
const jsi::Pointer& pointer) {
|
||||||
|
return pointer.ptr_;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const Runtime::PointerValue* Runtime::getPointerValue(
|
||||||
|
const jsi::Value& value) {
|
||||||
|
return value.data_.pointer.ptr_;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Value Object::getProperty(Runtime& runtime, const char* name) const {
|
||||||
|
return getProperty(runtime, String::createFromAscii(runtime, name));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Value Object::getProperty(Runtime& runtime, const String& name) const {
|
||||||
|
return runtime.getProperty(*this, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Value Object::getProperty(Runtime& runtime, const PropNameID& name)
|
||||||
|
const {
|
||||||
|
return runtime.getProperty(*this, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool Object::hasProperty(Runtime& runtime, const char* name) const {
|
||||||
|
return hasProperty(runtime, String::createFromAscii(runtime, name));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool Object::hasProperty(Runtime& runtime, const String& name) const {
|
||||||
|
return runtime.hasProperty(*this, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool Object::hasProperty(Runtime& runtime, const PropNameID& name)
|
||||||
|
const {
|
||||||
|
return runtime.hasProperty(*this, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void Object::setProperty(Runtime& runtime, const char* name, T&& value) {
|
||||||
|
setProperty(
|
||||||
|
runtime, String::createFromAscii(runtime, name), std::forward<T>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void Object::setProperty(Runtime& runtime, const String& name, T&& value) {
|
||||||
|
setPropertyValue(
|
||||||
|
runtime, name, detail::toValue(runtime, std::forward<T>(value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void Object::setProperty(Runtime& runtime, const PropNameID& name, T&& value) {
|
||||||
|
setPropertyValue(
|
||||||
|
runtime, name, detail::toValue(runtime, std::forward<T>(value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Array Object::getArray(Runtime& runtime) const& {
|
||||||
|
assert(runtime.isArray(*this));
|
||||||
|
(void)runtime; // when assert is disabled we need to mark this as used
|
||||||
|
return Array(runtime.cloneObject(ptr_));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Array Object::getArray(Runtime& runtime) && {
|
||||||
|
assert(runtime.isArray(*this));
|
||||||
|
(void)runtime; // when assert is disabled we need to mark this as used
|
||||||
|
Runtime::PointerValue* value = ptr_;
|
||||||
|
ptr_ = nullptr;
|
||||||
|
return Array(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ArrayBuffer Object::getArrayBuffer(Runtime& runtime) const& {
|
||||||
|
assert(runtime.isArrayBuffer(*this));
|
||||||
|
(void)runtime; // when assert is disabled we need to mark this as used
|
||||||
|
return ArrayBuffer(runtime.cloneObject(ptr_));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ArrayBuffer Object::getArrayBuffer(Runtime& runtime) && {
|
||||||
|
assert(runtime.isArrayBuffer(*this));
|
||||||
|
(void)runtime; // when assert is disabled we need to mark this as used
|
||||||
|
Runtime::PointerValue* value = ptr_;
|
||||||
|
ptr_ = nullptr;
|
||||||
|
return ArrayBuffer(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Function Object::getFunction(Runtime& runtime) const& {
|
||||||
|
assert(runtime.isFunction(*this));
|
||||||
|
return Function(runtime.cloneObject(ptr_));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Function Object::getFunction(Runtime& runtime) && {
|
||||||
|
assert(runtime.isFunction(*this));
|
||||||
|
(void)runtime; // when assert is disabled we need to mark this as used
|
||||||
|
Runtime::PointerValue* value = ptr_;
|
||||||
|
ptr_ = nullptr;
|
||||||
|
return Function(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline bool Object::isHostObject(Runtime& runtime) const {
|
||||||
|
return runtime.isHostObject(*this) &&
|
||||||
|
std::dynamic_pointer_cast<T>(runtime.getHostObject(*this));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline bool Object::isHostObject<HostObject>(Runtime& runtime) const {
|
||||||
|
return runtime.isHostObject(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline std::shared_ptr<T> Object::getHostObject(Runtime& runtime) const {
|
||||||
|
assert(isHostObject<T>(runtime));
|
||||||
|
return std::static_pointer_cast<T>(runtime.getHostObject(*this));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline std::shared_ptr<T> Object::asHostObject(Runtime& runtime) const {
|
||||||
|
if (!isHostObject<T>(runtime)) {
|
||||||
|
detail::throwJSError(runtime, "Object is not a HostObject of desired type");
|
||||||
|
}
|
||||||
|
return std::static_pointer_cast<T>(runtime.getHostObject(*this));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline std::shared_ptr<HostObject> Object::getHostObject<HostObject>(
|
||||||
|
Runtime& runtime) const {
|
||||||
|
assert(runtime.isHostObject(*this));
|
||||||
|
return runtime.getHostObject(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Array Object::getPropertyNames(Runtime& runtime) const {
|
||||||
|
return runtime.getPropertyNames(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Value WeakObject::lock(Runtime& runtime) {
|
||||||
|
return runtime.lockWeakObject(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void Array::setValueAtIndex(Runtime& runtime, size_t i, T&& value) {
|
||||||
|
setValueAtIndexImpl(
|
||||||
|
runtime, i, detail::toValue(runtime, std::forward<T>(value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Value Array::getValueAtIndex(Runtime& runtime, size_t i) const {
|
||||||
|
return runtime.getValueAtIndex(*this, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Function Function::createFromHostFunction(
|
||||||
|
Runtime& runtime,
|
||||||
|
const jsi::PropNameID& name,
|
||||||
|
unsigned int paramCount,
|
||||||
|
jsi::HostFunctionType func) {
|
||||||
|
return runtime.createFunctionFromHostFunction(
|
||||||
|
name, paramCount, std::move(func));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Value Function::call(Runtime& runtime, const Value* args, size_t count)
|
||||||
|
const {
|
||||||
|
return runtime.call(*this, Value::undefined(), args, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Value Function::call(Runtime& runtime, std::initializer_list<Value> args)
|
||||||
|
const {
|
||||||
|
return call(runtime, args.begin(), args.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
inline Value Function::call(Runtime& runtime, Args&&... args) const {
|
||||||
|
// A more awesome version of this would be able to create raw values
|
||||||
|
// which can be used directly as HermesValues, instead of having to
|
||||||
|
// wrap the args in Values and hvFromValue on each to unwrap them.
|
||||||
|
// But this will do for now.
|
||||||
|
return call(runtime, {detail::toValue(runtime, std::forward<Args>(args))...});
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Value Function::callWithThis(
|
||||||
|
Runtime& runtime,
|
||||||
|
const Object& jsThis,
|
||||||
|
const Value* args,
|
||||||
|
size_t count) const {
|
||||||
|
return runtime.call(*this, Value(runtime, jsThis), args, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Value Function::callWithThis(
|
||||||
|
Runtime& runtime,
|
||||||
|
const Object& jsThis,
|
||||||
|
std::initializer_list<Value> args) const {
|
||||||
|
return callWithThis(runtime, jsThis, args.begin(), args.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
inline Value Function::callWithThis(
|
||||||
|
Runtime& runtime,
|
||||||
|
const Object& jsThis,
|
||||||
|
Args&&... args) const {
|
||||||
|
// A more awesome version of this would be able to create raw values
|
||||||
|
// which can be used directly as HermesValues, instead of having to
|
||||||
|
// wrap the args in Values and hvFromValue on each to unwrap them.
|
||||||
|
// But this will do for now.
|
||||||
|
return callWithThis(
|
||||||
|
runtime, jsThis, {detail::toValue(runtime, std::forward<Args>(args))...});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
inline Array Array::createWithElements(Runtime& runtime, Args&&... args) {
|
||||||
|
return createWithElements(
|
||||||
|
runtime, {detail::toValue(runtime, std::forward<Args>(args))...});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
inline std::vector<PropNameID> PropNameID::names(
|
||||||
|
Runtime& runtime,
|
||||||
|
Args&&... args) {
|
||||||
|
return names({detail::toPropNameID(runtime, std::forward<Args>(args))...});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <size_t N>
|
||||||
|
inline std::vector<PropNameID> PropNameID::names(
|
||||||
|
PropNameID(&&propertyNames)[N]) {
|
||||||
|
std::vector<PropNameID> result;
|
||||||
|
result.reserve(N);
|
||||||
|
for (auto& name : propertyNames) {
|
||||||
|
result.push_back(std::move(name));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Value Function::callAsConstructor(
|
||||||
|
Runtime& runtime,
|
||||||
|
const Value* args,
|
||||||
|
size_t count) const {
|
||||||
|
return runtime.callAsConstructor(*this, args, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Value Function::callAsConstructor(
|
||||||
|
Runtime& runtime,
|
||||||
|
std::initializer_list<Value> args) const {
|
||||||
|
return callAsConstructor(runtime, args.begin(), args.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
inline Value Function::callAsConstructor(Runtime& runtime, Args&&... args)
|
||||||
|
const {
|
||||||
|
return callAsConstructor(
|
||||||
|
runtime, {detail::toValue(runtime, std::forward<Args>(args))...});
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace jsi
|
||||||
|
} // namespace facebook
|
|
@ -0,0 +1,350 @@
|
||||||
|
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include <jsi/instrumentation.h>
|
||||||
|
#include <jsi/jsi.h>
|
||||||
|
|
||||||
|
namespace facebook {
|
||||||
|
namespace jsi {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
void throwJSError(Runtime& rt, const char* msg) {
|
||||||
|
throw JSError(rt, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
Buffer::~Buffer() {}
|
||||||
|
|
||||||
|
Value HostObject::get(Runtime&, const PropNameID&) {
|
||||||
|
return Value();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HostObject::set(Runtime& rt, const PropNameID& name, const Value&) {
|
||||||
|
std::string msg("TypeError: Cannot assign to property '");
|
||||||
|
msg += name.utf8(rt);
|
||||||
|
msg += "' on HostObject with default setter";
|
||||||
|
throw JSError(rt, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
HostObject::~HostObject() {}
|
||||||
|
|
||||||
|
Runtime::~Runtime() {}
|
||||||
|
|
||||||
|
Instrumentation& Runtime::instrumentation() {
|
||||||
|
class NoInstrumentation : public Instrumentation {
|
||||||
|
std::string getRecordedGCStats() override {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
Value getHeapInfo(bool) override {
|
||||||
|
return Value::undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
void collectGarbage() override {}
|
||||||
|
|
||||||
|
bool createSnapshotToFile(const std::string&, bool) override {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeBridgeTrafficTraceToFile(const std::string&) const override {
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeBasicBlockProfileTraceToFile(const std::string&) const override {
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
void enableSamplingProfiler() const override {
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
void dumpSampledTraceToFile(const std::string&) const override {
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
void dumpProfilerSymbolsToFile(const std::string&) const override {
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static NoInstrumentation sharedInstance;
|
||||||
|
return sharedInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
Pointer& Pointer::operator=(Pointer&& other) {
|
||||||
|
if (ptr_) {
|
||||||
|
ptr_->invalidate();
|
||||||
|
}
|
||||||
|
ptr_ = other.ptr_;
|
||||||
|
other.ptr_ = nullptr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object Object::getPropertyAsObject(Runtime& runtime, const char* name) const {
|
||||||
|
Value v = getProperty(runtime, name);
|
||||||
|
|
||||||
|
if (!v.isObject()) {
|
||||||
|
throw JSError(
|
||||||
|
runtime,
|
||||||
|
std::string("getPropertyAsObject: property '") + name +
|
||||||
|
"' is not an Object");
|
||||||
|
}
|
||||||
|
|
||||||
|
return v.getObject(runtime);
|
||||||
|
}
|
||||||
|
|
||||||
|
Function Object::getPropertyAsFunction(Runtime& runtime, const char* name)
|
||||||
|
const {
|
||||||
|
Object obj = getPropertyAsObject(runtime, name);
|
||||||
|
if (!obj.isFunction(runtime)) {
|
||||||
|
throw JSError(
|
||||||
|
runtime,
|
||||||
|
std::string("getPropertyAsFunction: property '") + name +
|
||||||
|
"' is not a Function");
|
||||||
|
};
|
||||||
|
|
||||||
|
Runtime::PointerValue* value = obj.ptr_;
|
||||||
|
obj.ptr_ = nullptr;
|
||||||
|
return Function(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
Array Object::asArray(Runtime& runtime) const& {
|
||||||
|
if (!isArray(runtime)) {
|
||||||
|
throw JSError(runtime, "Object is not an array");
|
||||||
|
}
|
||||||
|
return getArray(runtime);
|
||||||
|
}
|
||||||
|
|
||||||
|
Array Object::asArray(Runtime& runtime) && {
|
||||||
|
if (!isArray(runtime)) {
|
||||||
|
throw JSError(runtime, "Object is not an array");
|
||||||
|
}
|
||||||
|
return std::move(*this).getArray(runtime);
|
||||||
|
}
|
||||||
|
|
||||||
|
Function Object::asFunction(Runtime& runtime) const& {
|
||||||
|
if (!isFunction(runtime)) {
|
||||||
|
throw JSError(runtime, "Object is not a function");
|
||||||
|
}
|
||||||
|
return getFunction(runtime);
|
||||||
|
}
|
||||||
|
|
||||||
|
Function Object::asFunction(Runtime& runtime) && {
|
||||||
|
if (!isFunction(runtime)) {
|
||||||
|
throw JSError(runtime, "Object is not a function");
|
||||||
|
}
|
||||||
|
return std::move(*this).getFunction(runtime);
|
||||||
|
}
|
||||||
|
|
||||||
|
Value::Value(Value&& other) : Value(other.kind_) {
|
||||||
|
if (kind_ == BooleanKind) {
|
||||||
|
data_.boolean = other.data_.boolean;
|
||||||
|
} else if (kind_ == NumberKind) {
|
||||||
|
data_.number = other.data_.number;
|
||||||
|
} else if (kind_ >= PointerKind) {
|
||||||
|
new (&data_.pointer) Pointer(std::move(other.data_.pointer));
|
||||||
|
}
|
||||||
|
// when the other's dtor runs, nothing will happen.
|
||||||
|
other.kind_ = UndefinedKind;
|
||||||
|
}
|
||||||
|
|
||||||
|
Value::Value(Runtime& runtime, const Value& other) : Value(other.kind_) {
|
||||||
|
// data_ is uninitialized, so use placement new to create non-POD
|
||||||
|
// types in it. Any other kind of initialization will call a dtor
|
||||||
|
// first, which is incorrect.
|
||||||
|
if (kind_ == BooleanKind) {
|
||||||
|
data_.boolean = other.data_.boolean;
|
||||||
|
} else if (kind_ == NumberKind) {
|
||||||
|
data_.number = other.data_.number;
|
||||||
|
} else if (kind_ == StringKind) {
|
||||||
|
new (&data_.pointer) Pointer(runtime.cloneString(other.data_.pointer.ptr_));
|
||||||
|
} else if (kind_ >= ObjectKind) {
|
||||||
|
new (&data_.pointer) Pointer(runtime.cloneObject(other.data_.pointer.ptr_));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Value::~Value() {
|
||||||
|
if (kind_ >= PointerKind) {
|
||||||
|
data_.pointer.~Pointer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Value Value::createFromJsonUtf8(
|
||||||
|
Runtime& runtime,
|
||||||
|
const uint8_t* json,
|
||||||
|
size_t length) {
|
||||||
|
Function parseJson = runtime.global()
|
||||||
|
.getPropertyAsObject(runtime, "JSON")
|
||||||
|
.getPropertyAsFunction(runtime, "parse");
|
||||||
|
return parseJson.call(runtime, String::createFromUtf8(runtime, json, length));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Value::strictEquals(Runtime& runtime, const Value& a, const Value& b) {
|
||||||
|
if (a.kind_ != b.kind_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
switch (a.kind_) {
|
||||||
|
case UndefinedKind:
|
||||||
|
case NullKind:
|
||||||
|
return true;
|
||||||
|
case BooleanKind:
|
||||||
|
return a.data_.boolean == b.data_.boolean;
|
||||||
|
case NumberKind:
|
||||||
|
return a.data_.number == b.data_.number;
|
||||||
|
case StringKind:
|
||||||
|
return runtime.strictEquals(
|
||||||
|
static_cast<const String&>(a.data_.pointer),
|
||||||
|
static_cast<const String&>(b.data_.pointer));
|
||||||
|
case ObjectKind:
|
||||||
|
return runtime.strictEquals(
|
||||||
|
static_cast<const Object&>(a.data_.pointer),
|
||||||
|
static_cast<const Object&>(b.data_.pointer));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
double Value::asNumber() const {
|
||||||
|
if (!isNumber()) {
|
||||||
|
throw JSINativeException("Value is not an Object");
|
||||||
|
}
|
||||||
|
|
||||||
|
return getNumber();
|
||||||
|
}
|
||||||
|
|
||||||
|
Object Value::asObject(Runtime& runtime) const& {
|
||||||
|
if (!isObject()) {
|
||||||
|
throw JSError(runtime, "Value is not an Object");
|
||||||
|
}
|
||||||
|
|
||||||
|
return getObject(runtime);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object Value::asObject(Runtime& rt) && {
|
||||||
|
if (!isObject()) {
|
||||||
|
throw JSError(rt, "Value is not an Object");
|
||||||
|
}
|
||||||
|
auto ptr = data_.pointer.ptr_;
|
||||||
|
data_.pointer.ptr_ = nullptr;
|
||||||
|
return static_cast<Object>(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
String Value::asString(Runtime& rt) const& {
|
||||||
|
if (!isString()) {
|
||||||
|
throw JSError(rt, "Value is not a String");
|
||||||
|
}
|
||||||
|
|
||||||
|
return getString(rt);
|
||||||
|
}
|
||||||
|
|
||||||
|
String Value::asString(Runtime& rt) && {
|
||||||
|
if (!isString()) {
|
||||||
|
throw JSError(rt, "Value is not a String");
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::move(*this).getString(rt);
|
||||||
|
}
|
||||||
|
|
||||||
|
String Value::toString(Runtime& runtime) const {
|
||||||
|
Function toString = runtime.global().getPropertyAsFunction(runtime, "String");
|
||||||
|
return toString.call(runtime, *this).getString(runtime);
|
||||||
|
}
|
||||||
|
|
||||||
|
Array Array::createWithElements(
|
||||||
|
Runtime& rt,
|
||||||
|
std::initializer_list<Value> elements) {
|
||||||
|
Array result(rt, elements.size());
|
||||||
|
size_t index = 0;
|
||||||
|
for (const auto& element : elements) {
|
||||||
|
result.setValueAtIndex(rt, index++, element);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<PropNameID> HostObject::getPropertyNames(Runtime&) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
Runtime::ScopeState* Runtime::pushScope() {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Runtime::popScope(ScopeState*) {}
|
||||||
|
|
||||||
|
JSError::JSError(Runtime& rt, Value&& value) {
|
||||||
|
setValue(rt, std::move(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
JSError::JSError(Runtime& rt, std::string msg) : message_(std::move(msg)) {
|
||||||
|
try {
|
||||||
|
setValue(
|
||||||
|
rt, rt.global().getPropertyAsFunction(rt, "Error").call(rt, message_));
|
||||||
|
} catch (...) {
|
||||||
|
setValue(rt, Value());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JSError::JSError(Runtime& rt, std::string msg, std::string stack)
|
||||||
|
: message_(std::move(msg)), stack_(std::move(stack)) {
|
||||||
|
try {
|
||||||
|
Object e(rt);
|
||||||
|
e.setProperty(rt, "message", String::createFromUtf8(rt, message_));
|
||||||
|
e.setProperty(rt, "stack", String::createFromUtf8(rt, stack_));
|
||||||
|
setValue(rt, std::move(e));
|
||||||
|
} catch (...) {
|
||||||
|
setValue(rt, Value());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JSError::JSError(std::string what, Runtime& rt, Value&& value)
|
||||||
|
: JSIException(std::move(what)) {
|
||||||
|
setValue(rt, std::move(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
void JSError::setValue(Runtime& rt, Value&& value) {
|
||||||
|
value_ = std::make_shared<jsi::Value>(std::move(value));
|
||||||
|
|
||||||
|
try {
|
||||||
|
if ((message_.empty() || stack_.empty()) && value_->isObject()) {
|
||||||
|
auto obj = value_->getObject(rt);
|
||||||
|
|
||||||
|
if (message_.empty()) {
|
||||||
|
jsi::Value message = obj.getProperty(rt, "message");
|
||||||
|
if (!message.isUndefined()) {
|
||||||
|
message_ = message.toString(rt).utf8(rt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stack_.empty()) {
|
||||||
|
jsi::Value stack = obj.getProperty(rt, "stack");
|
||||||
|
if (!stack.isUndefined()) {
|
||||||
|
stack_ = stack.toString(rt).utf8(rt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message_.empty()) {
|
||||||
|
message_ = value_->toString(rt).utf8(rt);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stack_.empty()) {
|
||||||
|
stack_ = "no stack";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (what_.empty()) {
|
||||||
|
what_ = message_ + "\n\n" + stack_;
|
||||||
|
}
|
||||||
|
} catch (...) {
|
||||||
|
message_ = "[Exception caught creating message string]";
|
||||||
|
stack_ = "[Exception caught creating stack string]";
|
||||||
|
what_ = "[Exception caught getting value fields]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace jsi
|
||||||
|
} // namespace facebook
|
File diff suppressed because it is too large
Load Diff
|
@ -83,11 +83,15 @@ def react_native_tests_target(path):
|
||||||
def react_native_integration_tests_target(path):
|
def react_native_integration_tests_target(path):
|
||||||
return "//ReactAndroid/src/androidTest/" + path
|
return "//ReactAndroid/src/androidTest/" + path
|
||||||
|
|
||||||
# Helper for referring to non-RN code from RN OSS code.
|
# Helpers for referring to non-RN code from RN OSS code.
|
||||||
# Example: react_native_dep('java/com/facebook/systrace:systrace')
|
# Example: react_native_dep('java/com/facebook/systrace:systrace')
|
||||||
def react_native_dep(path):
|
def react_native_dep(path):
|
||||||
return "//ReactAndroid/src/main/" + path
|
return "//ReactAndroid/src/main/" + path
|
||||||
|
|
||||||
|
# Example: react_native_xplat_dep('java/com/facebook/systrace:systrace')
|
||||||
|
def react_native_xplat_dep(path):
|
||||||
|
return "//ReactCommon/" + path
|
||||||
|
|
||||||
# React property preprocessor
|
# React property preprocessor
|
||||||
def rn_android_library(name, deps = [], plugins = [], *args, **kwargs):
|
def rn_android_library(name, deps = [], plugins = [], *args, **kwargs):
|
||||||
if react_native_target(
|
if react_native_target(
|
||||||
|
|
Loading…
Reference in New Issue