Factor out an unnecessary struct member. JNI sqlite3_shutdown() now frees up the various object-recycling bins. Doc touchups.
FossilOrigin-Name: bae4d022aad9bbeb78cb027ecad799af87afe331e697add44ec22297c873141d
This commit is contained in:
parent
60aca33a8b
commit
32a79760b5
@ -511,7 +511,7 @@ struct S3JniUdf {
|
||||
jmethodID jmidxFinal /* Java ID of xFinal method */;
|
||||
jmethodID jmidxValue /* Java ID of xValue method */;
|
||||
jmethodID jmidxInverse /* Java ID of xInverse method */;
|
||||
S3JniUdf * pNext /* Next entry in free-list. */;
|
||||
S3JniUdf * pNext /* Next entry in SJG.udf.aFree. */;
|
||||
};
|
||||
|
||||
#if !defined(SQLITE_JNI_OMIT_METRICS) && !defined(SQLITE_JNI_ENABLE_METRICS)
|
||||
@ -584,25 +584,24 @@ struct S3JniGlobalType {
|
||||
object. Used only for sanity checking. */;
|
||||
} perDb;
|
||||
struct {
|
||||
S3JniUdf * aFree /* Head of the free-item list. Guarded by global
|
||||
mutex. */;
|
||||
S3JniUdf * aFree /* Head of the free-item list. Guarded by global
|
||||
mutex. */;
|
||||
} udf;
|
||||
/*
|
||||
** Refs to global classes and methods. Obtained during static init
|
||||
** and never released.
|
||||
*/
|
||||
struct {
|
||||
jclass cObj /* global ref to java.lang.Object */;
|
||||
jclass cLong /* global ref to java.lang.Long */;
|
||||
jclass cString /* global ref to java.lang.String */;
|
||||
jobject oCharsetUtf8 /* global ref to StandardCharset.UTF_8 */;
|
||||
jmethodID ctorLong1 /* the Long(long) constructor */;
|
||||
jmethodID ctorStringBA /* the String(byte[],Charset) constructor */;
|
||||
jmethodID stringGetBytes /* the String.getBytes(Charset) method */;
|
||||
} g /* refs to global Java state */;
|
||||
/**
|
||||
The list of bound auto-extensions (Java-side:
|
||||
org.sqlite.jni.auto_extension objects).
|
||||
} g;
|
||||
/*
|
||||
** The list of Java-side auto-extensions
|
||||
** (org.sqlite.jni.AutoExtensionCallback objects).
|
||||
*/
|
||||
struct {
|
||||
S3JniAutoExtension *aExt /* The auto-extension list. It is
|
||||
@ -661,7 +660,8 @@ struct S3JniGlobalType {
|
||||
volatile unsigned nValue;
|
||||
volatile unsigned nInverse;
|
||||
} udf;
|
||||
unsigned nMetrics /* Total number of mutex-locked metrics increments. */;
|
||||
unsigned nMetrics /* Total number of mutex-locked
|
||||
metrics increments. */;
|
||||
#if S3JNI_METRICS_MUTEX
|
||||
sqlite3_mutex * mutex;
|
||||
#endif
|
||||
@ -1124,11 +1124,37 @@ static void S3JniHook_localdup( JNIEnv * const env, S3JniHook const * const src,
|
||||
S3JniMutex_S3JniDb_leave;
|
||||
}
|
||||
|
||||
/*
|
||||
** Clears all of s's state. Requires that that the caller has locked
|
||||
** S3JniGlobal.perDb.mutex. Make sure to do anything needed with
|
||||
** s->pNext and s->pPrev before calling this, as this clears them.
|
||||
*/
|
||||
static void S3JniDb_clear(JNIEnv * const env, S3JniDb * const s){
|
||||
S3JniMutex_S3JniDb_assertLocker;
|
||||
sqlite3_free( s->zMainDbName );
|
||||
#define UNHOOK(MEMBER) S3JniHook_unref(env, &s->hooks.MEMBER, 0)
|
||||
UNHOOK(auth);
|
||||
UNHOOK(busyHandler);
|
||||
UNHOOK(collation);
|
||||
UNHOOK(collationNeeded);
|
||||
UNHOOK(commit);
|
||||
UNHOOK(progress);
|
||||
UNHOOK(rollback);
|
||||
UNHOOK(trace);
|
||||
UNHOOK(update);
|
||||
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
|
||||
UNHOOK(preUpdate);
|
||||
#endif
|
||||
#undef UNHOOK
|
||||
S3JniUnrefGlobal(s->jDb);
|
||||
memset(s, 0, sizeof(S3JniDb));
|
||||
}
|
||||
|
||||
/*
|
||||
** Clears s's state and moves it to the free-list. Requires that that the
|
||||
** caller has locked S3JniGlobal.perDb.mutex.
|
||||
*/
|
||||
static void S3JniDb_set_aside(JNIEnv * env, S3JniDb * const s){
|
||||
static void S3JniDb_set_aside(JNIEnv * const env, S3JniDb * const s){
|
||||
if( s ){
|
||||
S3JniMutex_S3JniDb_enter;
|
||||
assert(s->pPrev != s);
|
||||
@ -1140,23 +1166,7 @@ static void S3JniDb_set_aside(JNIEnv * env, S3JniDb * const s){
|
||||
assert(!s->pPrev);
|
||||
SJG.perDb.aHead = s->pNext;
|
||||
}
|
||||
sqlite3_free( s->zMainDbName );
|
||||
#define UNHOOK(MEMBER) S3JniHook_unref(env, &s->hooks.MEMBER, 0)
|
||||
UNHOOK(auth);
|
||||
UNHOOK(busyHandler);
|
||||
UNHOOK(collation);
|
||||
UNHOOK(collationNeeded);
|
||||
UNHOOK(commit);
|
||||
UNHOOK(progress);
|
||||
UNHOOK(rollback);
|
||||
UNHOOK(trace);
|
||||
UNHOOK(update);
|
||||
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
|
||||
UNHOOK(preUpdate);
|
||||
#endif
|
||||
#undef UNHOOK
|
||||
S3JniUnrefGlobal(s->jDb);
|
||||
memset(s, 0, sizeof(S3JniDb));
|
||||
S3JniDb_clear(env, s);
|
||||
s->pNext = SJG.perDb.aFree;
|
||||
if(s->pNext) s->pNext->pPrev = s;
|
||||
SJG.perDb.aFree = s;
|
||||
@ -1169,6 +1179,8 @@ static void S3JniDb_set_aside(JNIEnv * env, S3JniDb * const s){
|
||||
** references the cache owns. Returns true if env was cached and false
|
||||
** if it was not found in the cache. Ownership of the given object is
|
||||
** passed over to this function, which makes it free for re-use.
|
||||
**
|
||||
** Requires that the Env mutex be locked.
|
||||
*/
|
||||
static int S3JniEnv_uncache(JNIEnv * const env){
|
||||
struct S3JniEnv * row;
|
||||
@ -1688,23 +1700,33 @@ static S3JniUdf * S3JniUdf_alloc(JNIEnv * const env, jobject jObj){
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
static void S3JniUdf_free(S3JniUdf * s){
|
||||
S3JniDeclLocal_env;
|
||||
|
||||
/*
|
||||
** Frees up all resources owned by s, clears its state, then either
|
||||
** caches it for reuse (if cacheIt is true) or frees it. The former
|
||||
** requires locking the global mutex, so it must not be held when this
|
||||
** is called.
|
||||
*/
|
||||
static void S3JniUdf_free(JNIEnv * const env, S3JniUdf * const s,
|
||||
int cacheIt){
|
||||
assert( !s->pNext );
|
||||
s3jni_call_xDestroy(env, s->jObj);
|
||||
S3JniUnrefGlobal(s->jObj);
|
||||
sqlite3_free(s->zFuncName);
|
||||
assert( !s->pNext );
|
||||
memset(s, 0, sizeof(*s));
|
||||
S3JniMutex_Global_enter;
|
||||
s->pNext = S3JniGlobal.udf.aFree;
|
||||
S3JniGlobal.udf.aFree = s;
|
||||
S3JniMutex_Global_leave;
|
||||
if( cacheIt ){
|
||||
S3JniMutex_Global_enter;
|
||||
s->pNext = S3JniGlobal.udf.aFree;
|
||||
S3JniGlobal.udf.aFree = s;
|
||||
S3JniMutex_Global_leave;
|
||||
}else{
|
||||
sqlite3_free( s );
|
||||
}
|
||||
}
|
||||
|
||||
/* Finalizer for sqlite3_create_function() and friends. */
|
||||
static void S3JniUdf_finalizer(void * s){
|
||||
S3JniUdf_free((S3JniUdf*)s);
|
||||
S3JniUdf_free(s3jni_env(), (S3JniUdf*)s, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1712,14 +1734,14 @@ static void S3JniUdf_finalizer(void * s){
|
||||
** (sqlite3_context*,int,sqlite3_value**).
|
||||
*/
|
||||
typedef struct {
|
||||
jobject jcx /* sqlite3_context object */;
|
||||
jobject jcx /* sqlite3_context */;
|
||||
jobjectArray jargv /* sqlite3_value[] */;
|
||||
} udf_jargs;
|
||||
|
||||
/**
|
||||
Converts the given (cx, argc, argv) into arguments for the given
|
||||
UDF, placing the result in the final argument. Returns 0 on
|
||||
success, SQLITE_NOMEM on allocation error.
|
||||
/*
|
||||
** Converts the given (cx, argc, argv) into arguments for the given
|
||||
** UDF, placing the result in the final argument. Returns 0 on
|
||||
** success, SQLITE_NOMEM on allocation error.
|
||||
*/
|
||||
static int udf_args(JNIEnv *env,
|
||||
sqlite3_context * const cx,
|
||||
@ -1728,19 +1750,19 @@ static int udf_args(JNIEnv *env,
|
||||
jobjectArray ja = 0;
|
||||
jobject jcx = new_sqlite3_context_wrapper(env, cx);
|
||||
jint i;
|
||||
S3JniNphClass * const pNC =
|
||||
S3JniGlobal_nph(env, &S3NphRefs.sqlite3_value);
|
||||
*jCx = 0;
|
||||
*jArgv = 0;
|
||||
if( !jcx ) goto error_oom;
|
||||
ja = (*env)->NewObjectArray(env, argc,
|
||||
SJG.g.cObj,
|
||||
NULL);
|
||||
ja = (*env)->NewObjectArray(env, argc, pNC->klazz, NULL);
|
||||
s3jni_oom_check( ja );
|
||||
if( !ja ) goto error_oom;
|
||||
for(i = 0; i < argc; ++i){
|
||||
jobject jsv = new_sqlite3_value_wrapper(env, argv[i]);
|
||||
if( !jsv ) goto error_oom;
|
||||
(*env)->SetObjectArrayElement(env, ja, i, jsv);
|
||||
S3JniUnrefLocal(jsv)/*array has a ref*/;
|
||||
S3JniUnrefLocal(jsv)/*ja has a ref*/;
|
||||
}
|
||||
*jCx = jcx;
|
||||
*jArgv = ja;
|
||||
@ -2018,8 +2040,8 @@ static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr,
|
||||
}
|
||||
jc->pdbOpening = 0;
|
||||
assert( !ps->pDb && "it's still being opened" );
|
||||
ps->pDb = pDb;
|
||||
assert( ps->jDb );
|
||||
ps->pDb = pDb;
|
||||
NativePointerHolder_set(env, &S3NphRefs.sqlite3, ps->jDb, pDb)
|
||||
/* As of here, the Java/C connection is complete */;
|
||||
for( i = 0; go && 0==rc; ++i ){
|
||||
@ -2057,7 +2079,6 @@ static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr,
|
||||
S3JniApi(sqlite3_auto_extension(),jint,1auto_1extension)(
|
||||
JniArgsEnvClass, jobject jAutoExt
|
||||
){
|
||||
static int once = 0;
|
||||
int i;
|
||||
S3JniAutoExtension * ax;
|
||||
int rc = 0;
|
||||
@ -2065,16 +2086,18 @@ S3JniApi(sqlite3_auto_extension(),jint,1auto_1extension)(
|
||||
if( !jAutoExt ) return SQLITE_MISUSE;
|
||||
S3JniMutex_Ext_enter;
|
||||
for( i = 0; i < SJG.autoExt.nExt; ++i ){
|
||||
/* Look for match or first empty slot. */
|
||||
/* Look for a match. */
|
||||
ax = &SJG.autoExt.aExt[i];
|
||||
if( ax->jObj && (*env)->IsSameObject(env, ax->jObj, jAutoExt) ){
|
||||
/* same object, so this is a no-op. */
|
||||
S3JniMutex_Ext_leave;
|
||||
return 0 /* this as a no-op. */;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if( i == SJG.autoExt.nExt ){
|
||||
assert( SJG.autoExt.nExt <= SJG.autoExt.nAlloc );
|
||||
if( SJG.autoExt.nExt == SJG.autoExt.nAlloc ){
|
||||
/* Allocate another slot. */
|
||||
unsigned n = 1 + SJG.autoExt.nAlloc;
|
||||
S3JniAutoExtension * const aNew =
|
||||
s3jni_realloc( SJG.autoExt.aExt, n * sizeof(*ax) );
|
||||
@ -2093,8 +2116,14 @@ S3JniApi(sqlite3_auto_extension(),jint,1auto_1extension)(
|
||||
}
|
||||
}
|
||||
if( 0==rc ){
|
||||
static int once = 0;
|
||||
if( 0==once && ++once ){
|
||||
rc = sqlite3_auto_extension( (void(*)(void))s3jni_run_java_auto_extensions );
|
||||
rc = sqlite3_auto_extension(
|
||||
(void(*)(void))s3jni_run_java_auto_extensions
|
||||
/* Reminder: the JNI binding of sqlite3_reset_auto_extension()
|
||||
** does not call the core-lib impl. It only clears Java-side
|
||||
** auto-extensions. */
|
||||
);
|
||||
if( rc ){
|
||||
assert( ax );
|
||||
S3JniAutoExtension_clear(ax);
|
||||
@ -2720,13 +2749,13 @@ S3JniApi(sqlite3_create_function() sqlite3_create_function_v2() sqlite3_create_w
|
||||
if( UDF_UNKNOWN_TYPE==s->type ){
|
||||
rc = s3jni_db_error(pDb, SQLITE_MISUSE,
|
||||
"Cannot unambiguously determine function type.");
|
||||
S3JniUdf_free(s);
|
||||
S3JniUdf_free(env, s, 1);
|
||||
goto error_cleanup;
|
||||
}
|
||||
zFuncName = s3jni_jstring_to_utf8(env,jFuncName,0);
|
||||
if( !zFuncName ){
|
||||
rc = SQLITE_NOMEM;
|
||||
S3JniUdf_free(s);
|
||||
S3JniUdf_free(env, s, 1);
|
||||
goto error_cleanup;
|
||||
}
|
||||
if( UDF_WINDOW == s->type ){
|
||||
@ -3815,16 +3844,38 @@ S3JniApi(sqlite3_shutdown(),jint,1shutdown)(
|
||||
JniArgsEnvClass
|
||||
){
|
||||
s3jni_reset_auto_extension(env);
|
||||
/* Free up env cache. */
|
||||
S3JniMutex_Env_enter;
|
||||
while( SJG.envCache.aHead ){
|
||||
S3JniEnv_uncache( SJG.envCache.aHead->env );
|
||||
}
|
||||
S3JniMutex_Env_leave;
|
||||
/* Free up S3JniUdf recycling bin. */
|
||||
S3JniMutex_Global_enter;
|
||||
while( S3JniGlobal.udf.aFree ){
|
||||
S3JniUdf * const u = S3JniGlobal.udf.aFree;
|
||||
S3JniGlobal.udf.aFree = u->pNext;
|
||||
u->pNext = 0;
|
||||
S3JniUdf_free(env, u, 0);
|
||||
}
|
||||
S3JniMutex_Global_leave;
|
||||
/* Free up S3JniDb recycling bin. */
|
||||
S3JniMutex_S3JniDb_enter;
|
||||
while( S3JniGlobal.perDb.aFree ){
|
||||
S3JniDb * const d = S3JniGlobal.perDb.aFree;
|
||||
S3JniGlobal.perDb.aFree = d->pNext;
|
||||
d->pNext = 0;
|
||||
S3JniDb_clear(env, d);
|
||||
sqlite3_free(d);
|
||||
}
|
||||
S3JniMutex_S3JniDb_leave;
|
||||
|
||||
#if 0
|
||||
/*
|
||||
** Is this a good idea? We will get rid of the perDb list once
|
||||
** sqlite3 gets a per-db client state, at which point we won't have
|
||||
** a central list of databases to close.
|
||||
** Is automatically closing any still-open dbs a good idea? We will
|
||||
** get rid of the perDb list once sqlite3 gets a per-db client
|
||||
** state, at which point we won't have a central list of databases
|
||||
** to close.
|
||||
*/
|
||||
S3JniMutex_S3JniDb_enter;
|
||||
while( SJG.perDb.pHead ){
|
||||
@ -3832,6 +3883,7 @@ S3JniApi(sqlite3_shutdown(),jint,1shutdown)(
|
||||
}
|
||||
S3JniMutex_S3JniDb_leave;
|
||||
#endif
|
||||
|
||||
/* Do not clear S3JniGlobal.jvm: it's legal to restart the lib. */
|
||||
return sqlite3_shutdown();
|
||||
}
|
||||
@ -3857,7 +3909,6 @@ S3JniApi(sqlite3_sql(),jstring,1sql)(
|
||||
const char * zSql = 0;
|
||||
zSql = sqlite3_sql(pStmt);
|
||||
rv = s3jni_utf8_to_jstring(env, zSql, -1);
|
||||
s3jni_oom_check(rv);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
@ -4972,8 +5023,6 @@ Java_org_sqlite_jni_SQLite3Jni_init(JniArgsEnvClass){
|
||||
}
|
||||
|
||||
/* Grab references to various global classes and objects... */
|
||||
SJG.g.cObj = S3JniRefGlobal((*env)->FindClass(env,"java/lang/Object"));
|
||||
S3JniExceptionIsFatal("Error getting reference to Object class.");
|
||||
|
||||
SJG.g.cLong = S3JniRefGlobal((*env)->FindClass(env,"java/lang/Long"));
|
||||
S3JniExceptionIsFatal("Error getting reference to Long class.");
|
||||
|
@ -1149,9 +1149,10 @@ public final class SQLite3Jni {
|
||||
|
||||
|
||||
/**
|
||||
Cleans up all stale per-thread state managed by the library, as
|
||||
well as any registered auto-extensions, then calls the C-native
|
||||
sqlite3_shutdown(). Calling this while database handles or
|
||||
In addition to calling the C-level sqlite3_shutdown(), the JNI
|
||||
binding also cleans up all stale per-thread state managed by the
|
||||
library, as well as any registered auto-extensions and free up
|
||||
various bits of memory. Calling this while database handles or
|
||||
prepared statements are still active will leak resources. Trying
|
||||
to use those objects after this routine is called invoked
|
||||
undefined behavior.
|
||||
|
14
manifest
14
manifest
@ -1,5 +1,5 @@
|
||||
C Factor\sout\sa\ssuperfluous\sJNI\sclass.\sDoc\sand\scode\sstyle\scleanups.
|
||||
D 2023-08-27T08:10:59.533
|
||||
C Factor\sout\san\sunnecessary\sstruct\smember.\sJNI\ssqlite3_shutdown()\snow\sfrees\sup\sthe\svarious\sobject-recycling\sbins.\sDoc\stouchups.
|
||||
D 2023-08-27T09:12:50.224
|
||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
||||
@ -236,7 +236,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3
|
||||
F ext/jni/GNUmakefile 05a756bb7a579b7d6570cb590567e9f0d12270529a2e7e50523284e5a3684838
|
||||
F ext/jni/README.md 1332b1fa27918bd5d9ca2d0d4f3ac3a6ab86b9e3699dc5bfe32904a027f3d2a9
|
||||
F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa
|
||||
F ext/jni/src/c/sqlite3-jni.c 9a07c95d570c1a46fff1db5383d0d247ef946b8c010a8e392045e350bf22e28f
|
||||
F ext/jni/src/c/sqlite3-jni.c b0b86b214477ee1604caf358fd1f232ee6649d327527063dddc22216026c20d2
|
||||
F ext/jni/src/c/sqlite3-jni.h a410d05ca47a676b75ff7b8980e75ad604ea15f3c29965f88989703abc2eeaf6
|
||||
F ext/jni/src/org/sqlite/jni/AggregateFunction.java 0a5a74bea5ee12a99407e9432d0ca393525af912c2b0ca55c7ee5dbd019c00ef
|
||||
F ext/jni/src/org/sqlite/jni/AuthorizerCallback.java c374bb76409cce7a0bdba94877706b59ac6127fa5d9e6af3e8058c99ce99c030
|
||||
@ -262,7 +262,7 @@ F ext/jni/src/org/sqlite/jni/ResultCode.java ba701f20213a5f259e94cfbfdd36eb7ac7c
|
||||
F ext/jni/src/org/sqlite/jni/RollbackHookCallback.java be7f7a26d1102fb514d835e11198d51302af8053d97188bfb2e34c2133208568
|
||||
F ext/jni/src/org/sqlite/jni/SQLFunction.java d060f302b2cc4cf7a4f5a6b2d36458a2e6fc9648374b5d09c36a43665af41207
|
||||
F ext/jni/src/org/sqlite/jni/SQLite3CallbackProxy.java 13c4ea6f35871261eba63fa4117715515e0beecbdebfb879ec5b1f340ed36904
|
||||
F ext/jni/src/org/sqlite/jni/SQLite3Jni.java d640493f52c04e03f9c0525f16dcec6b718638ace22101c0ccfc014883a15b0b
|
||||
F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 1ae75149383cd8b9fde175aa87855d18e425e32af2bc5e81bf56a95e92155195
|
||||
F ext/jni/src/org/sqlite/jni/ScalarFunction.java 21301a947e49f0dd9c682dfe2cc8a6518226c837253dd791cd512f847eeca52c
|
||||
F ext/jni/src/org/sqlite/jni/Tester1.java 37b46dc15ac8fbeb916dcf1f7771023d2be025d05422d725d5891935eda506ac
|
||||
F ext/jni/src/org/sqlite/jni/TesterFts5.java 6f135c60e24c89e8eecb9fe61dde0f3bb2906de668ca6c9186bcf34bdaf94629
|
||||
@ -2103,8 +2103,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||
P deed5797de65a25896e991a441f0d05eb92662536296485920fb081e84ad5d32
|
||||
R 1286cf04d67bcef64c621ffe2a87fc44
|
||||
P 0f37f27148dfa93ecc42381ad3455a9059285d1af2df027429044942dc4d861b
|
||||
R e62c13b2db81e23df4398b126d6aa9e9
|
||||
U stephan
|
||||
Z 113b1efbfce3141a69775192a9b6c79b
|
||||
Z aca6efed8b8717dbec6b53005671b2cd
|
||||
# Remove this line to create a well-formed Fossil manifest.
|
||||
|
@ -1 +1 @@
|
||||
0f37f27148dfa93ecc42381ad3455a9059285d1af2df027429044942dc4d861b
|
||||
bae4d022aad9bbeb78cb027ecad799af87afe331e697add44ec22297c873141d
|
Loading…
Reference in New Issue
Block a user