UITemplateProcessor.cpp 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. /*
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. */
  7. #include "UITemplateProcessor.h"
  8. #include <folly/json.h>
  9. #include <glog/logging.h>
  10. #include <react/components/view/ViewComponentDescriptor.h>
  11. #include <react/components/view/ViewProps.h>
  12. #include <react/components/view/ViewShadowNode.h>
  13. #include <react/core/ComponentDescriptor.h>
  14. #include <react/core/LayoutContext.h>
  15. #include <react/core/ShadowNodeFragment.h>
  16. #include <react/debug/DebugStringConvertible.h>
  17. #include <react/debug/DebugStringConvertibleItem.h>
  18. namespace facebook {
  19. namespace react {
  20. bool constexpr DEBUG_FLY = false;
  21. struct RBCContext {
  22. const Tag rootTag;
  23. const std::vector<SharedShadowNode> &nodes;
  24. const std::vector<folly::dynamic> &registers;
  25. const ComponentDescriptorRegistry &componentDescriptorRegistry;
  26. const NativeModuleRegistry &nativeModuleRegistry;
  27. };
  28. // TODO: use RBCContext instead of all the separate arguments.
  29. SharedShadowNode UITemplateProcessor::runCommand(
  30. const folly::dynamic &command,
  31. SurfaceId surfaceId,
  32. std::vector<SharedShadowNode> &nodes,
  33. std::vector<folly::dynamic> &registers,
  34. const ComponentDescriptorRegistry &componentDescriptorRegistry,
  35. const NativeModuleRegistry &nativeModuleRegistry,
  36. const std::shared_ptr<const ReactNativeConfig> reactNativeConfig) {
  37. const std::string &opcode = command[0].asString();
  38. const int tagOffset = 420000;
  39. // TODO: change to integer codes and a switch statement
  40. if (opcode == "createNode") {
  41. int tag = command[1].asInt();
  42. const auto &type = command[2].asString();
  43. const auto parentTag = command[3].asInt();
  44. const auto &props = command[4];
  45. nodes[tag] = componentDescriptorRegistry.createNode(
  46. tag + tagOffset, type, surfaceId, props, nullptr);
  47. if (parentTag > -1) { // parentTag == -1 indicates root node
  48. auto parentShadowNode = nodes[parentTag];
  49. auto const &componentDescriptor = componentDescriptorRegistry.at(
  50. parentShadowNode->getComponentHandle());
  51. componentDescriptor.appendChild(parentShadowNode, nodes[tag]);
  52. }
  53. } else if (opcode == "returnRoot") {
  54. if (DEBUG_FLY) {
  55. LOG(INFO)
  56. << "(stop) UITemplateProcessor inject serialized 'server rendered' view tree";
  57. }
  58. return nodes[command[1].asInt()];
  59. } else if (opcode == "loadNativeBool") {
  60. int registerNumber = command[1].asInt();
  61. std::string param = command[4][0].asString();
  62. registers[registerNumber] = reactNativeConfig->getBool(param);
  63. } else if (opcode == "conditional") {
  64. int registerNumber = command[1].asInt();
  65. auto conditionDynamic = registers[registerNumber];
  66. if (conditionDynamic.isNull()) {
  67. // TODO: provide original command or command line?
  68. auto err = std::runtime_error(
  69. "register " + command[1].asString() + " wasn't loaded before access");
  70. throw err;
  71. } else if (conditionDynamic.type() != folly::dynamic::BOOL) {
  72. // TODO: provide original command or command line?
  73. auto err = std::runtime_error(
  74. "register " + command[1].asString() + " had type '" +
  75. conditionDynamic.typeName() +
  76. "' but needs to be 'boolean' for conditionals");
  77. throw err;
  78. }
  79. const auto &nextCommands =
  80. conditionDynamic.asBool() ? command[2] : command[3];
  81. for (const auto &nextCommand : nextCommands) {
  82. runCommand(
  83. nextCommand,
  84. surfaceId,
  85. nodes,
  86. registers,
  87. componentDescriptorRegistry,
  88. nativeModuleRegistry,
  89. reactNativeConfig);
  90. }
  91. } else {
  92. throw std::runtime_error("Unsupported opcode: " + command[0].asString());
  93. }
  94. return nullptr;
  95. }
  96. SharedShadowNode UITemplateProcessor::buildShadowTree(
  97. const std::string &jsonStr,
  98. SurfaceId surfaceId,
  99. const folly::dynamic &params,
  100. const ComponentDescriptorRegistry &componentDescriptorRegistry,
  101. const NativeModuleRegistry &nativeModuleRegistry,
  102. const std::shared_ptr<const ReactNativeConfig> reactNativeConfig) {
  103. if (DEBUG_FLY) {
  104. LOG(INFO)
  105. << "(strt) UITemplateProcessor inject hardcoded 'server rendered' view tree";
  106. }
  107. std::string content = jsonStr;
  108. for (const auto &param : params.items()) {
  109. const auto &key = param.first.asString();
  110. size_t start_pos = content.find(key);
  111. if (start_pos != std::string::npos) {
  112. content.replace(start_pos, key.length(), param.second.asString());
  113. }
  114. }
  115. auto parsed = folly::parseJson(content);
  116. auto commands = parsed["commands"];
  117. std::vector<SharedShadowNode> nodes(commands.size() * 2);
  118. std::vector<folly::dynamic> registers(32);
  119. for (const auto &command : commands) {
  120. try {
  121. if (DEBUG_FLY) {
  122. LOG(INFO) << "try to run command " << folly::toJson(command);
  123. }
  124. auto ret = runCommand(
  125. command,
  126. surfaceId,
  127. nodes,
  128. registers,
  129. componentDescriptorRegistry,
  130. nativeModuleRegistry,
  131. reactNativeConfig);
  132. if (ret != nullptr) {
  133. return ret;
  134. }
  135. } catch (const std::exception &e) {
  136. LOG(ERROR) << " >>> Exception <<< running previous command '"
  137. << folly::toJson(command) << "': '" << e.what() << "'";
  138. }
  139. }
  140. LOG(ERROR) << "react ui template missing returnRoot command :(";
  141. throw std::runtime_error(
  142. "Missing returnRoot command in template content:\n" + content);
  143. return SharedShadowNode{};
  144. }
  145. } // namespace react
  146. } // namespace facebook