feat(smart-app): implement complete mobile app MVP

- App.tsx: full navigation (Auth stack + Main tabs with 5 screens)
- Auth: LoginScreen, RegisterScreen, ForgotPasswordScreen
- HomeScreen: dashboard with IoT metrics, weather widget, alerts, quick actions, sensors
- MapScreen: interactive map with layer toggles (6 layers)
- MarketplaceScreen: categories (6), products (5), search
- ChatScreen: AI chat with quick prompts (4), bot responses
- ProfileScreen: user info, stats, menu (9 items), logout
- AlertsScreen: alert list with severity, acknowledge
- SensorsScreen: sensor list with type filters (6 types), search
- ZonesScreen: zone cards with stats
- SettingsScreen: language picker (FR/EN/ES/DE), privacy, about
- Stores: iotStore (sensors, zones, alerts), notificationStore, uiStore + i18n
- Hooks: useSensors, useAlerts, useNotifications, useLocation
- Components: Card, Button, LoadingSpinner, ErrorBoundary, Header
- Services: iotService, notificationService (with axios API client)
- Utils: formatters (temp, AQI, noise, dates), validators (email, password, IBAN)
- Theme: colors.ts with full design system (Blue Ocean palette)
- Ditto: fixed MongoDB connection, new JWT secrets, official gateway image
This commit is contained in:
Eric FELIXINE
2026-06-01 18:00:35 -04:00
parent 08ca495bde
commit e30ae8ed09
35578 changed files with 3703534 additions and 43 deletions

View File

@@ -0,0 +1,6 @@
---
Checks: '>
clang-diagnostic-*,
'
InheritParentConfig: true
...

View File

@@ -0,0 +1,27 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
##################
### jsi ###
##################
cmake_minimum_required(VERSION 3.13)
set(CMAKE_VERBOSE_MAKEFILE on)
add_compile_options(
-fexceptions
-frtti
-O3
-Wno-unused-lambda-capture
-DLOG_TAG=\"ReactNative\")
file(GLOB jsi_SRC CONFIGURE_DEPENDS jsi/*.cpp)
add_library(jsi SHARED ${jsi_SRC})
target_include_directories(jsi PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(jsi
folly_runtime
glog)

View File

@@ -0,0 +1,61 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
require "json"
js_engine = ENV['USE_HERMES'] == "0" ?
:jsc :
:hermes
package = JSON.parse(File.read(File.join(__dir__, "..", "..", "package.json")))
version = package['version']
source = { :git => 'https://github.com/facebook/react-native.git' }
if version == '1000.0.0'
# This is an unpublished version, use the latest commit hash of the react-native repo, which were presumably in.
source[:commit] = `git rev-parse HEAD`.strip if system("git rev-parse --git-dir > /dev/null 2>&1")
else
source[:tag] = "v#{version}"
end
folly_config = get_folly_config()
folly_compiler_flags = folly_config[:compiler_flags]
folly_version = folly_config[:version]
boost_compiler_flags = '-Wno-documentation'
Pod::Spec.new do |s|
s.name = "React-jsi"
s.version = version
s.summary = "JavaScript Interface layer for React Native"
s.homepage = "https://reactnative.dev/"
s.license = package["license"]
s.author = "Meta Platforms, Inc. and its affiliates"
s.platforms = min_supported_versions
s.source = source
s.header_dir = "jsi"
s.compiler_flags = folly_compiler_flags + ' ' + boost_compiler_flags
s.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\" \"$(PODS_ROOT)/RCT-Folly\" \"$(PODS_ROOT)/DoubleConversion\" \"$(PODS_ROOT)/fmt/include\"",
"DEFINES_MODULE" => "YES" }
s.dependency "boost", "1.83.0"
s.dependency "DoubleConversion"
s.dependency "fmt", "9.1.0"
s.dependency "RCT-Folly", folly_version
s.dependency "glog"
s.source_files = "**/*.{cpp,h}"
files_to_exclude = [
"jsi/jsilib-posix.cpp",
"jsi/jsilib-windows.cpp",
"**/test/*"
]
if js_engine == :hermes
# JSI is a part of hermes-engine. Including them also in react-native will violate the One Definition Rulle.
files_to_exclude += [ "jsi/jsi.cpp" ]
s.dependency "hermes-engine"
end
s.exclude_files = files_to_exclude
end

View File

@@ -0,0 +1,31 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_library(jsi
jsi.cpp)
target_include_directories(jsi PUBLIC ..)
set(jsi_compile_flags "")
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" OR
"${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU")
list(APPEND jsi_compile_flags "-Wno-non-virtual-dtor")
elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "MSVC")
# Turn on Error Handling in MSVC, otherwise objects are not destructed
# when they go out of scope due to exceptions.
list(APPEND jsi_compile_flags "/EHsc")
endif()
if (HERMES_ENABLE_BITCODE)
list(APPEND jsi_compile_flags "-fembed-bitcode")
endif ()
target_compile_options(jsi PRIVATE ${jsi_compile_flags})
install(DIRECTORY "${PROJECT_SOURCE_DIR}/API/jsi/" DESTINATION include
FILES_MATCHING PATTERN "*.h"
PATTERN "test" EXCLUDE)

View File

@@ -0,0 +1,212 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "JSIDynamic.h"
#include <glog/logging.h>
#include <folly/dynamic.h>
#include <jsi/jsi.h>
using namespace facebook::jsi;
namespace facebook {
namespace jsi {
namespace {
struct FromDynamic {
FromDynamic(const folly::dynamic* dynArg, Object objArg)
: dyn(dynArg), obj(std::move(objArg)) {}
const folly::dynamic* dyn;
Object obj;
};
// This converts one element. If it's a collection, it gets pushed onto
// the stack for later processing.
Value valueFromDynamicShallow(
Runtime& runtime,
std::vector<FromDynamic>& stack,
const folly::dynamic& dyn) {
switch (dyn.type()) {
case folly::dynamic::NULLT:
return Value::null();
case folly::dynamic::ARRAY: {
Object arr = Array(runtime, dyn.size());
Value ret = Value(runtime, arr);
stack.emplace_back(&dyn, std::move(arr));
return ret;
}
case folly::dynamic::BOOL:
return Value(dyn.getBool());
case folly::dynamic::DOUBLE:
return dyn.getDouble();
case folly::dynamic::INT64:
return Value((double)dyn.getInt());
case folly::dynamic::OBJECT: {
auto obj = Object(runtime);
Value ret = Value(runtime, obj);
stack.emplace_back(&dyn, std::move(obj));
return ret;
}
case folly::dynamic::STRING:
return Value(String::createFromUtf8(runtime, dyn.getString()));
}
CHECK(false);
}
} // namespace
Value valueFromDynamic(Runtime& runtime, const folly::dynamic& dynInput) {
std::vector<FromDynamic> stack;
Value ret = valueFromDynamicShallow(runtime, stack, dynInput);
while (!stack.empty()) {
auto top = std::move(stack.back());
stack.pop_back();
switch (top.dyn->type()) {
case folly::dynamic::ARRAY: {
Array arr = std::move(top.obj).getArray(runtime);
for (size_t i = 0; i < top.dyn->size(); ++i) {
arr.setValueAtIndex(
runtime,
i,
valueFromDynamicShallow(runtime, stack, (*top.dyn)[i]));
}
break;
}
case folly::dynamic::OBJECT: {
Object obj = std::move(top.obj);
for (const auto& element : top.dyn->items()) {
if (element.first.isNumber() || element.first.isString()) {
obj.setProperty(
runtime,
PropNameID::forUtf8(runtime, element.first.asString()),
valueFromDynamicShallow(runtime, stack, element.second));
}
}
break;
}
default:
CHECK(false);
}
}
return ret;
}
namespace {
struct FromValue {
FromValue(folly::dynamic* dynArg, Object objArg)
: dyn(dynArg), obj(std::move(objArg)) {}
folly::dynamic* dyn;
Object obj;
};
// This converts one element. If it's a collection, it gets pushed
// onto the stack for later processing. The output is created by
// mutating the output argument, because we need its actual pointer to
// push onto the stack.
void dynamicFromValueShallow(
Runtime& runtime,
std::vector<FromValue>& stack,
const jsi::Value& value,
folly::dynamic& output) {
if (value.isUndefined() || value.isNull()) {
output = nullptr;
} else if (value.isBool()) {
output = value.getBool();
} else if (value.isNumber()) {
output = value.getNumber();
} else if (value.isString()) {
output = value.getString(runtime).utf8(runtime);
} else if (value.isObject()) {
Object obj = value.getObject(runtime);
if (obj.isArray(runtime)) {
output = folly::dynamic::array();
} else if (obj.isFunction(runtime)) {
throw JSError(runtime, "JS Functions are not convertible to dynamic");
} else {
output = folly::dynamic::object();
}
stack.emplace_back(&output, std::move(obj));
} else if (value.isBigInt()) {
throw JSError(runtime, "JS BigInts are not convertible to dynamic");
} else if (value.isSymbol()) {
throw JSError(runtime, "JS Symbols are not convertible to dynamic");
} else {
throw JSError(runtime, "Value is not convertible to dynamic");
}
}
} // namespace
folly::dynamic dynamicFromValue(
Runtime& runtime,
const Value& valueInput,
std::function<bool(const std::string&)> filterObjectKeys) {
std::vector<FromValue> stack;
folly::dynamic ret;
dynamicFromValueShallow(runtime, stack, valueInput, ret);
while (!stack.empty()) {
auto top = std::move(stack.back());
stack.pop_back();
if (top.obj.isArray(runtime)) {
// Inserting into a dyn can invalidate references into it, so we
// need to insert new elements up front, then push stuff onto
// the stack.
Array array = top.obj.getArray(runtime);
size_t arraySize = array.size(runtime);
for (size_t i = 0; i < arraySize; ++i) {
top.dyn->push_back(nullptr);
}
for (size_t i = 0; i < arraySize; ++i) {
dynamicFromValueShallow(
runtime, stack, array.getValueAtIndex(runtime, i), top.dyn->at(i));
}
} else {
Array names = top.obj.getPropertyNames(runtime);
std::vector<std::pair<std::string, jsi::Value>> props;
for (size_t i = 0; i < names.size(runtime); ++i) {
String name = names.getValueAtIndex(runtime, i).getString(runtime);
Value prop = top.obj.getProperty(runtime, name);
if (prop.isUndefined()) {
continue;
}
auto nameStr = name.utf8(runtime);
if (filterObjectKeys && filterObjectKeys(nameStr)) {
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();
}
props.emplace_back(std::move(nameStr), std::move(prop));
top.dyn->insert(props.back().first, nullptr);
}
for (const auto& prop : props) {
dynamicFromValueShallow(
runtime, stack, prop.second, (*top.dyn)[prop.first]);
}
}
}
return ret;
}
} // namespace jsi
} // namespace facebook

View File

@@ -0,0 +1,26 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#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,
std::function<bool(const std::string&)> filterObjectKeys = nullptr);
} // namespace jsi
} // namespace facebook

View File

@@ -0,0 +1,810 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <tuple>
#include <jsi/instrumentation.h>
#include <jsi/jsi.h>
// This file contains objects to help API users create their own
// runtime adapters, i.e. if you want to compose runtimes to add your
// own behavior.
namespace facebook {
namespace jsi {
// Use this to wrap host functions. It will pass the member runtime as
// the first arg to the callback. The first argument to the ctor
// should be the decorated runtime, not the plain one.
class DecoratedHostFunction {
public:
DecoratedHostFunction(Runtime& drt, HostFunctionType plainHF)
: drt_(drt), plainHF_(std::move(plainHF)) {}
Runtime& decoratedRuntime() {
return drt_;
}
Value
operator()(Runtime&, const Value& thisVal, const Value* args, size_t count) {
return plainHF_(decoratedRuntime(), thisVal, args, count);
}
private:
template <typename Plain, typename Base>
friend class RuntimeDecorator;
Runtime& drt_;
HostFunctionType plainHF_;
};
// From the perspective of the caller, a plain HostObject is passed to
// the decorated Runtime, and the HostObject methods expect to get
// passed that Runtime. But the plain Runtime will pass itself to its
// callback, so we need a helper here which curries the decorated
// Runtime, and calls the plain HostObject with it.
//
// If the concrete RuntimeDecorator derives DecoratedHostObject, it
// should call the base class get() and set() to invoke the plain
// HostObject functionality. The Runtime& it passes does not matter,
// as it is not used.
class DecoratedHostObject : public HostObject {
public:
DecoratedHostObject(Runtime& drt, std::shared_ptr<HostObject> plainHO)
: drt_(drt), plainHO_(plainHO) {}
// The derived class methods can call this to get a reference to the
// decorated runtime, since the rt passed to the callback will be
// the plain runtime.
Runtime& decoratedRuntime() {
return drt_;
}
Value get(Runtime&, const PropNameID& name) override {
return plainHO_->get(decoratedRuntime(), name);
}
void set(Runtime&, const PropNameID& name, const Value& value) override {
plainHO_->set(decoratedRuntime(), name, value);
}
std::vector<PropNameID> getPropertyNames(Runtime&) override {
return plainHO_->getPropertyNames(decoratedRuntime());
}
private:
template <typename Plain, typename Base>
friend class RuntimeDecorator;
Runtime& drt_;
std::shared_ptr<HostObject> plainHO_;
};
/// C++ variant on a standard Decorator pattern, using template
/// parameters. The \c Plain template parameter type is the
/// undecorated Runtime type. You can usually use \c Runtime here,
/// but if you know the concrete type ahead of time and it's final,
/// the compiler can devirtualize calls to the decorated
/// implementation. The \c Base template parameter type will be used
/// as the base class of the decorated type. Here, too, you can
/// usually use \c Runtime, but if you want the decorated type to
/// implement a derived class of Runtime, you can specify that here.
/// For an example, see threadsafe.h.
template <typename Plain = Runtime, typename Base = Runtime>
class RuntimeDecorator : public Base, private jsi::Instrumentation {
public:
Plain& plain() {
static_assert(
std::is_base_of<Runtime, Plain>::value,
"RuntimeDecorator's Plain type must derive from jsi::Runtime");
static_assert(
std::is_base_of<Runtime, Base>::value,
"RuntimeDecorator's Base type must derive from jsi::Runtime");
return plain_;
}
const Plain& plain() const {
return plain_;
}
Value evaluateJavaScript(
const std::shared_ptr<const Buffer>& buffer,
const std::string& sourceURL) override {
return plain().evaluateJavaScript(buffer, sourceURL);
}
std::shared_ptr<const PreparedJavaScript> prepareJavaScript(
const std::shared_ptr<const Buffer>& buffer,
std::string sourceURL) override {
return plain().prepareJavaScript(buffer, std::move(sourceURL));
}
Value evaluatePreparedJavaScript(
const std::shared_ptr<const PreparedJavaScript>& js) override {
return plain().evaluatePreparedJavaScript(js);
}
bool drainMicrotasks(int maxMicrotasksHint) override {
return plain().drainMicrotasks(maxMicrotasksHint);
}
Object global() override {
return plain().global();
}
std::string description() override {
return plain().description();
};
bool isInspectable() override {
return plain().isInspectable();
};
Instrumentation& instrumentation() override {
return *this;
}
protected:
// plain is generally going to be a reference to an object managed
// by a derived class. We cache it here so this class can be
// concrete, and avoid making virtual calls to find the plain
// Runtime. Note that the ctor and dtor do not access through the
// reference, so passing a reference to an object before its
// lifetime has started is ok.
RuntimeDecorator(Plain& plain) : plain_(plain) {}
Runtime::PointerValue* cloneSymbol(const Runtime::PointerValue* pv) override {
return plain_.cloneSymbol(pv);
};
Runtime::PointerValue* cloneBigInt(const Runtime::PointerValue* pv) override {
return plain_.cloneBigInt(pv);
};
Runtime::PointerValue* cloneString(const Runtime::PointerValue* pv) override {
return plain_.cloneString(pv);
};
Runtime::PointerValue* cloneObject(const Runtime::PointerValue* pv) override {
return plain_.cloneObject(pv);
};
Runtime::PointerValue* clonePropNameID(
const Runtime::PointerValue* pv) override {
return plain_.clonePropNameID(pv);
};
PropNameID createPropNameIDFromAscii(const char* str, size_t length)
override {
return plain_.createPropNameIDFromAscii(str, length);
};
PropNameID createPropNameIDFromUtf8(const uint8_t* utf8, size_t length)
override {
return plain_.createPropNameIDFromUtf8(utf8, length);
};
PropNameID createPropNameIDFromString(const String& str) override {
return plain_.createPropNameIDFromString(str);
};
PropNameID createPropNameIDFromSymbol(const Symbol& sym) override {
return plain_.createPropNameIDFromSymbol(sym);
};
std::string utf8(const PropNameID& id) override {
return plain_.utf8(id);
};
bool compare(const PropNameID& a, const PropNameID& b) override {
return plain_.compare(a, b);
};
std::string symbolToString(const Symbol& sym) override {
return plain_.symbolToString(sym);
}
BigInt createBigIntFromInt64(int64_t value) override {
return plain_.createBigIntFromInt64(value);
}
BigInt createBigIntFromUint64(uint64_t value) override {
return plain_.createBigIntFromUint64(value);
}
bool bigintIsInt64(const BigInt& b) override {
return plain_.bigintIsInt64(b);
}
bool bigintIsUint64(const BigInt& b) override {
return plain_.bigintIsUint64(b);
}
uint64_t truncate(const BigInt& b) override {
return plain_.truncate(b);
}
String bigintToString(const BigInt& bigint, int radix) override {
return plain_.bigintToString(bigint, radix);
}
String createStringFromAscii(const char* str, size_t length) override {
return plain_.createStringFromAscii(str, length);
};
String createStringFromUtf8(const uint8_t* utf8, size_t length) override {
return plain_.createStringFromUtf8(utf8, length);
};
std::string utf8(const String& s) override {
return plain_.utf8(s);
}
Object createObject() override {
return plain_.createObject();
};
Object createObject(std::shared_ptr<HostObject> ho) override {
return plain_.createObject(
std::make_shared<DecoratedHostObject>(*this, std::move(ho)));
};
std::shared_ptr<HostObject> getHostObject(const jsi::Object& o) override {
std::shared_ptr<HostObject> dho = plain_.getHostObject(o);
return static_cast<DecoratedHostObject&>(*dho).plainHO_;
};
HostFunctionType& getHostFunction(const jsi::Function& f) override {
HostFunctionType& dhf = plain_.getHostFunction(f);
// This will fail if a cpp file including this header is not compiled
// with RTTI.
return dhf.target<DecoratedHostFunction>()->plainHF_;
};
bool hasNativeState(const Object& o) override {
return plain_.hasNativeState(o);
}
std::shared_ptr<NativeState> getNativeState(const Object& o) override {
return plain_.getNativeState(o);
}
void setNativeState(const Object& o, std::shared_ptr<NativeState> state)
override {
plain_.setNativeState(o, state);
}
void setExternalMemoryPressure(const Object& obj, size_t amt) override {
plain_.setExternalMemoryPressure(obj, amt);
}
Value getProperty(const Object& o, const PropNameID& name) override {
return plain_.getProperty(o, name);
};
Value getProperty(const Object& o, const String& name) override {
return plain_.getProperty(o, name);
};
bool hasProperty(const Object& o, const PropNameID& name) override {
return plain_.hasProperty(o, name);
};
bool hasProperty(const Object& o, const String& name) override {
return plain_.hasProperty(o, name);
};
void setPropertyValue(
const Object& o,
const PropNameID& name,
const Value& value) override {
plain_.setPropertyValue(o, name, value);
};
void setPropertyValue(const Object& o, const String& name, const Value& value)
override {
plain_.setPropertyValue(o, name, value);
};
bool isArray(const Object& o) const override {
return plain_.isArray(o);
};
bool isArrayBuffer(const Object& o) const override {
return plain_.isArrayBuffer(o);
};
bool isFunction(const Object& o) const override {
return plain_.isFunction(o);
};
bool isHostObject(const jsi::Object& o) const override {
return plain_.isHostObject(o);
};
bool isHostFunction(const jsi::Function& f) const override {
return plain_.isHostFunction(f);
};
Array getPropertyNames(const Object& o) override {
return plain_.getPropertyNames(o);
};
WeakObject createWeakObject(const Object& o) override {
return plain_.createWeakObject(o);
};
Value lockWeakObject(const WeakObject& wo) override {
return plain_.lockWeakObject(wo);
};
Array createArray(size_t length) override {
return plain_.createArray(length);
};
ArrayBuffer createArrayBuffer(
std::shared_ptr<MutableBuffer> buffer) override {
return plain_.createArrayBuffer(std::move(buffer));
};
size_t size(const Array& a) override {
return plain_.size(a);
};
size_t size(const ArrayBuffer& ab) override {
return plain_.size(ab);
};
uint8_t* data(const ArrayBuffer& ab) override {
return plain_.data(ab);
};
Value getValueAtIndex(const Array& a, size_t i) override {
return plain_.getValueAtIndex(a, i);
};
void setValueAtIndexImpl(const Array& a, size_t i, const Value& value)
override {
plain_.setValueAtIndexImpl(a, i, value);
};
Function createFunctionFromHostFunction(
const PropNameID& name,
unsigned int paramCount,
HostFunctionType func) override {
return plain_.createFunctionFromHostFunction(
name, paramCount, DecoratedHostFunction(*this, std::move(func)));
};
Value call(
const Function& f,
const Value& jsThis,
const Value* args,
size_t count) override {
return plain_.call(f, jsThis, args, count);
};
Value callAsConstructor(const Function& f, const Value* args, size_t count)
override {
return plain_.callAsConstructor(f, args, count);
};
// Private data for managing scopes.
Runtime::ScopeState* pushScope() override {
return plain_.pushScope();
}
void popScope(Runtime::ScopeState* ss) override {
plain_.popScope(ss);
}
bool strictEquals(const Symbol& a, const Symbol& b) const override {
return plain_.strictEquals(a, b);
};
bool strictEquals(const BigInt& a, const BigInt& b) const override {
return plain_.strictEquals(a, b);
};
bool strictEquals(const String& a, const String& b) const override {
return plain_.strictEquals(a, b);
};
bool strictEquals(const Object& a, const Object& b) const override {
return plain_.strictEquals(a, b);
};
bool instanceOf(const Object& o, const Function& f) override {
return plain_.instanceOf(o, f);
};
// jsi::Instrumentation methods
std::string getRecordedGCStats() override {
return plain().instrumentation().getRecordedGCStats();
}
std::unordered_map<std::string, int64_t> getHeapInfo(
bool includeExpensive) override {
return plain().instrumentation().getHeapInfo(includeExpensive);
}
void collectGarbage(std::string cause) override {
plain().instrumentation().collectGarbage(std::move(cause));
}
void startTrackingHeapObjectStackTraces(
std::function<void(
uint64_t,
std::chrono::microseconds,
std::vector<HeapStatsUpdate>)> callback) override {
plain().instrumentation().startTrackingHeapObjectStackTraces(
std::move(callback));
}
void stopTrackingHeapObjectStackTraces() override {
plain().instrumentation().stopTrackingHeapObjectStackTraces();
}
void startHeapSampling(size_t samplingInterval) override {
plain().instrumentation().startHeapSampling(samplingInterval);
}
void stopHeapSampling(std::ostream& os) override {
plain().instrumentation().stopHeapSampling(os);
}
void createSnapshotToFile(const std::string& path) override {
plain().instrumentation().createSnapshotToFile(path);
}
void createSnapshotToStream(std::ostream& os) override {
plain().instrumentation().createSnapshotToStream(os);
}
std::string flushAndDisableBridgeTrafficTrace() override {
return const_cast<Plain&>(plain())
.instrumentation()
.flushAndDisableBridgeTrafficTrace();
}
void writeBasicBlockProfileTraceToFile(
const std::string& fileName) const override {
const_cast<Plain&>(plain())
.instrumentation()
.writeBasicBlockProfileTraceToFile(fileName);
}
/// Dump external profiler symbols to the given file name.
void dumpProfilerSymbolsToFile(const std::string& fileName) const override {
const_cast<Plain&>(plain()).instrumentation().dumpProfilerSymbolsToFile(
fileName);
}
private:
Plain& plain_;
};
namespace detail {
// This metaprogramming allows the With type's methods to be
// optional.
template <typename T, typename U = void>
struct BeforeCaller {
static void before(T&) {}
};
template <typename T, typename U = void>
struct AfterCaller {
static void after(T&) {}
};
// decltype((void)&...) is either SFINAE, or void.
// So, if SFINAE does not happen for T, then this specialization exists
// for BeforeCaller<T, void>, and always applies. If not, only the
// default above exists, and that is used instead.
template <typename T>
struct BeforeCaller<T, decltype((void)&T::before)> {
static void before(T& t) {
t.before();
}
};
template <typename T>
struct AfterCaller<T, decltype((void)&T::after)> {
static void after(T& t) {
t.after();
}
};
// It's possible to use multiple decorators by nesting
// WithRuntimeDecorator<...>, but this specialization allows use of
// std::tuple of decorator classes instead. See testlib.cpp for an
// example.
template <typename... T>
struct BeforeCaller<std::tuple<T...>> {
static void before(std::tuple<T...>& tuple) {
all_before<0, T...>(tuple);
}
private:
template <size_t N, typename U, typename... Rest>
static void all_before(std::tuple<T...>& tuple) {
detail::BeforeCaller<U>::before(std::get<N>(tuple));
all_before<N + 1, Rest...>(tuple);
}
template <size_t N>
static void all_before(std::tuple<T...>&) {}
};
template <typename... T>
struct AfterCaller<std::tuple<T...>> {
static void after(std::tuple<T...>& tuple) {
all_after<0, T...>(tuple);
}
private:
template <size_t N, typename U, typename... Rest>
static void all_after(std::tuple<T...>& tuple) {
all_after<N + 1, Rest...>(tuple);
detail::AfterCaller<U>::after(std::get<N>(tuple));
}
template <size_t N>
static void all_after(std::tuple<T...>&) {}
};
} // namespace detail
// A decorator which implements an around idiom. A With instance is
// RAII constructed before each call to the undecorated class; the
// ctor is passed a single argument of type WithArg&. Plain and Base
// are used as in the base class.
template <typename With, typename Plain = Runtime, typename Base = Runtime>
class WithRuntimeDecorator : public RuntimeDecorator<Plain, Base> {
public:
using RD = RuntimeDecorator<Plain, Base>;
// The reference arguments to the ctor are stored, but not used by
// the ctor, and there is no ctor, so they can be passed members of
// the derived class.
WithRuntimeDecorator(Plain& plain, With& with) : RD(plain), with_(with) {}
Value evaluateJavaScript(
const std::shared_ptr<const Buffer>& buffer,
const std::string& sourceURL) override {
Around around{with_};
return RD::evaluateJavaScript(buffer, sourceURL);
}
std::shared_ptr<const PreparedJavaScript> prepareJavaScript(
const std::shared_ptr<const Buffer>& buffer,
std::string sourceURL) override {
Around around{with_};
return RD::prepareJavaScript(buffer, std::move(sourceURL));
}
Value evaluatePreparedJavaScript(
const std::shared_ptr<const PreparedJavaScript>& js) override {
Around around{with_};
return RD::evaluatePreparedJavaScript(js);
}
bool drainMicrotasks(int maxMicrotasksHint) override {
Around around{with_};
return RD::drainMicrotasks(maxMicrotasksHint);
}
Object global() override {
Around around{with_};
return RD::global();
}
std::string description() override {
Around around{with_};
return RD::description();
};
bool isInspectable() override {
Around around{with_};
return RD::isInspectable();
};
// The jsi:: prefix is necessary because MSVC compiler complains C2247:
// Instrumentation is not accessible because RuntimeDecorator uses private
// to inherit from Instrumentation.
// TODO(T40821815) Consider removing this workaround when updating MSVC
jsi::Instrumentation& instrumentation() override {
Around around{with_};
return RD::instrumentation();
}
protected:
Runtime::PointerValue* cloneSymbol(const Runtime::PointerValue* pv) override {
Around around{with_};
return RD::cloneSymbol(pv);
};
Runtime::PointerValue* cloneString(const Runtime::PointerValue* pv) override {
Around around{with_};
return RD::cloneString(pv);
};
Runtime::PointerValue* cloneObject(const Runtime::PointerValue* pv) override {
Around around{with_};
return RD::cloneObject(pv);
};
Runtime::PointerValue* clonePropNameID(
const Runtime::PointerValue* pv) override {
Around around{with_};
return RD::clonePropNameID(pv);
};
PropNameID createPropNameIDFromAscii(const char* str, size_t length)
override {
Around around{with_};
return RD::createPropNameIDFromAscii(str, length);
};
PropNameID createPropNameIDFromUtf8(const uint8_t* utf8, size_t length)
override {
Around around{with_};
return RD::createPropNameIDFromUtf8(utf8, length);
};
PropNameID createPropNameIDFromString(const String& str) override {
Around around{with_};
return RD::createPropNameIDFromString(str);
};
std::string utf8(const PropNameID& id) override {
Around around{with_};
return RD::utf8(id);
};
bool compare(const PropNameID& a, const PropNameID& b) override {
Around around{with_};
return RD::compare(a, b);
};
std::string symbolToString(const Symbol& sym) override {
Around around{with_};
return RD::symbolToString(sym);
};
String createStringFromAscii(const char* str, size_t length) override {
Around around{with_};
return RD::createStringFromAscii(str, length);
};
String createStringFromUtf8(const uint8_t* utf8, size_t length) override {
Around around{with_};
return RD::createStringFromUtf8(utf8, length);
};
std::string utf8(const String& s) override {
Around around{with_};
return RD::utf8(s);
}
Object createObject() override {
Around around{with_};
return RD::createObject();
};
Object createObject(std::shared_ptr<HostObject> ho) override {
Around around{with_};
return RD::createObject(std::move(ho));
};
std::shared_ptr<HostObject> getHostObject(const jsi::Object& o) override {
Around around{with_};
return RD::getHostObject(o);
};
HostFunctionType& getHostFunction(const jsi::Function& f) override {
Around around{with_};
return RD::getHostFunction(f);
};
Value getProperty(const Object& o, const PropNameID& name) override {
Around around{with_};
return RD::getProperty(o, name);
};
Value getProperty(const Object& o, const String& name) override {
Around around{with_};
return RD::getProperty(o, name);
};
bool hasProperty(const Object& o, const PropNameID& name) override {
Around around{with_};
return RD::hasProperty(o, name);
};
bool hasProperty(const Object& o, const String& name) override {
Around around{with_};
return RD::hasProperty(o, name);
};
void setPropertyValue(
const Object& o,
const PropNameID& name,
const Value& value) override {
Around around{with_};
RD::setPropertyValue(o, name, value);
};
void setPropertyValue(const Object& o, const String& name, const Value& value)
override {
Around around{with_};
RD::setPropertyValue(o, name, value);
};
bool isArray(const Object& o) const override {
Around around{with_};
return RD::isArray(o);
};
bool isArrayBuffer(const Object& o) const override {
Around around{with_};
return RD::isArrayBuffer(o);
};
bool isFunction(const Object& o) const override {
Around around{with_};
return RD::isFunction(o);
};
bool isHostObject(const jsi::Object& o) const override {
Around around{with_};
return RD::isHostObject(o);
};
bool isHostFunction(const jsi::Function& f) const override {
Around around{with_};
return RD::isHostFunction(f);
};
Array getPropertyNames(const Object& o) override {
Around around{with_};
return RD::getPropertyNames(o);
};
WeakObject createWeakObject(const Object& o) override {
Around around{with_};
return RD::createWeakObject(o);
};
Value lockWeakObject(const WeakObject& wo) override {
Around around{with_};
return RD::lockWeakObject(wo);
};
Array createArray(size_t length) override {
Around around{with_};
return RD::createArray(length);
};
ArrayBuffer createArrayBuffer(
std::shared_ptr<MutableBuffer> buffer) override {
return RD::createArrayBuffer(std::move(buffer));
};
size_t size(const Array& a) override {
Around around{with_};
return RD::size(a);
};
size_t size(const ArrayBuffer& ab) override {
Around around{with_};
return RD::size(ab);
};
uint8_t* data(const ArrayBuffer& ab) override {
Around around{with_};
return RD::data(ab);
};
Value getValueAtIndex(const Array& a, size_t i) override {
Around around{with_};
return RD::getValueAtIndex(a, i);
};
void setValueAtIndexImpl(const Array& a, size_t i, const Value& value)
override {
Around around{with_};
RD::setValueAtIndexImpl(a, i, value);
};
Function createFunctionFromHostFunction(
const PropNameID& name,
unsigned int paramCount,
HostFunctionType func) override {
Around around{with_};
return RD::createFunctionFromHostFunction(
name, paramCount, std::move(func));
};
Value call(
const Function& f,
const Value& jsThis,
const Value* args,
size_t count) override {
Around around{with_};
return RD::call(f, jsThis, args, count);
};
Value callAsConstructor(const Function& f, const Value* args, size_t count)
override {
Around around{with_};
return RD::callAsConstructor(f, args, count);
};
// Private data for managing scopes.
Runtime::ScopeState* pushScope() override {
Around around{with_};
return RD::pushScope();
}
void popScope(Runtime::ScopeState* ss) override {
Around around{with_};
RD::popScope(ss);
}
bool strictEquals(const Symbol& a, const Symbol& b) const override {
Around around{with_};
return RD::strictEquals(a, b);
};
bool strictEquals(const String& a, const String& b) const override {
Around around{with_};
return RD::strictEquals(a, b);
};
bool strictEquals(const Object& a, const Object& b) const override {
Around around{with_};
return RD::strictEquals(a, b);
};
bool instanceOf(const Object& o, const Function& f) override {
Around around{with_};
return RD::instanceOf(o, f);
};
private:
// Wrap an RAII type around With& to guarantee after always happens.
struct Around {
Around(With& with) : with_(with) {
detail::BeforeCaller<With>::before(with_);
}
~Around() {
detail::AfterCaller<With>::after(with_);
}
With& with_;
};
With& with_;
};
} // namespace jsi
} // namespace facebook

View File

@@ -0,0 +1,117 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <chrono>
#include <iosfwd>
#include <string>
#include <tuple>
#include <unordered_map>
#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.
/// None of these functions should return newly created jsi values, nor should
/// it modify the values of any jsi values in the heap (although GCs are fine).
class JSI_EXPORT 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 map from a string key to a number associated with that
/// statistic.
virtual std::unordered_map<std::string, int64_t> getHeapInfo(
bool includeExpensive) = 0;
/// Perform a full garbage collection.
/// \param cause The cause of this collection, as it should be reported in
/// logs.
virtual void collectGarbage(std::string cause) = 0;
/// A HeapStatsUpdate is a tuple of the fragment index, the number of objects
/// in that fragment, and the number of bytes used by those objects.
/// A "fragment" is a view of all objects allocated within a time slice.
using HeapStatsUpdate = std::tuple<uint64_t, uint64_t, uint64_t>;
/// Start capturing JS stack-traces for all JS heap allocated objects. These
/// can be accessed via \c ::createSnapshotToFile().
/// \param fragmentCallback If present, invoke this callback every so often
/// with the most recently seen object ID, and a list of fragments that have
/// been updated. This callback will be invoked on the same thread that the
/// runtime is using.
virtual void startTrackingHeapObjectStackTraces(
std::function<void(
uint64_t lastSeenObjectID,
std::chrono::microseconds timestamp,
std::vector<HeapStatsUpdate> stats)> fragmentCallback) = 0;
/// Stop capture JS stack-traces for JS heap allocated objects.
virtual void stopTrackingHeapObjectStackTraces() = 0;
/// Start a heap sampling profiler that will sample heap allocations, and the
/// stack trace they were allocated at. Reports a summary of which functions
/// allocated the most.
/// \param samplingInterval The number of bytes allocated to wait between
/// samples. This will be used as the expected value of a poisson
/// distribution.
virtual void startHeapSampling(size_t samplingInterval) = 0;
/// Turns off the heap sampling profiler previously enabled via
/// \c startHeapSampling. Writes the output of the sampling heap profiler to
/// \p os. The output is a JSON formatted string.
virtual void stopHeapSampling(std::ostream& os) = 0;
/// Captures the heap to a file
///
/// \param path to save the heap capture
virtual void createSnapshotToFile(const std::string& path) = 0;
/// Captures the heap to an output stream
///
/// \param os output stream to write to.
virtual void createSnapshotToStream(std::ostream& os) = 0;
/// If the runtime has been created to trace to a temp file, flush
/// any unwritten parts of the trace of bridge traffic to the file,
/// and return the name of the file. Otherwise, return the empty string.
/// Tracing is disabled after this call.
virtual std::string flushAndDisableBridgeTrafficTrace() = 0;
/// Write basic block profile trace to the given file name.
virtual void writeBasicBlockProfileTraceToFile(
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

View File

@@ -0,0 +1,356 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#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&, float f) {
return Value(static_cast<double>(f));
}
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::createFromUtf8(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);
}
/// Helper to throw while still compiling with exceptions turned off.
template <typename E, typename... Args>
[[noreturn]] inline void throwOrDie(Args&&... args) {
std::rethrow_exception(
std::make_exception_ptr(E{std::forward<Args>(args)...}));
}
} // namespace detail
template <typename T>
inline T Runtime::make(Runtime::PointerValue* pv) {
return T(pv);
}
inline Runtime::PointerValue* Runtime::getPointerValue(jsi::Pointer& pointer) {
return pointer.ptr_;
}
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) const {
setProperty(
runtime, String::createFromAscii(runtime, name), std::forward<T>(value));
}
template <typename T>
void Object::setProperty(Runtime& runtime, const String& name, T&& value)
const {
setPropertyValue(
runtime, name, detail::toValue(runtime, std::forward<T>(value)));
}
template <typename T>
void Object::setProperty(Runtime& runtime, const PropNameID& name, T&& value)
const {
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::throwOrDie<JSINativeException>(
"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);
}
template <typename T>
inline bool Object::hasNativeState(Runtime& runtime) const {
return runtime.hasNativeState(*this) &&
std::dynamic_pointer_cast<T>(runtime.getNativeState(*this));
}
template <>
inline bool Object::hasNativeState<NativeState>(Runtime& runtime) const {
return runtime.hasNativeState(*this);
}
template <typename T>
inline std::shared_ptr<T> Object::getNativeState(Runtime& runtime) const {
assert(hasNativeState<T>(runtime));
return std::static_pointer_cast<T>(runtime.getNativeState(*this));
}
inline void Object::setNativeState(
Runtime& runtime,
std::shared_ptr<NativeState> state) const {
runtime.setNativeState(*this, state);
}
inline void Object::setExternalMemoryPressure(Runtime& runtime, size_t amt)
const {
runtime.setExternalMemoryPressure(*this, amt);
}
inline Array Object::getPropertyNames(Runtime& runtime) const {
return runtime.getPropertyNames(*this);
}
inline Value WeakObject::lock(Runtime& runtime) const {
return runtime.lockWeakObject(*this);
}
template <typename T>
void Array::setValueAtIndex(Runtime& runtime, size_t i, T&& value) const {
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 without wrapping and unwrapping, 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 without wrapping and unwrapping, 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))...});
}
String BigInt::toString(Runtime& runtime, int radix) const {
return runtime.bigintToString(*this, radix);
}
} // namespace jsi
} // namespace facebook

View File

@@ -0,0 +1,534 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <cassert>
#include <cmath>
#include <cstdlib>
#include <stdexcept>
#include <jsi/instrumentation.h>
#include <jsi/jsi.h>
namespace facebook {
namespace jsi {
namespace {
// This is used for generating short exception strings.
std::string kindToString(const Value& v, Runtime* rt = nullptr) {
if (v.isUndefined()) {
return "undefined";
} else if (v.isNull()) {
return "null";
} else if (v.isBool()) {
return v.getBool() ? "true" : "false";
} else if (v.isNumber()) {
return "a number";
} else if (v.isString()) {
return "a string";
} else if (v.isSymbol()) {
return "a symbol";
} else if (v.isBigInt()) {
return "a bigint";
} else {
assert(v.isObject() && "Expecting object.");
return rt != nullptr && v.getObject(*rt).isFunction(*rt) ? "a function"
: "an object";
}
}
// getPropertyAsFunction() will try to create a JSError. If the
// failure is in building a JSError, this will lead to infinite
// recursion. This function is used in place of getPropertyAsFunction
// when building JSError, to avoid that infinite recursion.
Value callGlobalFunction(Runtime& runtime, const char* name, const Value& arg) {
Value v = runtime.global().getProperty(runtime, name);
if (!v.isObject()) {
throw JSINativeException(
std::string("callGlobalFunction: JS global property '") + name +
"' is " + kindToString(v, &runtime) + ", expected a Function");
}
Object o = v.getObject(runtime);
if (!o.isFunction(runtime)) {
throw JSINativeException(
std::string("callGlobalFunction: JS global property '") + name +
"' is a non-callable Object, expected a Function");
}
Function f = std::move(o).getFunction(runtime);
return f.call(runtime, arg);
}
} // namespace
Buffer::~Buffer() = default;
MutableBuffer::~MutableBuffer() = default;
PreparedJavaScript::~PreparedJavaScript() = default;
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() {}
NativeState::~NativeState() {}
Runtime::~Runtime() {}
Instrumentation& Runtime::instrumentation() {
class NoInstrumentation : public Instrumentation {
std::string getRecordedGCStats() override {
return "";
}
std::unordered_map<std::string, int64_t> getHeapInfo(bool) override {
return std::unordered_map<std::string, int64_t>{};
}
void collectGarbage(std::string) override {}
void startTrackingHeapObjectStackTraces(
std::function<void(
uint64_t,
std::chrono::microseconds,
std::vector<HeapStatsUpdate>)>) override {}
void stopTrackingHeapObjectStackTraces() override {}
void startHeapSampling(size_t) override {}
void stopHeapSampling(std::ostream&) override {}
void createSnapshotToFile(const std::string&) override {
throw JSINativeException(
"Default instrumentation cannot create a heap snapshot");
}
void createSnapshotToStream(std::ostream&) override {
throw JSINativeException(
"Default instrumentation cannot create a heap snapshot");
}
std::string flushAndDisableBridgeTrafficTrace() override {
std::abort();
}
void writeBasicBlockProfileTraceToFile(const std::string&) const override {
std::abort();
}
void dumpProfilerSymbolsToFile(const std::string&) const override {
std::abort();
}
};
static NoInstrumentation sharedInstance;
return sharedInstance;
}
Value Runtime::createValueFromJsonUtf8(const uint8_t* json, size_t length) {
Function parseJson = global()
.getPropertyAsObject(*this, "JSON")
.getPropertyAsFunction(*this, "parse");
return parseJson.call(*this, String::createFromUtf8(*this, json, length));
}
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 " +
kindToString(v, &runtime) + ", expected 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 " +
kindToString(std::move(obj), &runtime) + ", expected a Function");
};
return std::move(obj).getFunction(runtime);
}
Array Object::asArray(Runtime& runtime) const& {
if (!isArray(runtime)) {
throw JSError(
runtime,
"Object is " + kindToString(Value(runtime, *this), &runtime) +
", expected an array");
}
return getArray(runtime);
}
Array Object::asArray(Runtime& runtime) && {
if (!isArray(runtime)) {
throw JSError(
runtime,
"Object is " + kindToString(Value(runtime, *this), &runtime) +
", expected an array");
}
return std::move(*this).getArray(runtime);
}
Function Object::asFunction(Runtime& runtime) const& {
if (!isFunction(runtime)) {
throw JSError(
runtime,
"Object is " + kindToString(Value(runtime, *this), &runtime) +
", expected a function");
}
return getFunction(runtime);
}
Function Object::asFunction(Runtime& runtime) && {
if (!isFunction(runtime)) {
throw JSError(
runtime,
"Object is " + kindToString(Value(runtime, *this), &runtime) +
", expected 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_ == SymbolKind) {
new (&data_.pointer) Pointer(runtime.cloneSymbol(other.data_.pointer.ptr_));
} else if (kind_ == BigIntKind) {
new (&data_.pointer) Pointer(runtime.cloneBigInt(other.data_.pointer.ptr_));
} 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();
}
}
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 SymbolKind:
return runtime.strictEquals(
static_cast<const Symbol&>(a.data_.pointer),
static_cast<const Symbol&>(b.data_.pointer));
case BigIntKind:
return runtime.strictEquals(
static_cast<const BigInt&>(a.data_.pointer),
static_cast<const BigInt&>(b.data_.pointer));
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;
}
bool Value::asBool() const {
if (!isBool()) {
throw JSINativeException(
"Value is " + kindToString(*this) + ", expected a boolean");
}
return getBool();
}
double Value::asNumber() const {
if (!isNumber()) {
throw JSINativeException(
"Value is " + kindToString(*this) + ", expected a number");
}
return getNumber();
}
Object Value::asObject(Runtime& rt) const& {
if (!isObject()) {
throw JSError(
rt, "Value is " + kindToString(*this, &rt) + ", expected an Object");
}
return getObject(rt);
}
Object Value::asObject(Runtime& rt) && {
if (!isObject()) {
throw JSError(
rt, "Value is " + kindToString(*this, &rt) + ", expected an Object");
}
auto ptr = data_.pointer.ptr_;
data_.pointer.ptr_ = nullptr;
return static_cast<Object>(ptr);
}
Symbol Value::asSymbol(Runtime& rt) const& {
if (!isSymbol()) {
throw JSError(
rt, "Value is " + kindToString(*this, &rt) + ", expected a Symbol");
}
return getSymbol(rt);
}
Symbol Value::asSymbol(Runtime& rt) && {
if (!isSymbol()) {
throw JSError(
rt, "Value is " + kindToString(*this, &rt) + ", expected a Symbol");
}
return std::move(*this).getSymbol(rt);
}
BigInt Value::asBigInt(Runtime& rt) const& {
if (!isBigInt()) {
throw JSError(
rt, "Value is " + kindToString(*this, &rt) + ", expected a BigInt");
}
return getBigInt(rt);
}
BigInt Value::asBigInt(Runtime& rt) && {
if (!isBigInt()) {
throw JSError(
rt, "Value is " + kindToString(*this, &rt) + ", expected a BigInt");
}
return std::move(*this).getBigInt(rt);
}
String Value::asString(Runtime& rt) const& {
if (!isString()) {
throw JSError(
rt, "Value is " + kindToString(*this, &rt) + ", expected a String");
}
return getString(rt);
}
String Value::asString(Runtime& rt) && {
if (!isString()) {
throw JSError(
rt, "Value is " + kindToString(*this, &rt) + ", expected 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);
}
uint64_t BigInt::asUint64(Runtime& runtime) const {
if (!isUint64(runtime)) {
throw JSError(runtime, "Lossy truncation in BigInt64::asUint64");
}
return getUint64(runtime);
}
int64_t BigInt::asInt64(Runtime& runtime) const {
if (!isInt64(runtime)) {
throw JSError(runtime, "Lossy truncation in BigInt64::asInt64");
}
return getInt64(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,
callGlobalFunction(rt, "Error", String::createFromUtf8(rt, message_)));
} catch (const JSIException& ex) {
message_ = std::string(ex.what()) + " (while raising " + message_ + ")";
setValue(rt, String::createFromUtf8(rt, message_));
}
}
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 (const JSIException& ex) {
setValue(rt, String::createFromUtf8(rt, ex.what()));
}
}
JSError::JSError(std::string what, Runtime& rt, Value&& value)
: JSIException(std::move(what)) {
setValue(rt, std::move(value));
}
JSError::JSError(Value&& value, std::string message, std::string stack)
: JSIException(message + "\n\n" + stack),
value_(std::make_shared<Value>(std::move(value))),
message_(std::move(message)),
stack_(std::move(stack)) {}
void JSError::setValue(Runtime& rt, Value&& value) {
value_ = std::make_shared<Value>(std::move(value));
if ((message_.empty() || stack_.empty()) && value_->isObject()) {
auto obj = value_->getObject(rt);
if (message_.empty()) {
try {
Value message = obj.getProperty(rt, "message");
if (!message.isUndefined() && !message.isString()) {
message = callGlobalFunction(rt, "String", message);
}
if (message.isString()) {
message_ = message.getString(rt).utf8(rt);
} else if (!message.isUndefined()) {
message_ = "String(e.message) is a " + kindToString(message, &rt);
}
} catch (const JSIException& ex) {
message_ = std::string("[Exception while creating message string: ") +
ex.what() + "]";
}
}
if (stack_.empty()) {
try {
Value stack = obj.getProperty(rt, "stack");
if (!stack.isUndefined() && !stack.isString()) {
stack = callGlobalFunction(rt, "String", stack);
}
if (stack.isString()) {
stack_ = stack.getString(rt).utf8(rt);
} else if (!stack.isUndefined()) {
stack_ = "String(e.stack) is a " + kindToString(stack, &rt);
}
} catch (const JSIException& ex) {
message_ = std::string("[Exception while creating stack string: ") +
ex.what() + "]";
}
}
}
if (message_.empty()) {
try {
if (value_->isString()) {
message_ = value_->getString(rt).utf8(rt);
} else {
Value message = callGlobalFunction(rt, "String", *value_);
if (message.isString()) {
message_ = message.getString(rt).utf8(rt);
} else {
message_ = "String(e) is a " + kindToString(message, &rt);
}
}
} catch (const JSIException& ex) {
message_ = std::string("[Exception while creating message string: ") +
ex.what() + "]";
}
}
if (stack_.empty()) {
stack_ = "no stack";
}
if (what_.empty()) {
what_ = message_ + "\n\n" + stack_;
}
}
JSIException::~JSIException() {}
JSINativeException::~JSINativeException() {}
JSError::~JSError() {}
} // namespace jsi
} // namespace facebook

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,108 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#ifndef _WINDOWS
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <cerrno>
#include <cstdarg>
#include <stdexcept>
#include <jsi/jsilib.h>
namespace facebook {
namespace jsi {
namespace {
constexpr size_t kErrorBufferSize = 512;
__attribute__((format(printf, 1, 2))) void throwFormattedError(
const char* fmt,
...) {
char logBuffer[kErrorBufferSize];
va_list va_args;
va_start(va_args, fmt);
int result = vsnprintf(logBuffer, sizeof(logBuffer), fmt, va_args);
va_end(va_args);
if (result < 0) {
throw JSINativeException(
std::string("Failed to format error message: ") + fmt);
}
throw JSINativeException(logBuffer);
}
class ScopedFile {
public:
ScopedFile(const std::string& path)
: path_(path), fd_(::open(path.c_str(), O_RDONLY)) {
if (fd_ == -1) {
throwFormattedError(
"Could not open %s: %s", path.c_str(), strerror(errno));
}
}
~ScopedFile() {
::close(fd_);
}
size_t size() {
struct stat fileInfo;
if (::fstat(fd_, &fileInfo) == -1) {
throwFormattedError(
"Could not stat %s: %s", path_.c_str(), strerror(errno));
}
return fileInfo.st_size;
}
uint8_t* mmap(size_t size) {
void* result = ::mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd_, 0);
if (result == MAP_FAILED) {
throwFormattedError(
"Could not mmap %s: %s", path_.c_str(), strerror(errno));
}
return reinterpret_cast<uint8_t*>(result);
}
const std::string& path_;
const int fd_;
};
} // namespace
FileBuffer::FileBuffer(const std::string& path) {
ScopedFile file(path);
size_ = file.size();
data_ = file.mmap(size_);
}
FileBuffer::~FileBuffer() {
if (::munmap(data_, size_)) {
// terminate the program with pending exception
try {
throwFormattedError(
"Could not unmap memory (%p, %zu bytes): %s",
data_,
size_,
strerror(errno));
} catch (...) {
std::terminate();
}
}
}
} // namespace jsi
} // namespace facebook
#endif // !defined(_WINDOWS)

View File

@@ -0,0 +1,27 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#ifdef _WINDOWS
#include <jsi/jsilib.h>
namespace facebook {
namespace jsi {
FileBuffer::FileBuffer(const std::string&) {
// TODO(T41045067) Implement this on Windows
throw new JSINativeException("FileBuffer is not implemented on Windows");
}
FileBuffer::~FileBuffer() {
assert(false && "FileBuffer is not implemented on Windows");
}
} // namespace jsi
} // namespace facebook
#endif //_WINDOWS

View File

@@ -0,0 +1,59 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <jsi/jsi.h>
namespace facebook {
namespace jsi {
class FileBuffer : public Buffer {
public:
FileBuffer(const std::string& path);
~FileBuffer() override;
size_t size() const override {
return size_;
}
const uint8_t* data() const override {
return data_;
}
private:
size_t size_;
uint8_t* data_;
};
// A trivial implementation of PreparedJavaScript that simply stores the source
// buffer and URL.
class SourceJavaScriptPreparation final : public jsi::PreparedJavaScript,
public jsi::Buffer {
std::shared_ptr<const jsi::Buffer> buf_;
std::string sourceURL_;
public:
SourceJavaScriptPreparation(
std::shared_ptr<const jsi::Buffer> buf,
std::string sourceURL)
: buf_(std::move(buf)), sourceURL_(std::move(sourceURL)) {}
const std::string& sourceURL() const {
return sourceURL_;
}
size_t size() const override {
return buf_->size();
}
const uint8_t* data() const override {
return buf_->data();
}
};
} // namespace jsi
} // namespace facebook

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,49 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <functional>
#include <memory>
#include <vector>
#include <gtest/gtest.h>
#include <jsi/jsi.h>
namespace facebook {
namespace jsi {
class Runtime;
using RuntimeFactory = std::function<std::unique_ptr<Runtime>()>;
std::vector<RuntimeFactory> runtimeGenerators();
class JSITestBase : public ::testing::TestWithParam<RuntimeFactory> {
public:
JSITestBase() : factory(GetParam()), runtime(factory()), rt(*runtime) {}
Value eval(const char* code) {
return rt.global().getPropertyAsFunction(rt, "eval").call(rt, code);
}
Function function(const std::string& code) {
return eval(("(" + code + ")").c_str()).getObject(rt).getFunction(rt);
}
bool checkValue(const Value& value, const std::string& jsValue) {
return function("function(value) { return value == " + jsValue + "; }")
.call(rt, std::move(value))
.getBool();
}
RuntimeFactory factory;
std::unique_ptr<Runtime> runtime;
Runtime& rt;
};
} // namespace jsi
} // namespace facebook

View File

@@ -0,0 +1,79 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <mutex>
#include <jsi/decorator.h>
#include <jsi/jsi.h>
namespace facebook {
namespace jsi {
class ThreadSafeRuntime : public Runtime {
public:
virtual void lock() const = 0;
virtual void unlock() const = 0;
virtual Runtime& getUnsafeRuntime() = 0;
};
namespace detail {
template <typename R, typename L>
struct WithLock {
L lock;
WithLock(R& r) : lock(r) {}
void before() {
lock.lock();
}
void after() {
lock.unlock();
}
};
// The actual implementation of a given ThreadSafeRuntime. It's parameterized
// by:
//
// - R: The actual Runtime type that this wraps
// - L: A lock type that has three members:
// - L(R& r) // ctor
// - void lock()
// - void unlock()
template <typename R, typename L>
class ThreadSafeRuntimeImpl final
: public WithRuntimeDecorator<WithLock<R, L>, R, ThreadSafeRuntime> {
public:
template <typename... Args>
ThreadSafeRuntimeImpl(Args&&... args)
: WithRuntimeDecorator<WithLock<R, L>, R, ThreadSafeRuntime>(
unsafe_,
lock_),
unsafe_(std::forward<Args>(args)...),
lock_(unsafe_) {}
R& getUnsafeRuntime() override {
return WithRuntimeDecorator<WithLock<R, L>, R, ThreadSafeRuntime>::plain();
}
void lock() const override {
lock_.before();
}
void unlock() const override {
lock_.after();
}
private:
R unsafe_;
mutable WithLock<R, L> lock_;
};
} // namespace detail
} // namespace jsi
} // namespace facebook