diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index d54cc1ca9c..a9e1552cf8 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -294,43 +294,66 @@ static inline void s3jni_unref_local(JNIEnv * const env, jobject const v){ #define S3JniUnrefLocal(VAR) s3jni_unref_local(env, (VAR)) /* -** Lookup key type for use with s3jni_nph(). +** Lookup key type for use with s3jni_nphop() and a cache of a +** frequently-needed Java-side class reference and one or two Java +** class member IDs. */ -typedef struct S3JniNphRef S3JniNphRef; -struct S3JniNphRef { +typedef struct S3JniNphOp S3JniNphOp; +struct S3JniNphOp { const int index /* index into S3JniGlobal.nph[] */; const char * const zName /* Full Java name of the class */; const char * const zMember /* Name of member property */; const char * const zTypeSig /* JNI type signature of zMember */; + /* + ** klazz is a global ref to the class represented by pRef. + ** + ** According to: + ** + ** https://developer.ibm.com/articles/j-jni/ + ** + ** > ... the IDs returned for a given class don't change for the + ** lifetime of the JVM process. But the call to get the field or + ** method can require significant work in the JVM, because fields + ** and methods might have been inherited from superclasses, making + ** the JVM walk up the class hierarchy to find them. Because the + ** IDs are the same for a given class, you should look them up + ** once and then reuse them. Similarly, looking up class objects + ** can be expensive, so they should be cached as well. + */ + jclass klazz; + volatile jfieldID fidValue /* NativePointerHolder.nativePointer or + ** OutputPointer.T.value */; + volatile jmethodID midCtor /* klazz's no-arg constructor. Used by + ** NativePointerHolder_new(). */; }; /* ** Cache keys for each concrete NativePointerHolder subclasses and -** OutputPointer.T types. The members are to be used with s3jni_nph() +** OutputPointer.T types. The members are to be used with s3jni_nphop() ** and friends, and each one's member->index corresponds to its index ** in the S3JniGlobal.nph[] array. */ static const struct { - const S3JniNphRef sqlite3; - const S3JniNphRef sqlite3_stmt; - const S3JniNphRef sqlite3_context; - const S3JniNphRef sqlite3_value; - const S3JniNphRef OutputPointer_Bool; - const S3JniNphRef OutputPointer_Int32; - const S3JniNphRef OutputPointer_Int64; - const S3JniNphRef OutputPointer_sqlite3; - const S3JniNphRef OutputPointer_sqlite3_stmt; - const S3JniNphRef OutputPointer_sqlite3_value; - const S3JniNphRef OutputPointer_String; + const S3JniNphOp sqlite3; + const S3JniNphOp sqlite3_stmt; + const S3JniNphOp sqlite3_context; + const S3JniNphOp sqlite3_value; + const S3JniNphOp OutputPointer_Bool; + const S3JniNphOp OutputPointer_Int32; + const S3JniNphOp OutputPointer_Int64; + const S3JniNphOp OutputPointer_sqlite3; + const S3JniNphOp OutputPointer_sqlite3_stmt; + const S3JniNphOp OutputPointer_sqlite3_value; + const S3JniNphOp OutputPointer_String; #ifdef SQLITE_ENABLE_FTS5 - const S3JniNphRef OutputPointer_ByteArray; - const S3JniNphRef Fts5Context; - const S3JniNphRef Fts5ExtensionApi; - const S3JniNphRef fts5_api; - const S3JniNphRef fts5_tokenizer; - const S3JniNphRef Fts5Tokenizer; + const S3JniNphOp OutputPointer_ByteArray; + const S3JniNphOp Fts5Context; + const S3JniNphOp Fts5ExtensionApi; + const S3JniNphOp fts5_api; + const S3JniNphOp fts5_tokenizer; + const S3JniNphOp Fts5Tokenizer; #endif -} S3JniNphRefs = { +} S3JniNphOps = { #define MkRef(INDEX, KLAZZ, MEMBER, SIG) \ { INDEX, "org/sqlite/jni/" KLAZZ, MEMBER, SIG } /* NativePointerHolder ref */ @@ -364,51 +387,17 @@ static const struct { #undef RefO }; -#define S3JniNph(T) &S3JniNphRefs.T +#define S3JniNph(T) &S3JniNphOps.T enum { /* ** Size of the NativePointerHolder cache. Need enough space for ** (only) the library's NativePointerHolder and OutputPointer types, ** a fixed count known at build-time. This value needs to be - ** exactly the number of S3JniNphRef entries in the S3JniNphRefs + ** exactly the number of S3JniNphOp entries in the S3JniNphOps ** object. */ - S3Jni_NphCache_size = sizeof(S3JniNphRefs) / sizeof(S3JniNphRef) -}; - -/* -** Cache entry for NativePointerHolder subclasses and OutputPointer -** types. The pRef and klazz fields are set up the first time the -** entry is fetched using s3jni_nph(). The other fields are -** populated as needed by the routines which use them. -*/ -typedef struct S3JniNphClass S3JniNphClass; -struct S3JniNphClass { - volatile const S3JniNphRef * pRef /* Entry from S3JniNphRefs */; - /* - ** klazz is a global ref to the class represented by pRef. - ** - ** According to: - ** - ** https://developer.ibm.com/articles/j-jni/ - ** - ** > ... the IDs returned for a given class don't change for the - ** lifetime of the JVM process. But the call to get the field or - ** method can require significant work in the JVM, because fields - ** and methods might have been inherited from superclasses, making - ** the JVM walk up the class hierarchy to find them. Because the - ** IDs are the same for a given class, you should look them up - ** once and then reuse them. Similarly, looking up class objects - ** can be expensive, so they should be cached as well. - */ - jclass klazz; - volatile jmethodID midCtor /* klazz's no-arg constructor. Used by - ** NativePointerHolder_new(). */; - volatile jfieldID fidValue /* NativePointerHolder.nativePointer or - ** OutputPointer.T.value */; - volatile jfieldID fidAggCtx /* sqlite3_context.aggregateContext, used only - ** by the sqlite3_context binding. */; + S3Jni_NphCache_size = sizeof(S3JniNphOps) / sizeof(S3JniNphOp) }; /* @@ -420,7 +409,9 @@ struct S3JniHook{ jmethodID midCallback /* callback method. Signature depends on ** jObj's type */; /* We lookup the jObj.xDestroy() method as-needed for contexts which - ** have custom finalizers. */ + ** support custom finalizers. Fundamentally we can support them for + ** any Java type, but we only want to expose support for them where + ** the C API does. */ jobject jExtra /* Global ref to a per-hook-type value */; int doXDestroy /* If true then S3JniHook_unref() will call jObj->xDestroy() if it's available. */; @@ -590,10 +581,10 @@ struct S3JniGlobalType { ** NativePointerHolder subclasses and OutputPointer.T types. */ struct { - S3JniNphClass list[S3Jni_NphCache_size]; + S3JniNphOp list[S3Jni_NphCache_size]; sqlite3_mutex * mutex; /* mutex for this->list */ - void const * locker; /* sanity-checking-only context object - for this->mutex */ + volatile void const * locker; /* sanity-checking-only context object + for this->mutex */ } nph; /* ** Cache of per-thread state. @@ -602,8 +593,9 @@ struct S3JniGlobalType { S3JniEnv * aHead /* Linked list of in-use instances */; S3JniEnv * aFree /* Linked list of free instances */; sqlite3_mutex * mutex /* mutex for aHead and aFree. */; - void const * locker /* env mutex is held on this object's behalf. - Used only for sanity checking. */; + volatile void const * locker /* env mutex is held on this + object's behalf. Used only for + sanity checking. */; } envCache; /* ** Per-db state. This can move into the core library once we can tie @@ -612,8 +604,10 @@ struct S3JniGlobalType { struct { S3JniDb * aFree /* Linked list of free instances */; sqlite3_mutex * mutex /* mutex for aHead and aFree */; - void const * locker /* perDb mutex is held on this object's - behalf. Used only for sanity checking. */; + volatile void const * locker + /* perDb mutex is held on this object's behalf. Used only for + sanity checking. Note that the mutex is at the class level, not + instance level. */; } perDb; struct { S3JniUdf * aFree /* Head of the free-item list. Guarded by global @@ -646,8 +640,9 @@ struct S3JniGlobalType { int nExt /* number of active entries in aExt, all in the first nExt'th array elements. */; sqlite3_mutex * mutex /* mutex for manipulation/traversal of aExt */; - const void * locker /* object on whose behalf the mutex is held. - Only for sanity checking in debug builds. */; + volatile const void * locker /* object on whose behalf the mutex + is held. Only for sanity checking + in debug builds. */; } autoExt; #ifdef SQLITE_ENABLE_FTS5 struct { @@ -1316,23 +1311,20 @@ static int S3JniEnv_uncache(JNIEnv * const env){ ** This simple cache catches >99% of searches in the current ** (2023-07-31) tests. */ -static S3JniNphClass * s3jni__nph(JNIEnv * const env, S3JniNphRef const* pRef){ - S3JniNphClass * const pNC = &SJG.nph.list[pRef->index]; +static S3JniNphOp * s3jni__nphop(JNIEnv * const env, S3JniNphOp const* pRef){ + S3JniNphOp * const pNC = &SJG.nph.list[pRef->index]; - assert( (void*)pRef>=(void*)&S3JniNphRefs && (void*)pRef<(void*)(&S3JniNphRefs + 1) + assert( (void*)pRef>=(void*)&S3JniNphOps && (void*)pRef<(void*)(&S3JniNphOps + 1) && "pRef is out of range" ); assert( pRef->index>=0 - && (pRef->index < (sizeof(S3JniNphRefs) / sizeof(S3JniNphRef))) + && (pRef->index < (sizeof(S3JniNphOps) / sizeof(S3JniNphOp))) && "pRef->index is out of range" ); - if( !pNC->pRef ){ + if( !pNC->klazz ){ S3JniNph_mutex_enter; - if( !pNC->pRef ){ + if( !pNC->klazz ){ jclass const klazz = (*env)->FindClass(env, pRef->zName); S3JniExceptionIsFatal("FindClass() unexpectedly threw"); pNC->klazz = S3JniRefGlobal(klazz); - pNC->pRef = pRef - /* Must come last to avoid a race condition where pNC->klass - can be NULL after this function returns. */; } S3JniNph_mutex_leave; } @@ -1340,11 +1332,11 @@ static S3JniNphClass * s3jni__nph(JNIEnv * const env, S3JniNphRef const* pRef){ return pNC; } -#define s3jni_nph(PRef) s3jni__nph(env, PRef) +#define s3jni_nphop(PRef) s3jni__nphop(env, PRef) /* ** Common code for accessor functions for NativePointerHolder and -** OutputPointer types. pRef must be a pointer from S3JniNphRefs. jOut +** OutputPointer types. pRef must be a pointer from S3JniNphOps. jOut ** must be an instance of that class (Java's type safety takes care of ** that requirement). If necessary, this fetches the jfieldID for ** jOut's pRef->zMember, which must be of the type represented by the @@ -1354,8 +1346,8 @@ static S3JniNphClass * s3jni__nph(JNIEnv * const env, S3JniNphRef const* pRef){ ** ** Property lookups are cached on a per-pRef basis. */ -static jfieldID s3jni_nphop_field(JNIEnv * const env, S3JniNphRef const* pRef){ - S3JniNphClass * const pNC = s3jni_nph(pRef); +static jfieldID s3jni_nphop_field(JNIEnv * const env, S3JniNphOp const* pRef){ + S3JniNphOp * const pNC = s3jni_nphop(pRef); if( !pNC->fidValue ){ S3JniNph_mutex_enter; @@ -1363,7 +1355,7 @@ static jfieldID s3jni_nphop_field(JNIEnv * const env, S3JniNphRef const* pRef){ pNC->fidValue = (*env)->GetFieldID(env, pNC->klazz, pRef->zMember, pRef->zTypeSig); S3JniExceptionIsFatal("Code maintenance required: missing " - "required S3JniNphClass::fidValue."); + "required S3JniNphOp::fidValue."); } S3JniNph_mutex_leave; } @@ -1376,7 +1368,7 @@ static jfieldID s3jni_nphop_field(JNIEnv * const env, S3JniNphRef const* pRef){ ** zClassName must be a static string so we can use its address ** as a cache key. */ -static void NativePointerHolder__set(JNIEnv * const env, S3JniNphRef const* pRef, +static void NativePointerHolder__set(JNIEnv * const env, S3JniNphOp const* pRef, jobject ppOut, const void * p){ assert( ppOut ); (*env)->SetLongField(env, ppOut, s3jni_nphop_field(env, pRef), (jlong)p); @@ -1392,7 +1384,7 @@ static void NativePointerHolder__set(JNIEnv * const env, S3JniNphRef const* pRef ** no-op if pObj is NULL. */ static void * NativePointerHolder__get(JNIEnv * env, jobject pObj, - S3JniNphRef const* pRef){ + S3JniNphOp const* pRef){ void * rv = 0; if( pObj ){ rv = (void*)(*env)->GetLongField(env, pObj, s3jni_nphop_field(env, pRef)); @@ -1570,7 +1562,7 @@ static void OutputPointer_set_Int64(JNIEnv * const env, jobject const jOut, ** Object type. */ static void OutputPointer_set_obj(JNIEnv * const env, - S3JniNphRef const * const pRef, + S3JniNphOp const * const pRef, jobject const jOut, jobject v){ (*env)->SetObjectField(env, jOut, s3jni_nphop_field(env, pRef), v); @@ -1666,14 +1658,14 @@ static void ResultJavaValue_finalizer(void *v){ ** if Java fails to allocate, but the JNI docs are not entirely clear ** on that detail. ** -** Always use a static pointer from the S3JniNphRefs struct for the +** Always use a static pointer from the S3JniNphOps struct for the ** 2nd argument. */ static jobject NativePointerHolder_new(JNIEnv * const env, - S3JniNphRef const * pRef, + S3JniNphOp const * pRef, const void * pNative){ jobject rv = 0; - S3JniNphClass * const pNC = s3jni_nph(pRef); + S3JniNphOp * const pNC = s3jni_nphop(pRef); if( !pNC->midCtor ){ S3JniNph_mutex_enter; if( !pNC->midCtor ){ @@ -1826,7 +1818,7 @@ static int udf_args(JNIEnv *env, *jArgv = 0; if( !jcx ) goto error_oom; ja = (*env)->NewObjectArray( - env, argc, s3jni_nph(S3JniNph(sqlite3_value))->klazz, + env, argc, s3jni_nphop(S3JniNph(sqlite3_value))->klazz, NULL); s3jni_oom_check( ja ); if( !ja ) goto error_oom; @@ -2113,12 +2105,12 @@ static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr, if( 0==SJG.autoExt.nExt ) return 0; env = s3jni_env(); jc = S3JniEnv_get(); - S3JniAutoExt_mutex_enter; + S3JniDb_mutex_enter; ps = jc->pdbOpening ? jc->pdbOpening : S3JniDb_from_c(pDb); if( !ps ){ *pzErr = sqlite3_mprintf("Unexpected arrival of null S3JniDb in " "auto-extension runner."); - S3JniAutoExt_mutex_leave; + S3JniDb_mutex_leave; return SQLITE_ERROR; } assert( ps->jDb ); @@ -2128,7 +2120,7 @@ static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr, ps, 0/* we'll re-set this after open() completes. */); if( rc ){ - S3JniAutoExt_mutex_leave; + S3JniDb_mutex_leave; return rc; } } @@ -2136,7 +2128,7 @@ static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr, assert( ps == jc->pdbOpening ); jc->pdbOpening = 0; } - S3JniAutoExt_mutex_leave; + S3JniDb_mutex_leave; NativePointerHolder_set(S3JniNph(sqlite3), ps->jDb, pDb) /* As of here, the Java/C connection is complete except for the (temporary) lack of finalizer for the ps object. */; @@ -4442,10 +4434,12 @@ JniDecl(void,1jni_1internal_1details)(JniArgsEnvClass){ SO(S3JniEnv); SO(S3JniHook); SO(S3JniDb); - SO(S3JniNphRefs); + SO(S3JniNphOps); printf("\t(^^^ %u NativePointerHolder/OutputPointer.T types)\n", (unsigned)S3Jni_NphCache_size); SO(S3JniGlobal); + SO(S3JniGlobal.nph); + SO(S3JniGlobal.metrics); SO(S3JniAutoExtension); SO(S3JniUdf); #undef SO @@ -4457,7 +4451,7 @@ JniDecl(void,1jni_1internal_1details)(JniArgsEnvClass){ printf("Mutex entry:" "\n\tglobal = %u" "\n\tenv = %u" - "\n\tnph = %u for S3JniNphClass init" + "\n\tnph = %u for S3JniNphOp init" "\n\tperDb = %u" "\n\tautoExt list = %u" "\n\tS3JniUdf = %u (free-list)" @@ -5034,7 +5028,7 @@ static int s3jni_xTokenize_xToken(void *p, int tFlags, const char* z, ** Proxy for Fts5ExtensionApi.xTokenize() and ** fts5_tokenizer.xTokenize() */ -static jint s3jni_fts5_xTokenize(JniArgsEnvObj, S3JniNphRef const *pRef, +static jint s3jni_fts5_xTokenize(JniArgsEnvObj, S3JniNphOp const *pRef, jint tokFlags, jobject jFcx, jbyteArray jbaText, jobject jCallback){ Fts5ExtDecl; diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index a0aaa82bbd..e396c94c3b 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -122,7 +122,7 @@ public class Tester1 implements Runnable { affirm(v, "Affirmation failed."); } - @ManualTest /* because testing this for threading is pointless */ + @SingleThreadOnly /* because it's thread-agnostic */ private void test1(){ affirm(sqlite3_libversion_number() == SQLITE_VERSION_NUMBER); } @@ -610,7 +610,7 @@ public class Tester1 implements Runnable { affirm( 1 == xDestroyCalled.value ); } - @ManualTest /* because threading is meaningless here */ + @SingleThreadOnly /* because it's thread-agnostic */ private void testToUtf8(){ /** https://docs.oracle.com/javase/8/docs/api/java/nio/charset/Charset.html @@ -978,8 +978,8 @@ public class Tester1 implements Runnable { affirm( 7 == counter.value ); } - @ManualTest /* because threads inherently break this test */ - private void testBusy(){ + @SingleThreadOnly /* because threads inherently break this test */ + private static void testBusy(){ final String dbName = "_busy-handler.db"; final OutputPointer.sqlite3 outDb = new OutputPointer.sqlite3(); final OutputPointer.sqlite3_stmt outStmt = new OutputPointer.sqlite3_stmt(); @@ -1482,7 +1482,7 @@ public class Tester1 implements Runnable { } - @ManualTest /* we really only want to run this test manually. */ + @ManualTest /* we really only want to run this test manually */ private void testSleep(){ out("Sleeping briefly... "); sqlite3_sleep(600); @@ -1525,8 +1525,6 @@ public class Tester1 implements Runnable { outln(); } } - testToUtf8(); - test1(); for(java.lang.reflect.Method m : mlist){ nap(); try{ @@ -1536,9 +1534,6 @@ public class Tester1 implements Runnable { throw e; } } - if( !fromThread ){ - testBusy(); - } synchronized( this.getClass() ){ ++nTestRuns; } diff --git a/manifest b/manifest index 4f091a5f46..32c1abd2c7 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Further\sJNI\scleanups. -D 2023-09-02T08:51:14.022 +C Remove\sa\ssuperfluous\slevel\sof\sindirection\sin\sthe\sJNI\sinternals. +D 2023-09-02T10:18:10.477 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -237,7 +237,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile dc6e78f9717470d262b4b3ec17c337834295f9df81717c1539da84106324fd1e F ext/jni/README.md 1332b1fa27918bd5d9ca2d0d4f3ac3a6ab86b9e3699dc5bfe32904a027f3d2a9 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c d98030bb7f502b42990eeb0f2bd103af0b1f1312ba82892949c5ced85262e036 +F ext/jni/src/c/sqlite3-jni.c 1ce08538c568ec47ccf9e88e8b6d0e5b8a874520238d809adbfc93feb9f54d27 F ext/jni/src/c/sqlite3-jni.h c22f0189254abe26fad3ba132b484785b19a1aa96d34d30d7d8c5ffe6a9b25d1 F ext/jni/src/org/sqlite/jni/AbstractCollationCallback.java 95e88ba04f4aac51ffec65693e878e234088b2f21b387f4e4285c8b72b33e436 F ext/jni/src/org/sqlite/jni/AggregateFunction.java 7312486bc65fecdb91753c0a4515799194e031f45edbe16a6373cea18f404dc4 @@ -264,7 +264,7 @@ F ext/jni/src/org/sqlite/jni/SQLFunction.java 544a875d33fd160467d82e2397ac33157b F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 98f8d62492e2d6693336dd42c12267ea7f21eefe219aa85b8dd399bd6b0732bd F ext/jni/src/org/sqlite/jni/ScalarFunction.java 6d387bb499fbe3bc13c53315335233dbf6a0c711e8fa7c521683219b041c614c F ext/jni/src/org/sqlite/jni/TableColumnMetadata.java 54511b4297fa28dcb3f49b24035e34ced10e3fd44fd0e458e784f4d6b0096dab -F ext/jni/src/org/sqlite/jni/Tester1.java 93e89c4f72065bf28625cf435c47ed2bd3d937fe5c2431e6e7c3407f09f3f9bc +F ext/jni/src/org/sqlite/jni/Tester1.java e62b0e855ef19a703dc53c1eb8395ff49f5dc4c9471627f5ba71b9b831a18b37 F ext/jni/src/org/sqlite/jni/TesterFts5.java 2b2d6f3cc9f508358c103b774aee296c0f3d8c2f387d6abae9b8b9055f62c800 F ext/jni/src/org/sqlite/jni/TraceV2Callback.java beb0b064c1a5f8bfe585a324ed39a4e33edbe379a3fc60f1401661620d3ca7c0 F ext/jni/src/org/sqlite/jni/UpdateHookCallback.java 8376f4a931f2d5612b295c003c9515ba933ee76d8f95610e89c339727376e36c @@ -2115,8 +2115,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 f9c1f9cad1ad22f689a4afa13d59bf9969ddaed6624cbc16cb1bf5d1fd0c8a5b -R 215167a378a1f1105562740ffd0f1842 +P 30e38173c3ece0c9f8e7a9710f46cb5e8e8ef101c04531318a7adb070242f5dd +R 5b531d67a390c107642befa040c21413 U stephan -Z 15dc34b47fe3ed21ed7cc58408f87312 +Z 11b0834dd4cedde9630a4a43d0bbc08f # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index bbd378b4fe..96a63aefce 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -30e38173c3ece0c9f8e7a9710f46cb5e8e8ef101c04531318a7adb070242f5dd \ No newline at end of file +8dca6f7660c15eacbda20da1c66c9ef1de36864f78750658226b1a7baf22b726 \ No newline at end of file