<commit_before>/*
 * 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 {
    CHECK(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));
  }
}

} // namespace

folly::dynamic dynamicFromValue(Runtime& runtime, const Value& valueInput) {
  std::vector<FromValue> stack;<commit_msg>fix crash when converting symbol or BigInt to dynamic<commit_after>/*
 * 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::vector<FromValue> stack;
{'fraction_non_alphanumeric': 0.13929118097256565, 'fraction_numerical': 0.001412928293889085, 'mean_word_length': 3.027501185395922, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5917044', 'n_tokens_mistral': 2615, 'n_tokens_neox': 2577, 'n_words': 679}