Skip to content

Commit

Permalink
chore: fix crash due to invalid component reference
Browse files Browse the repository at this point in the history
  • Loading branch information
azimgd committed Dec 8, 2024
1 parent 5129f78 commit 40352bb
Show file tree
Hide file tree
Showing 14 changed files with 305 additions and 190 deletions.
111 changes: 4 additions & 107 deletions cpp/module/SLModuleSpec/SLCommitHook.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,117 +3,12 @@
#include "SLCommitHook.h"
#include "SLContainerShadowNode.h"
#include "SLElementShadowNode.h"

#include "SLTemplate.h"

using namespace facebook::react;

namespace facebook::react {

int nextFamilyTag = -2;

class KeyExtractor {
public:
static std::optional<std::string> extractKey(const std::string& input) {
if (input.length() < 4) {
return std::nullopt;
}

if (input.substr(0, 2) != "{{" || input.substr(input.length() - 2) != "}}") {
return std::nullopt;
}

std::string key = input.substr(2, input.length() - 4);

if (key.find('{') != std::string::npos || key.find('}') != std::string::npos) {
return std::nullopt;
}

if (key.empty()) {
return std::nullopt;
}

return key;
}

static std::vector<std::string> extractAllKeys(const std::string& input) {
std::vector<std::string> keys;
size_t pos = 0;

while (pos < input.length()) {
size_t start = input.find("{{", pos);
if (start == std::string::npos) break;

size_t end = input.find("}}", start);
if (end == std::string::npos) break;

std::string potential_key = input.substr(start, end - start + 2);
auto key = extractKey(potential_key);

if (key) {
keys.push_back(*key);
}

pos = end + 2;
}

return keys;
}
};

auto adjustFamilyTag = [](int tag) {
const int MIN_TAG_VALUE = -2e9;
const int CLAMPED_TAG = -2;
return tag < MIN_TAG_VALUE ? CLAMPED_TAG : tag - 2;
};

ShadowNode::Shared cloneShadowNodeTree(jsi::Runtime *runtime, nlohmann::json* elementData, const ShadowNode::Shared& shadowNode)
{
auto const &componentDescriptor = shadowNode->getComponentDescriptor();
PropsParserContext propsParserContext{shadowNode->getSurfaceId(), *componentDescriptor.getContextContainer().get()};

nextFamilyTag = adjustFamilyTag(nextFamilyTag);

InstanceHandle::Shared instanceHandle = std::make_shared<const InstanceHandle>(
*runtime,
shadowNode->getInstanceHandle(*runtime),
shadowNode->getTag());
auto const fragment = ShadowNodeFamilyFragment{nextFamilyTag, shadowNode->getSurfaceId(), instanceHandle};
auto const family = componentDescriptor.createFamily(fragment);
auto const props = componentDescriptor.cloneProps(propsParserContext, shadowNode->getProps(), {});
auto const state = componentDescriptor.createInitialState(props, family);
auto const nextShadowNode = componentDescriptor.createShadowNode(
ShadowNodeFragment{props, ShadowNodeFragment::childrenPlaceholder(), state}, family);

RawTextShadowNode::ConcreteProps* interpolatedProps;
if (shadowNode->getComponentName() == std::string("RawText")) {
const Props::Shared& nextprops = nextShadowNode->getProps();
interpolatedProps = const_cast<RawTextShadowNode::ConcreteProps*>(
static_cast<const RawTextShadowNode::ConcreteProps*>(nextprops.get())
);

try {
if (!KeyExtractor::extractKey(interpolatedProps->text).has_value()) {
throw false;
}

auto elementDataPointer = nlohmann::json::json_pointer(
"/" + KeyExtractor::extractKey(interpolatedProps->text).value()
);

if ((*elementData).contains(elementDataPointer) && (*elementData)[elementDataPointer].is_string()) {
interpolatedProps->text = (*elementData)[elementDataPointer].get<std::string>();
}
} catch (...) {}
}

for (const auto &childShadowNode : shadowNode->getChildren()) {
auto const clonedChildShadowNode = cloneShadowNodeTree(runtime, elementData, childShadowNode);
componentDescriptor.appendChild(nextShadowNode, clonedChildShadowNode);
}

return nextShadowNode;
}

class SLYogaLayoutableShadowNode : public facebook::react::YogaLayoutableShadowNode {
public:
facebook::yoga::Node& getYogaNode() {
Expand Down Expand Up @@ -171,7 +66,9 @@ RootShadowNode::Unshared SLCommitHook::shadowTreeWillCommit(
for (int i = 0; i < (*containerData).size(); ++i) {
auto elementDataPointer = nlohmann::json::json_pointer("/" + std::to_string(i));
auto* elementData = &(*containerData)[elementDataPointer];
rootShadowNodeChildren->push_back(cloneShadowNodeTree(runtime_, elementData, elementNode.second));
rootShadowNodeChildren->push_back(
SLTemplate::cloneShadowNodeTree(runtime_, elementData, elementNode.second)
);
}
} else {
// rootShadowNodeChildren->push_back(cloneShadowNodeTree(runtime_, elementData, elementNode.second));
Expand Down
50 changes: 50 additions & 0 deletions cpp/module/SLModuleSpec/SLKeyExtractor.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#include "SLKeyExtractor.h"

namespace facebook::react {

std::optional<std::string> SLKeyExtractor::extractKey(const std::string& input) {
if (input.length() < 4) {
return std::nullopt;
}

if (input.substr(0, 2) != "{{" || input.substr(input.length() - 2) != "}}") {
return std::nullopt;
}

std::string key = input.substr(2, input.length() - 4);

if (key.find('{') != std::string::npos || key.find('}') != std::string::npos) {
return std::nullopt;
}

if (key.empty()) {
return std::nullopt;
}

return key;
}

std::vector<std::string> SLKeyExtractor::extractAllKeys(const std::string& input) {
std::vector<std::string> keys;
size_t pos = 0;

while (pos < input.length()) {
size_t start = input.find("{{", pos);
if (start == std::string::npos) break;

size_t end = input.find("}}", start);
if (end == std::string::npos) break;

std::string potential_key = input.substr(start, end - start + 2);
auto key = extractKey(potential_key);

if (key) {
keys.push_back(*key);
}

pos = end + 2;
}

return keys;
}
}
13 changes: 13 additions & 0 deletions cpp/module/SLModuleSpec/SLKeyExtractor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#include <string>
#include <optional>
#include <vector>

namespace facebook::react {

class SLKeyExtractor {
public:
static std::optional<std::string> extractKey(const std::string& input);
static std::vector<std::string> extractAllKeys(const std::string& input);
};

}
74 changes: 74 additions & 0 deletions cpp/module/SLModuleSpec/SLModuleJSI.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#include "SLModuleJSI.h"

namespace facebook::react {

using namespace facebook;

void SLModuleJSI::install(facebook::jsi::Runtime &runtime, std::shared_ptr<SLCommitHook> &commitHook) {
auto registerContainerFamily = facebook::jsi::Function::createFromHostFunction(
runtime,
facebook::jsi::PropNameID::forAscii(runtime, "__NATIVE_registerContainerNode"),
1,
[&](facebook::jsi::Runtime &runtime,
const facebook::jsi::Value &thisValue,
const facebook::jsi::Value *arguments,
size_t count) -> facebook::jsi::Value
{
auto shadowNode = shadowNodeFromValue(runtime, arguments[0]);
commitHook->registerContainerNode(shadowNode);

return facebook::jsi::Value::undefined();
});
runtime.global().setProperty(runtime, "__NATIVE_registerContainerNode", std::move(registerContainerFamily));

auto unregisterContainerFamily = facebook::jsi::Function::createFromHostFunction(
runtime,
facebook::jsi::PropNameID::forAscii(runtime, "__NATIVE_unregisterContainerNode"),
1,
[&](facebook::jsi::Runtime &runtime,
const facebook::jsi::Value &thisValue,
const facebook::jsi::Value *arguments,
size_t count) -> facebook::jsi::Value
{
auto shadowNode = shadowNodeFromValue(runtime, arguments[0]);
commitHook->unregisterContainerNode(shadowNode);

return facebook::jsi::Value::undefined();
});
runtime.global().setProperty(runtime, "__NATIVE_unregisterContainerNode", std::move(unregisterContainerFamily));

auto registerElementFamily = facebook::jsi::Function::createFromHostFunction(
runtime,
facebook::jsi::PropNameID::forAscii(runtime, "__NATIVE_registerElementNode"),
1,
[&](facebook::jsi::Runtime &runtime,
const facebook::jsi::Value &thisValue,
const facebook::jsi::Value *arguments,
size_t count) -> facebook::jsi::Value
{
auto shadowNode = shadowNodeFromValue(runtime, arguments[0]);
commitHook->registerElementNode(shadowNode);

return facebook::jsi::Value::undefined();
});
runtime.global().setProperty(runtime, "__NATIVE_registerElementNode", std::move(registerElementFamily));

auto unregisterElementFamily = facebook::jsi::Function::createFromHostFunction(
runtime,
facebook::jsi::PropNameID::forAscii(runtime, "__NATIVE_unregisterElementNode"),
1,
[&](facebook::jsi::Runtime &runtime,
const facebook::jsi::Value &thisValue,
const facebook::jsi::Value *arguments,
size_t count) -> facebook::jsi::Value
{
auto shadowNode = shadowNodeFromValue(runtime, arguments[0]);
commitHook->unregisterElementNode(shadowNode);

return facebook::jsi::Value::undefined();
});
runtime.global().setProperty(runtime, "__NATIVE_unregisterElementNode", std::move(unregisterElementFamily));
}

}

10 changes: 10 additions & 0 deletions cpp/module/SLModuleSpec/SLModuleJSI.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#include "SLCommitHook.h"

namespace facebook::react {

class SLModuleJSI {
public:
static void install(facebook::jsi::Runtime &runtime, std::shared_ptr<SLCommitHook> &commitHook);
};

}
61 changes: 61 additions & 0 deletions cpp/module/SLModuleSpec/SLTemplate.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#include "SLTemplate.h"

namespace facebook::react {

int nextFamilyTag = -2;

auto adjustFamilyTag = [](int tag) {
const int MIN_TAG_VALUE = -2e9;
const int CLAMPED_TAG = -2;
return tag < MIN_TAG_VALUE ? CLAMPED_TAG : tag - 2;
};

ShadowNode::Shared SLTemplate::cloneShadowNodeTree(jsi::Runtime *runtime, nlohmann::json* elementData, const ShadowNode::Shared& shadowNode)
{
auto const &componentDescriptor = shadowNode->getComponentDescriptor();
PropsParserContext propsParserContext{shadowNode->getSurfaceId(), *componentDescriptor.getContextContainer().get()};

nextFamilyTag = adjustFamilyTag(nextFamilyTag);

InstanceHandle::Shared instanceHandle = std::make_shared<const InstanceHandle>(
*runtime,
shadowNode->getInstanceHandle(*runtime),
shadowNode->getTag());
auto const fragment = ShadowNodeFamilyFragment{nextFamilyTag, shadowNode->getSurfaceId(), instanceHandle};
auto const family = componentDescriptor.createFamily(fragment);
auto const props = componentDescriptor.cloneProps(propsParserContext, shadowNode->getProps(), {});
auto const state = componentDescriptor.createInitialState(props, family);
auto const nextShadowNode = componentDescriptor.createShadowNode(
ShadowNodeFragment{props, ShadowNodeFragment::childrenPlaceholder(), state}, family);

RawTextShadowNode::ConcreteProps* interpolatedProps;
if (shadowNode->getComponentName() == std::string("RawText")) {
const Props::Shared& nextprops = nextShadowNode->getProps();
interpolatedProps = const_cast<RawTextShadowNode::ConcreteProps*>(
static_cast<const RawTextShadowNode::ConcreteProps*>(nextprops.get())
);

try {
if (!SLKeyExtractor::extractKey(interpolatedProps->text).has_value()) {
throw false;
}

auto elementDataPointer = nlohmann::json::json_pointer(
"/" + SLKeyExtractor::extractKey(interpolatedProps->text).value()
);

if ((*elementData).contains(elementDataPointer) && (*elementData)[elementDataPointer].is_string()) {
interpolatedProps->text = (*elementData)[elementDataPointer].get<std::string>();
}
} catch (...) {}
}

for (const auto &childShadowNode : shadowNode->getChildren()) {
auto const clonedChildShadowNode = cloneShadowNodeTree(runtime, elementData, childShadowNode);
componentDescriptor.appendChild(nextShadowNode, clonedChildShadowNode);
}

return nextShadowNode;
}

}
16 changes: 16 additions & 0 deletions cpp/module/SLModuleSpec/SLTemplate.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#include <react/renderer/uimanager/UIManager.h>
#include <react/renderer/components/text/RawTextShadowNode.h>
#include "json.hpp"
#include "SLKeyExtractor.h"

namespace facebook::react {

class SLTemplate {
public:
static ShadowNode::Shared cloneShadowNodeTree(
jsi::Runtime *runtime,
nlohmann::json* elementData,
const ShadowNode::Shared& shadowNode);
};

}
2 changes: 1 addition & 1 deletion example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
import useData from './useData';
import Element from './Element';

const ITEMS_COUNT = 5;
const ITEMS_COUNT = 500;
const IS_INVERTED = false;
const IS_HORIZONTAL = false;
const INITIAL_SCROLL_INDEX = 0;
Expand Down
Loading

0 comments on commit 40352bb

Please sign in to comment.