Skip to content

Commit

Permalink
feat: Actually implement JSIConverter for Map
Browse files Browse the repository at this point in the history
  • Loading branch information
mrousavy committed Sep 14, 2024
1 parent b831fb2 commit 8e8e0ea
Show file tree
Hide file tree
Showing 5 changed files with 194 additions and 63 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,28 @@ class JAnyMap final : public jni::HybridClass<JAnyMap> {
static jni::local_ref<JAnyMap::javaobject> create(const std::shared_ptr<AnyMap>& map) {
return newObjectCxxArgs(map);
}
/**
* Create a new `JAnyMap` with the given pre-allocated size.
*/
static jni::local_ref<JAnyMap::javaobject> create(size_t size) {
return newObjectCxxArgs(size);
}

private:
JAnyMap() {
explicit JAnyMap() {
auto map = jni::JHashMap<jni::JString, jni::JObject>::create();
_map = jni::make_global(map);
}
JAnyMap(const std::shared_ptr<AnyMap>& map) {
// TODO: Implement c++ -> java
throw std::runtime_error("Cannot create a JAnyMap from a C++ AnyMap yet!");
explicit JAnyMap(size_t size) {
auto map = jni::JHashMap<jni::JString, jni::JObject>::create(size);
_map = jni::make_global(map);
}
explicit JAnyMap(const std::shared_ptr<AnyMap>& map) {
// TODO: Implement c++ -> java
throw std::runtime_error("Cannot create a JAnyMap from a C++ AnyMap yet!");
}

protected:
public:
bool contains(const std::string& key) {
static const auto method = _map->javaClassStatic()->getMethod<jboolean(jni::local_ref<jni::JObject>)>("containsKey");
return method(_map, jni::make_jstring(key));
Expand All @@ -66,99 +76,99 @@ class JAnyMap final : public jni::HybridClass<JAnyMap> {
return method(_map, key);
}

protected:
public:
bool isNull(const jni::alias_ref<jni::JString>& key) {
jni::local_ref<jni::JObject> result = get(key);
return result == nullptr;
}
bool isDouble(const jni::alias_ref<jni::JString>& key) {
static const auto doubleClass = jni::JDouble::javaClassStatic();
jni::local_ref<jni::JObject> result = get(key);
return result->isInstanceOf(doubleClass);
static const auto doubleClass = jni::JDouble::javaClassStatic();
jni::local_ref<jni::JObject> result = get(key);
return result->isInstanceOf(doubleClass);
}
bool isBoolean(const jni::alias_ref<jni::JString>& key) {
static const auto booleanClass = jni::JBoolean::javaClassStatic();
jni::local_ref<jni::JObject> result = get(key);
return result->isInstanceOf(booleanClass);
static const auto booleanClass = jni::JBoolean::javaClassStatic();
jni::local_ref<jni::JObject> result = get(key);
return result->isInstanceOf(booleanClass);
}
bool isBigInt(const jni::alias_ref<jni::JString>& key) {
static const auto longClass = jni::JLong::javaClassStatic();
jni::local_ref<jni::JObject> result = get(key);
return result->isInstanceOf(longClass);
static const auto longClass = jni::JLong::javaClassStatic();
jni::local_ref<jni::JObject> result = get(key);
return result->isInstanceOf(longClass);
}
bool isString(const jni::alias_ref<jni::JString>& key) {
static const auto stringClass = jni::JString::javaClassStatic();
jni::local_ref<jni::JObject> result = get(key);
return result->isInstanceOf(stringClass);
static const auto stringClass = jni::JString::javaClassStatic();
jni::local_ref<jni::JObject> result = get(key);
return result->isInstanceOf(stringClass);
}
bool isArray(const jni::alias_ref<jni::JString>& key) {
static const auto arrayClass = jni::JArrayClass<jni::JObject>::javaClassStatic();
jni::local_ref<jni::JObject> result = get(key);
return result->isInstanceOf(arrayClass);
static const auto arrayClass = jni::JArrayClass<jni::JObject>::javaClassStatic();
jni::local_ref<jni::JObject> result = get(key);
return result->isInstanceOf(arrayClass);
}
bool isObject(const jni::alias_ref<jni::JString>& key) {
static const auto mapClass = jni::JMap<jni::JObject, jni::JObject>::javaClassStatic();
jni::local_ref<jni::JObject> result = get(key);
return result->isInstanceOf(mapClass);
static const auto mapClass = jni::JMap<jni::JObject, jni::JObject>::javaClassStatic();
jni::local_ref<jni::JObject> result = get(key);
return result->isInstanceOf(mapClass);
}

protected:
public:
jni::local_ref<jni::JDouble> getDouble(const jni::alias_ref<jni::JString>& key) {
jni::local_ref<jni::JObject> result = get(key);
return jni::static_ref_cast<jni::JDouble>(result);
}
jni::local_ref<jni::JBoolean> getBoolean(const jni::alias_ref<jni::JString>& key) {
jni::local_ref<jni::JObject> result = get(key);
return jni::static_ref_cast<jni::JBoolean>(result);
jni::local_ref<jni::JBoolean> getBoolean(const jni::alias_ref<jni::JString>& key) {
jni::local_ref<jni::JObject> result = get(key);
return jni::static_ref_cast<jni::JBoolean>(result);
}
jni::local_ref<jni::JLong> getBigInt(const jni::alias_ref<jni::JString>& key) {
jni::local_ref<jni::JObject> result = get(key);
return jni::static_ref_cast<jni::JLong>(result);
jni::local_ref<jni::JLong> getBigInt(const jni::alias_ref<jni::JString>& key) {
jni::local_ref<jni::JObject> result = get(key);
return jni::static_ref_cast<jni::JLong>(result);
}
jni::local_ref<jni::JString> getString(const jni::alias_ref<jni::JString>& key) {
jni::local_ref<jni::JObject> result = get(key);
return jni::static_ref_cast<jni::JString>(result);
jni::local_ref<jni::JString> getString(const jni::alias_ref<jni::JString>& key) {
jni::local_ref<jni::JObject> result = get(key);
return jni::static_ref_cast<jni::JString>(result);
}
jni::local_ref<jni::JArrayClass<jni::JObject>> getAnyArray(const jni::alias_ref<jni::JString>& key) {
jni::local_ref<jni::JObject> result = get(key);
return jni::static_ref_cast<jni::JArrayClass<jni::JObject>>(result);
jni::local_ref<jni::JArrayClass<jni::JObject>> getAnyArray(const jni::alias_ref<jni::JString>& key) {
jni::local_ref<jni::JObject> result = get(key);
return jni::static_ref_cast<jni::JArrayClass<jni::JObject>>(result);
}
jni::local_ref<jni::JMap<jni::JString, jni::JObject>> getAnyObject(const jni::alias_ref<jni::JString>& key) {
jni::local_ref<jni::JObject> result = get(key);
return jni::static_ref_cast<jni::JMap<jni::JString, jni::JObject>>(result);
jni::local_ref<jni::JObject> result = get(key);
return jni::static_ref_cast<jni::JMap<jni::JString, jni::JObject>>(result);
}

protected:
public:
void setNull(const jni::alias_ref<jni::JString>& key) {
_map->put(key, nullptr);
}
void setDouble(const jni::alias_ref<jni::JString>& key, double value) {
_map->put(key, jni::autobox(value));
}
void setBoolean(const jni::alias_ref<jni::JString>& key, bool value) {
_map->put(key, jni::autobox(value));
}
void setBigInt(const jni::alias_ref<jni::JString>& key, int64_t value) {
_map->put(key, jni::autobox(value));
}
void setDouble(const jni::alias_ref<jni::JString>& key, double value) {
_map->put(key, jni::autobox(value));
}
void setBoolean(const jni::alias_ref<jni::JString>& key, bool value) {
_map->put(key, jni::autobox(value));
}
void setBigInt(const jni::alias_ref<jni::JString>& key, int64_t value) {
_map->put(key, jni::autobox(value));
}
void setString(const jni::alias_ref<jni::JString>& key, const jni::alias_ref<jni::JString>& value) {
_map->put(key, value);
_map->put(key, value);
}
void setAnyArray(const jni::alias_ref<jni::JString>& key, jni::alias_ref<jni::JArrayClass<jni::JObject>> value) {
_map->put(key, value);
_map->put(key, value);
}
void setAnyObject(const jni::alias_ref<jni::JString>& key, jni::alias_ref<jni::JMap<jni::JString, jni::JObject>> value) {
_map->put(key, value);
_map->put(key, value);
}

protected:
public:
jni::global_ref<jni::JHashMap<jni::JString, jni::JObject>> getJavaMap() {
return _map;
return _map;
}

public:
std::shared_ptr<AnyMap> getMap() const {
// TODO: java -> c++
// TODO: java -> c++
throw std::runtime_error("JAnyMap cannot be converted to C++ AnyMap yet!");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ struct JSIConverter;
#include "AnyMap.hpp"
#include "JAnyMap.hpp"
#include "JSIConverter.hpp"
#include "JSIConverter+JObject.hpp"
#include <fbjni/fbjni.h>
#include <jni.h>
#include <jsi/jsi.h>
Expand All @@ -25,17 +26,36 @@ using namespace facebook;
template <>
struct JSIConverter<JAnyMap::javaobject> final {
static inline jni::local_ref<JAnyMap::javaobject> fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
// TODO: Stay within Java's data structures to convert AnyMap more efficiently.
auto anyMap = JSIConverter<std::shared_ptr<AnyMap>>::fromJSI(runtime, arg);
return JAnyMap::create(anyMap);
jsi::Object object = arg.asObject(runtime);
jsi::Array properties = object.getPropertyNames(runtime);
size_t size = properties.size(runtime);

jni::local_ref<JAnyMap::javaobject> map = JAnyMap::create(size);
jni::alias_ref<jni::JMap<jni::JString, jni::JObject>> javaMap = map->cthis()->getJavaMap();
for (size_t i = 0; i < size; i++) {
jsi::String prop = properties.getValueAtIndex(runtime, i).asString(runtime);
jsi::Value value = object.getProperty(runtime, prop);
javaMap->put(/* key */ jni::make_jstring(prop.utf8(runtime)),
/* value */ JSIConverter<jni::JObject>::fromJSI(runtime, value));
}

return map;
}
static inline jsi::Value toJSI(jsi::Runtime& runtime, const jni::alias_ref<JAnyMap::javaobject>& arg) {
// TODO: Stay within Java's data structures to convert AnyMap more efficiently.
auto anyMap = arg->cthis()->getMap();
return JSIConverter<std::shared_ptr<AnyMap>>::toJSI(runtime, anyMap);
jsi::Object object(runtime);
auto map = arg->cthis()->getJavaMap();
for (const auto& entry : *map) {
jsi::String key = JSIConverter<jni::JString>::toJSI(runtime, entry.first).getString(runtime);
jni::alias_ref<jni::JObject> value = entry.second;

object.setProperty(runtime, key, JSIConverter<jni::JObject>::toJSI(runtime, value));
}
return object;
}
static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
return JSIConverter<std::shared_ptr<AnyMap>>::canConvert(runtime, value);
if (!value.isObject()) return false;
jsi::Object object = value.getObject(runtime);
return isPlainObject(runtime, object);
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ struct JSIConverter<jni::JArrayClass<T>> final {

jni::local_ref<jni::JArrayClass<T>> result = jni::JArrayClass<T>::newArray(size);
for (size_t i = 0; i < size; i++) {
result->setElement(i, *JSIConverter<T>::fromJSI(runtime, array.getValueAtIndex(runtime, i)));
auto element = JSIConverter<T>::fromJSI(runtime, array.getValueAtIndex(runtime, i));
result->setElement(i, *element);
}
return result;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "JSIConverter+JHybridObject.hpp"
#include "JSIConverter+JMap.hpp"
#include "JSIConverter+JNIReference.hpp"
#include "JSIConverter+JObject.hpp"
#include "JSIConverter+JPromise.hpp"
#include "JSIConverter+JString.hpp"
#include "JSIConverter+PrimitiveArray.hpp"
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
//
// Created by Marc Rousavy on 21.02.24.
//

#pragma once

// Forward declare a few of the common types that might have cyclic includes.
namespace margelo::nitro {
template <typename T, typename Enable>
struct JSIConverter;
} // namespace margelo::nitro

#include "JSIConverter.hpp"
#include "JSIConverter+JString.hpp"
#include "JSIConverter+JArrayClass.hpp"
#include "JSIConverter+JMap.hpp"
#include "JSIConverter+BoxedPrimitives.hpp"
#include <fbjni/fbjni.h>
#include <jni.h>
#include <jsi/jsi.h>

namespace margelo::nitro {

using namespace facebook;

template<>
struct JSIConverter<jni::JObject> final {
static inline jsi::Value toJSI(jsi::Runtime& runtime, jni::alias_ref<jni::JObject> boxedValue) {
if (boxedValue->isInstanceOf(jni::JDouble::javaClassStatic())) {
// It's a double
return JSIConverter<jni::JDouble>::toJSI(runtime, jni::static_ref_cast<jni::JDouble>(boxedValue));
} else if (boxedValue->isInstanceOf(jni::JBoolean::javaClassStatic())) {
// It's a boolean
return JSIConverter<jni::JBoolean>::toJSI(runtime, jni::static_ref_cast<jni::JBoolean>(boxedValue));
} else if (boxedValue->isInstanceOf(jni::JLong::javaClassStatic())) {
// It's a long
return JSIConverter<jni::JLong>::toJSI(runtime, jni::static_ref_cast<jni::JLong>(boxedValue));
} else if (boxedValue->isInstanceOf(jni::JString::javaClassStatic())) {
// It's a string
return JSIConverter<jni::JString>::toJSI(runtime, jni::static_ref_cast<jni::JString>(boxedValue));
} else if (boxedValue->isInstanceOf(jni::JArrayClass<jni::JObject>::javaClassStatic())) {
// It's an array
return JSIConverter<jni::JArrayClass<jni::JObject>>::toJSI(runtime, jni::static_ref_cast<jni::JArrayClass<jni::JObject>>(boxedValue));
} else if (boxedValue->isInstanceOf(jni::JMap<jni::JString, jni::JObject>::javaClassStatic())) {
// It's a map
return JSIConverter<jni::JMap<jni::JString, jni::JObject>>::toJSI(runtime, jni::static_ref_cast<jni::JMap<jni::JString, jni::JObject>>(boxedValue));
} else [[unlikely]] {
throw std::runtime_error("Cannot convert \"" + boxedValue->toString() + "\" to jsi::Value!");
}
}
static inline jni::local_ref<jni::JObject> fromJSI(jsi::Runtime& runtime, const jsi::Value& value) {
if (JSIConverter<jni::JDouble>::canConvert(runtime, value)) {
// It's a double
return JSIConverter<jni::JDouble>::fromJSI(runtime, value);
} else if (JSIConverter<jni::JBoolean>::canConvert(runtime, value)) {
// It's a boolean
return JSIConverter<jni::JBoolean>::fromJSI(runtime, value);
} else if (JSIConverter<jni::JLong>::canConvert(runtime, value)) {
// It's a long
return JSIConverter<jni::JLong>::fromJSI(runtime, value);
} else if (JSIConverter<jni::JString>::canConvert(runtime, value)) {
// It's a string
return JSIConverter<jni::JString>::fromJSI(runtime, value);
} else if (JSIConverter<jni::JArrayClass<jni::JObject>>::canConvert(runtime, value)) {
// It's an array
return JSIConverter<jni::JArrayClass<jni::JObject>>::fromJSI(runtime, value);
} else if (JSIConverter<jni::JMap<jni::JString, jni::JObject>>::canConvert(runtime, value)) {
// It's a map
#ifndef NDEBUG
if (!isPlainObject(runtime, value.asObject(runtime))) [[unlikely]] {
std::string stringRepresentation = value.toString(runtime).utf8(runtime);
throw std::runtime_error("Cannot convert \"" + stringRepresentation + "\" to JObject!");
}
#endif
return JSIConverter<jni::JMap<jni::JString, jni::JObject>>::fromJSI(runtime, value);
} else [[unlikely]] {
std::string stringRepresentation = value.toString(runtime).utf8(runtime);
throw std::runtime_error("Cannot convert \"" + stringRepresentation + "\" to JObject!");
}
}
static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
if (JSIConverter<jni::JDouble>::canConvert(runtime, value)) {
return true;
} else if (JSIConverter<jni::JBoolean>::canConvert(runtime, value)) {
return true;
} else if (JSIConverter<jni::JLong>::canConvert(runtime, value)) {
return true;
} else if (JSIConverter<jni::JString>::canConvert(runtime, value)) {
return true;
} else if (JSIConverter<jni::JArrayClass<jni::JObject>>::canConvert(runtime, value)) {
return true;
} else if (JSIConverter<jni::JMap<jni::JString, jni::JObject>>::canConvert(runtime, value)) {
return true;
}
return false;
}
};

} // namespace margelo::nitro

0 comments on commit 8e8e0ea

Please sign in to comment.