Skip to content

Commit

Permalink
fix: Return JMap instead of JHashMap in typed map converter (`Rec…
Browse files Browse the repository at this point in the history
…ord<string, T>`) (#518)

* chore: Add Map in struct tests (`Record<string, T>` inside a plain struct/object)

* feat: Add `extractMap(mapWrapper)` test

* Update HybridTestObjectSwift.swift

* fix: Return `JMap` instead of `JHashMap` in record converter
  • Loading branch information
mrousavy authored Feb 3, 2025
1 parent af6db9b commit e5925a3
Show file tree
Hide file tree
Showing 23 changed files with 491 additions and 152 deletions.
13 changes: 12 additions & 1 deletion example/src/getTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ const TEST_MAP: Record<string, number | boolean> = {
a_bool: true,
another_bool: false,
}
const TEST_MAP_2: Record<string, string> = {
'someKey': 'someValue',
'anotherKey': 'another-value',
'third-key': 'thirdValue',
}

function createTest<T>(
name: string,
Expand Down Expand Up @@ -730,12 +735,18 @@ export function getTests(
// Swift/Kotlin Test Object does not have tuples yet!
]),

createTest('bounceMap()', () =>
createTest('bounceMap(map) === map', () =>
it(() => testObject.bounceMap(TEST_MAP))
.didNotThrow()
.didReturn('object')
.equals(TEST_MAP)
),
createTest('extractMap(mapWrapper) === mapWrapper.map', () =>
it(() => testObject.extractMap({ map: TEST_MAP_2 }))
.didNotThrow()
.didReturn('object')
.equals(TEST_MAP_2)
),

// Promises
createTest('wait', async () =>
Expand Down
7 changes: 4 additions & 3 deletions packages/nitrogen/src/syntax/kotlin/KotlinCxxBridgedType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -500,10 +500,11 @@ export class KotlinCxxBridgedType implements BridgedType<'kotlin', 'c++'> {
'__entry.second',
'c++'
)
const javaMapType = `jni::JHashMap<${key.getTypeCode('c++')}, ${value.getTypeCode('c++')}>`
const javaMapType = `jni::JMap<${key.getTypeCode('c++')}, ${value.getTypeCode('c++')}>`
const javaHashMapType = `jni::JHashMap<${key.getTypeCode('c++')}, ${value.getTypeCode('c++')}>`
return `
[&]() {
auto __map = ${javaMapType}::create(${parameterName}.size());
[&]() -> jni::local_ref<${javaMapType}> {
auto __map = ${javaHashMapType}::create(${parameterName}.size());
for (const auto& __entry : ${parameterName}) {
__map->put(${indent(parseKey, ' ')}, ${indent(parseValue, ' ')});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@ class HybridTestObjectKotlin: HybridTestObjectSwiftKotlinSpec() {
return map
}

override fun extractMap(mapWrapper: MapWrapper): Map<String, String> {
return mapWrapper.map
}

override fun calculateFibonacciSync(value: Double): Long {
val n = value.toInt()
if (n == 0) return 0L
Expand Down
4 changes: 4 additions & 0 deletions packages/react-native-nitro-image/cpp/HybridTestObjectCpp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,10 @@ HybridTestObjectCpp::bounceMap(const std::unordered_map<std::string, std::varian
return map;
}

std::unordered_map<std::string, std::string> HybridTestObjectCpp::extractMap(const MapWrapper& mapWrapper) {
return mapWrapper.map;
}

std::shared_ptr<Promise<int64_t>> HybridTestObjectCpp::calculateFibonacciAsync(double value) {
return Promise<int64_t>::async([this, value]() -> int64_t { return this->calculateFibonacci(value); });
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ class HybridTestObjectCpp : public HybridTestObjectCppSpec {
std::tuple<double, std::string, bool> passTuple(const std::tuple<double, std::string, bool>& tuple) override;
std::unordered_map<std::string, std::variant<double, bool>>
bounceMap(const std::unordered_map<std::string, std::variant<double, bool>>& map) override;
std::unordered_map<std::string, std::string> extractMap(const MapWrapper& mapWrapper) override;
int64_t calculateFibonacciSync(double value) override;
std::shared_ptr<Promise<int64_t>> calculateFibonacciAsync(double value) override;
std::shared_ptr<Promise<void>> wait(double seconds) override;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,10 @@ class HybridTestObjectSwift : HybridTestObjectSwiftKotlinSpec {
return map
}

func extractMap(mapWrapper: MapWrapper) throws -> Dictionary<String, String> {
return mapWrapper.map
}

func calculateFibonacciSync(value: Double) throws -> Int64 {
let n = Int64(value)
if n <= 1 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ namespace NitroModules { class ArrayBuffer; }
namespace margelo::nitro::image { class HybridChildSpec; }
// Forward declaration of `HybridBaseSpec` to properly resolve imports.
namespace margelo::nitro::image { class HybridBaseSpec; }
// Forward declaration of `MapWrapper` to properly resolve imports.
namespace margelo::nitro::image { struct MapWrapper; }
// Forward declaration of `JsStyleStruct` to properly resolve imports.
namespace margelo::nitro::image { struct JsStyleStruct; }

Expand All @@ -47,10 +49,10 @@ namespace margelo::nitro::image { struct JsStyleStruct; }
#include "JPerson.hpp"
#include <NitroModules/AnyMap.hpp>
#include <NitroModules/JAnyMap.hpp>
#include <NitroModules/Promise.hpp>
#include <NitroModules/JPromise.hpp>
#include <unordered_map>
#include "JVariant_Double_Boolean.hpp"
#include <NitroModules/Promise.hpp>
#include <NitroModules/JPromise.hpp>
#include "Car.hpp"
#include "JCar.hpp"
#include <NitroModules/ArrayBuffer.hpp>
Expand All @@ -61,6 +63,8 @@ namespace margelo::nitro::image { struct JsStyleStruct; }
#include "HybridBaseSpec.hpp"
#include "JHybridBaseSpec.hpp"
#include "JFunc_void_std__vector_Powertrain_.hpp"
#include "MapWrapper.hpp"
#include "JMapWrapper.hpp"
#include <exception>
#include "JFunc_void.hpp"
#include "JFunc_void_std__optional_double_.hpp"
Expand Down Expand Up @@ -366,6 +370,36 @@ namespace margelo::nitro::image {
auto __result = method(_javaPart, JAnyMap::create(map));
return __result->cthis()->getMap();
}
std::unordered_map<std::string, std::variant<double, bool>> JHybridTestObjectSwiftKotlinSpec::bounceMap(const std::unordered_map<std::string, std::variant<double, bool>>& map) {
static const auto method = _javaPart->getClass()->getMethod<jni::local_ref<jni::JMap<jni::JString, JVariant_Double_Boolean>>(jni::alias_ref<jni::JMap<jni::JString, JVariant_Double_Boolean>> /* map */)>("bounceMap");
auto __result = method(_javaPart, [&]() -> jni::local_ref<jni::JMap<jni::JString, JVariant_Double_Boolean>> {
auto __map = jni::JHashMap<jni::JString, JVariant_Double_Boolean>::create(map.size());
for (const auto& __entry : map) {
__map->put(jni::make_jstring(__entry.first), JVariant_Double_Boolean::fromCpp(__entry.second));
}
return __map;
}());
return [&]() {
std::unordered_map<std::string, std::variant<double, bool>> __map;
__map.reserve(__result->size());
for (const auto& __entry : *__result) {
__map.emplace(__entry.first->toStdString(), __entry.second->toCpp());
}
return __map;
}();
}
std::unordered_map<std::string, std::string> JHybridTestObjectSwiftKotlinSpec::extractMap(const MapWrapper& mapWrapper) {
static const auto method = _javaPart->getClass()->getMethod<jni::local_ref<jni::JMap<jni::JString, jni::JString>>(jni::alias_ref<JMapWrapper> /* mapWrapper */)>("extractMap");
auto __result = method(_javaPart, JMapWrapper::fromCpp(mapWrapper));
return [&]() {
std::unordered_map<std::string, std::string> __map;
__map.reserve(__result->size());
for (const auto& __entry : *__result) {
__map.emplace(__entry.first->toStdString(), __entry.second->toStdString());
}
return __map;
}();
}
double JHybridTestObjectSwiftKotlinSpec::funcThatThrows() {
static const auto method = _javaPart->getClass()->getMethod<double()>("funcThatThrows");
auto __result = method(_javaPart);
Expand Down Expand Up @@ -405,24 +439,6 @@ namespace margelo::nitro::image {
auto __result = method(_javaPart, value.has_value() ? JPowertrain::fromCpp(value.value()) : nullptr);
return __result != nullptr ? std::make_optional(__result->toCpp()) : std::nullopt;
}
std::unordered_map<std::string, std::variant<double, bool>> JHybridTestObjectSwiftKotlinSpec::bounceMap(const std::unordered_map<std::string, std::variant<double, bool>>& map) {
static const auto method = _javaPart->getClass()->getMethod<jni::local_ref<jni::JMap<jni::JString, JVariant_Double_Boolean>>(jni::alias_ref<jni::JMap<jni::JString, JVariant_Double_Boolean>> /* map */)>("bounceMap");
auto __result = method(_javaPart, [&]() {
auto __map = jni::JHashMap<jni::JString, JVariant_Double_Boolean>::create(map.size());
for (const auto& __entry : map) {
__map->put(jni::make_jstring(__entry.first), JVariant_Double_Boolean::fromCpp(__entry.second));
}
return __map;
}());
return [&]() {
std::unordered_map<std::string, std::variant<double, bool>> __map;
__map.reserve(__result->size());
for (const auto& __entry : *__result) {
__map.emplace(__entry.first->toStdString(), __entry.second->toCpp());
}
return __map;
}();
}
int64_t JHybridTestObjectSwiftKotlinSpec::calculateFibonacciSync(double value) {
static const auto method = _javaPart->getClass()->getMethod<int64_t(double /* value */)>("calculateFibonacciSync");
auto __result = method(_javaPart, value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,14 @@ namespace margelo::nitro::image {
void complexEnumCallback(const std::vector<Powertrain>& array, const std::function<void(const std::vector<Powertrain>& /* array */)>& callback) override;
std::shared_ptr<AnyMap> createMap() override;
std::shared_ptr<AnyMap> mapRoundtrip(const std::shared_ptr<AnyMap>& map) override;
std::unordered_map<std::string, std::variant<double, bool>> bounceMap(const std::unordered_map<std::string, std::variant<double, bool>>& map) override;
std::unordered_map<std::string, std::string> extractMap(const MapWrapper& mapWrapper) override;
double funcThatThrows() override;
std::shared_ptr<Promise<void>> funcThatThrowsBeforePromise() override;
void throwError(const std::exception_ptr& error) override;
std::string tryOptionalParams(double num, bool boo, const std::optional<std::string>& str) override;
std::string tryMiddleParam(double num, std::optional<bool> boo, const std::string& str) override;
std::optional<Powertrain> tryOptionalEnum(std::optional<Powertrain> value) override;
std::unordered_map<std::string, std::variant<double, bool>> bounceMap(const std::unordered_map<std::string, std::variant<double, bool>>& map) override;
int64_t calculateFibonacciSync(double value) override;
std::shared_ptr<Promise<int64_t>> calculateFibonacciAsync(double value) override;
std::shared_ptr<Promise<void>> wait(double seconds) override;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
///
/// JMapWrapper.hpp
/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
/// https://github.com/mrousavy/nitro
/// Copyright © 2025 Marc Rousavy @ Margelo
///

#pragma once

#include <fbjni/fbjni.h>
#include "MapWrapper.hpp"

#include <string>
#include <unordered_map>

namespace margelo::nitro::image {

using namespace facebook;

/**
* The C++ JNI bridge between the C++ struct "MapWrapper" and the the Kotlin data class "MapWrapper".
*/
struct JMapWrapper final: public jni::JavaClass<JMapWrapper> {
public:
static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/image/MapWrapper;";

public:
/**
* Convert this Java/Kotlin-based struct to the C++ struct MapWrapper by copying all values to C++.
*/
[[maybe_unused]]
[[nodiscard]]
MapWrapper toCpp() const {
static const auto clazz = javaClassStatic();
static const auto fieldMap = clazz->getField<jni::JMap<jni::JString, jni::JString>>("map");
jni::local_ref<jni::JMap<jni::JString, jni::JString>> map = this->getFieldValue(fieldMap);
return MapWrapper(
[&]() {
std::unordered_map<std::string, std::string> __map;
__map.reserve(map->size());
for (const auto& __entry : *map) {
__map.emplace(__entry.first->toStdString(), __entry.second->toStdString());
}
return __map;
}()
);
}

public:
/**
* Create a Java/Kotlin-based struct by copying all values from the given C++ struct to Java.
*/
[[maybe_unused]]
static jni::local_ref<JMapWrapper::javaobject> fromCpp(const MapWrapper& value) {
return newInstance(
[&]() -> jni::local_ref<jni::JMap<jni::JString, jni::JString>> {
auto __map = jni::JHashMap<jni::JString, jni::JString>::create(value.map.size());
for (const auto& __entry : value.map) {
__map->put(jni::make_jstring(__entry.first), jni::make_jstring(__entry.second));
}
return __map;
}()
);
}
};

} // namespace margelo::nitro::image
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,14 @@ abstract class HybridTestObjectSwiftKotlinSpec: HybridObject() {
@Keep
abstract fun mapRoundtrip(map: AnyMap): AnyMap

@DoNotStrip
@Keep
abstract fun bounceMap(map: Map<String, Variant_Double_Boolean>): Map<String, Variant_Double_Boolean>

@DoNotStrip
@Keep
abstract fun extractMap(mapWrapper: MapWrapper): Map<String, String>

@DoNotStrip
@Keep
abstract fun funcThatThrows(): Double
Expand All @@ -205,10 +213,6 @@ abstract class HybridTestObjectSwiftKotlinSpec: HybridObject() {
@Keep
abstract fun tryOptionalEnum(value: Powertrain?): Powertrain?

@DoNotStrip
@Keep
abstract fun bounceMap(map: Map<String, Variant_Double_Boolean>): Map<String, Variant_Double_Boolean>

@DoNotStrip
@Keep
abstract fun calculateFibonacciSync(value: Double): Long
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
///
/// MapWrapper.kt
/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
/// https://github.com/mrousavy/nitro
/// Copyright © 2025 Marc Rousavy @ Margelo
///

package com.margelo.nitro.image

import androidx.annotation.Keep
import com.facebook.proguard.annotations.DoNotStrip
import com.margelo.nitro.core.*

/**
* Represents the JavaScript object/struct "MapWrapper".
*/
@DoNotStrip
@Keep
data class MapWrapper
@DoNotStrip
@Keep
constructor(
val map: Map<String, String>
) {
/* main constructor */
}
Loading

0 comments on commit e5925a3

Please sign in to comment.