Skip to content

Commit e0921e9

Browse files
generatedunixname1563563004708334meta-codesync[bot]
authored andcommitted
xplat/js/react-native-github/packages/react-native/ReactAndroid/src/main/jni/react/reactnativeblob/BlobCollector.cpp (#58300)
Summary: Pull Request resolved: #58300 Reviewed By: javache Differential Revision: D118604015 fbshipit-source-id: 61769d6987c620e1912f23fbee0dd92bd8a762fd
1 parent d4c8569 commit e0921e9

1 file changed

Lines changed: 151 additions & 0 deletions

File tree

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and 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+
8+
#include "BlobCollector.h"
9+
10+
#include <fbjni/fbjni.h>
11+
#include <gtest/gtest.h>
12+
13+
#include <array>
14+
#include <memory>
15+
#include <string>
16+
17+
namespace facebook::react {
18+
namespace {
19+
20+
class JContext : public jni::JavaClass<JContext> {
21+
public:
22+
static constexpr auto kJavaDescriptor = "Landroid/content/Context;";
23+
};
24+
25+
class JInstrumentation : public jni::JavaClass<JInstrumentation> {
26+
public:
27+
static constexpr auto kJavaDescriptor = "Landroid/app/Instrumentation;";
28+
29+
jni::local_ref<JContext> getTargetContext() {
30+
static const auto method =
31+
javaClassStatic()->getMethod<JContext::javaobject()>(
32+
"getTargetContext");
33+
return method(self());
34+
}
35+
};
36+
37+
class JInstrumentationRegistry
38+
: public jni::JavaClass<JInstrumentationRegistry> {
39+
public:
40+
static constexpr auto kJavaDescriptor =
41+
"Landroidx/test/platform/app/InstrumentationRegistry;";
42+
43+
static jni::local_ref<JInstrumentation> getInstrumentation() {
44+
static const auto method =
45+
javaClassStatic()->getStaticMethod<JInstrumentation::javaobject()>(
46+
"getInstrumentation");
47+
return method(javaClassStatic());
48+
}
49+
};
50+
51+
class JReactApplicationContext
52+
: public jni::JavaClass<JReactApplicationContext> {
53+
public:
54+
static constexpr auto kJavaDescriptor =
55+
"Lcom/facebook/react/bridge/ReactApplicationContext;";
56+
};
57+
58+
class JBridgeReactContext
59+
: public jni::JavaClass<JBridgeReactContext, JReactApplicationContext> {
60+
public:
61+
static constexpr auto kJavaDescriptor =
62+
"Lcom/facebook/react/bridge/BridgeReactContext;";
63+
64+
static jni::local_ref<JReactApplicationContext> create(
65+
jni::alias_ref<JContext> context) {
66+
static const auto constructor =
67+
javaClassStatic()
68+
->getConstructor<JBridgeReactContext::javaobject(
69+
JContext::javaobject)>();
70+
return jni::static_ref_cast<JReactApplicationContext>(
71+
javaClassStatic()->newObject(constructor, context.get()));
72+
}
73+
};
74+
75+
class JBlobModule : public jni::JavaClass<JBlobModule> {
76+
public:
77+
static constexpr auto kJavaDescriptor =
78+
"Lcom/facebook/react/modules/blob/BlobModule;";
79+
80+
static jni::local_ref<JBlobModule> create(
81+
jni::alias_ref<JReactApplicationContext> reactContext) {
82+
static const auto constructor =
83+
javaClassStatic()
84+
->getConstructor<JBlobModule::javaobject(
85+
JReactApplicationContext::javaobject)>();
86+
return javaClassStatic()->newObject(constructor, reactContext.get());
87+
}
88+
89+
void store(jni::alias_ref<jni::JArrayByte> data, const std::string& blobId) {
90+
static const auto method =
91+
javaClassStatic()->getMethod<void(jbyteArray, jstring)>("store");
92+
method(self(), data.get(), jni::make_jstring(blobId).get());
93+
}
94+
95+
jlong getLengthOfBlob(const std::string& blobId) {
96+
static const auto method =
97+
javaClassStatic()->getMethod<jlong(jstring)>("getLengthOfBlob");
98+
return method(self(), jni::make_jstring(blobId).get());
99+
}
100+
};
101+
102+
jni::local_ref<jni::JArrayByte> makeByteArray(const jbyte* bytes, jsize size) {
103+
auto javaBytes = jni::JArrayByte::newArray(size);
104+
javaBytes->setRegion(0, size, bytes);
105+
return javaBytes;
106+
}
107+
108+
class BlobCollectorTest : public ::testing::Test {
109+
protected:
110+
void SetUp() override {
111+
jni::ThreadScope::WithClassLoader([&] {
112+
auto instrumentation = JInstrumentationRegistry::getInstrumentation();
113+
auto context =
114+
JBridgeReactContext::create(instrumentation->getTargetContext());
115+
blobModule_ = jni::make_global(JBlobModule::create(context));
116+
});
117+
}
118+
119+
jni::global_ref<JBlobModule> blobModule_;
120+
};
121+
122+
/*
123+
* Bug this catches: the collector must ask Java for the blob's actual byte
124+
* length and must release the same blob id when the JS host object is
125+
* collected. A mismatch in method names, descriptor, object lifetime, or blob
126+
* id plumbing would either report the wrong external-memory pressure to Hermes
127+
* or leak blob storage after GC.
128+
*/
129+
TEST_F(
130+
BlobCollectorTest,
131+
testBlobCollectorReportsStoredLengthAndRemovesBlobOnDestruction) {
132+
const std::string blobId{"native-collector-test"};
133+
constexpr jsize blobLength = 7;
134+
const std::array<jbyte, blobLength> bytes{0, 1, 1, 2, 3, 5, 8};
135+
136+
jni::ThreadScope::WithClassLoader([&] {
137+
blobModule_->store(makeByteArray(bytes.data(), blobLength), blobId);
138+
ASSERT_EQ(blobModule_->getLengthOfBlob(blobId), blobLength);
139+
140+
{
141+
BlobCollector collector{blobModule_, blobId};
142+
143+
EXPECT_EQ(collector.getBlobLength(), blobLength);
144+
}
145+
146+
EXPECT_EQ(blobModule_->getLengthOfBlob(blobId), 0);
147+
});
148+
}
149+
150+
} // namespace
151+
} // namespace facebook::react

0 commit comments

Comments
 (0)