from the ClassNames struct. */;
jclass klazz /* global ref to concrete NativePointerHolder class */;
jmethodID midCtor /* klazz's constructor */;
- jmethodID midSet /* NativePointerHolder.setNativePointer() */;
- jmethodID midGet /* NativePointerHolder.getNativePointer() */;
+ jfieldID fidValue /* NativePointerHolder.nativePointer and OutputPointer.X.value */;
jfieldID fidSetAgg /* sqlite3_context::aggregateContext */;
};
for( i = 0; i < NphCache_SIZE; ++i ){
cacheLine = &envRow->nph[i];
if(zClassName == cacheLine->zClassName){
-#if 0
+#define DUMP_NPH_CACHES 0
+#if DUMP_NPH_CACHES
static unsigned int n = 0;
- MARKER(("Cache hit #%u %s klazz@%p getter@%p, setter@%p, ctor@%p\n",
- ++n, zClassName, cacheLine->klazz, cacheLine->midGet,
- cacheLine->midSet, cacheLine->midCtor));
+ MARKER(("Cache hit #%u %s klazz@%p nativePointer field@%p, ctor@%p\n",
+ ++n, zClassName, cacheLine->klazz, cacheLine->fidValue,
+ cacheLine->midCtor));
#endif
assert(cacheLine->klazz);
return cacheLine;
if(freeSlot){
freeSlot->zClassName = zClassName;
freeSlot->klazz = REF_G((*env)->FindClass(env, zClassName));
-#if 0
+#if DUMP_NPH_CACHES
static unsigned int cacheMisses = 0;
- MARKER(("Cache miss #%u %s klazz@%p getter@%p, setter@%p, ctor@%p\n",
+ MARKER(("Cache miss #%u %s klazz@%p nativePointer field@%p, ctor@%p\n",
++cacheMisses, zClassName, freeSlot->klazz,
- freeSlot->midGet, freeSlot->midSet, freeSlot->midCtor));
+ freeSlot->fidValue, freeSlot->midCtor));
#endif
+#undef DUMP_NPH_CACHES
}else{
(*env)->FatalError(env, "MAINTENANCE REQUIRED: NphCache_SIZE is too low.");
}
return freeSlot;
}
+static jfieldID getNativePointerField(JNIEnv *env, jclass klazz){
+ jfieldID rv = (*env)->GetFieldID(env, klazz, "nativePointer", "J");
+ IFTHREW{
+ (*env)->FatalError(env, "Maintenance required: missing nativePointer field.");
+ }
+ return rv;
+}
+
/**
Sets a native ptr value in NativePointerHolder object ppOut.
zClassName must be a static string so we can use its address
*/
static void setNativePointer(JNIEnv * env, jobject ppOut, void * p,
const char *zClassName){
- jmethodID setter = 0;
+ jfieldID setter = 0;
struct NphCacheLine * const cacheLine = S3Global_nph_cache(env, zClassName);
- if(cacheLine && cacheLine->klazz && cacheLine->midSet){
+ if(cacheLine && cacheLine->klazz && cacheLine->fidValue){
assert(zClassName == cacheLine->zClassName);
- setter = cacheLine->midSet;
+ setter = cacheLine->fidValue;
assert(setter);
}else{
jclass const klazz =
cacheLine ? cacheLine->klazz : (*env)->GetObjectClass(env, ppOut);
- setter = (*env)->GetMethodID(env, klazz, "setNativePointer", "(J)V");
+ setter = getNativePointerField(env, klazz);
if(cacheLine){
assert(cacheLine->klazz);
- assert(!cacheLine->midSet);
+ assert(!cacheLine->fidValue);
assert(zClassName == cacheLine->zClassName);
- cacheLine->midSet = setter;
+ cacheLine->fidValue = setter;
}
}
- (*env)->CallVoidMethod(env, ppOut, setter, (jlong)p);
+ (*env)->SetLongField(env, ppOut, setter, (jlong)p);
IFTHREW_REPORT;
}
static void * getNativePointer(JNIEnv * env, jobject pObj, const char *zClassName){
if( 0==pObj ) return 0;
else{
- jmethodID getter = 0;
+ jfieldID getter = 0;
void * rv = 0;
struct NphCacheLine * const cacheLine = S3Global_nph_cache(env, zClassName);
- if(cacheLine && cacheLine->midGet){
- getter = cacheLine->midGet;
+ if(cacheLine && cacheLine->fidValue){
+ getter = cacheLine->fidValue;
}else{
jclass const klazz =
cacheLine ? cacheLine->klazz : (*env)->GetObjectClass(env, pObj);
- getter = (*env)->GetMethodID(env, klazz, "getNativePointer", "()J");
+ getter = getNativePointerField(env, klazz);
if(cacheLine){
assert(cacheLine->klazz);
assert(zClassName == cacheLine->zClassName);
- cacheLine->midGet = getter;
+ cacheLine->fidValue = getter;
}
}
- rv = (void*)(*env)->CallLongMethod(env, pObj, getter);
+ rv = (void*)(*env)->GetLongField(env, pObj, getter);
IFTHREW_REPORT;
return rv;
}
/* Sets a native int32 value in OutputPointer.Int32 object ppOut. */
static void setOutputInt32(JNIEnv * env, jobject ppOut, int v){
- jmethodID setter = 0;
+ jfieldID setter = 0;
struct NphCacheLine * const cacheLine =
S3Global_nph_cache(env, ClassNames.OutputPointer_Int32);
- if(cacheLine && cacheLine->klazz && cacheLine->midSet){
- setter = cacheLine->midSet;
+ if(cacheLine && cacheLine->klazz && cacheLine->fidValue){
+ setter = cacheLine->fidValue;
}else{
const jclass klazz = (*env)->GetObjectClass(env, ppOut);
- setter = (*env)->GetMethodID(env, klazz, "setValue", "(I)V");
+ setter = (*env)->GetFieldID(env, klazz, "value", "I");
if(cacheLine){
- assert(!cacheLine->midSet);
- cacheLine->midSet = setter;
+ assert(!cacheLine->fidValue);
+ cacheLine->fidValue = setter;
}
}
- (*env)->CallVoidMethod(env, ppOut, setter, (jint)v);
+ (*env)->SetIntField(env, ppOut, setter, (jint)v);
IFTHREW_REPORT;
}
-#if 0
-/* Fetches a native int32 value from OutputPointer.Int32 object pObj. */
-static int getOutputInt(JNIEnv * env, jobject pObj){
- const jclass klazz = (*env)->GetObjectClass(env, pObj);
- const jmethodID getter =
- (*env)->GetMethodID(env, klazz, "getValue", "(V)I;");
- return (int)(*env)->CallIntMethod(env, pObj, getter);
-}
-#define VAL_GET_INT(OBJ) getOutputInt(env, OBJ)
-#endif
-
static int encodingTypeIsValid(int eTextRep){
switch(eTextRep){
case SQLITE_UTF8: case SQLITE_UTF16:
NativePointerHolder is not inadvertently passed to an incompatible
function signature.
- These objects are not intended to _own_ the pointer they refer to.
- They are intended to simply communicate that pointer between C and
- Java.
+ These objects do not _own_ the pointer they refer to. They are
+ intended simply to communicate that pointer between C and Java.
*/
public class NativePointerHolder<ContextType> {
- private long pointer;
- public NativePointerHolder(long pointer){
- this.pointer = pointer;
- }
- public NativePointerHolder(){
- this.pointer = 0;
- }
- public final long getNativePointer(){ return pointer; }
- public final void setNativePointer(long p){ pointer = p; }
+ //! Only set from JNI, where access permissions don't matter.
+ private long nativePointer = 0;
+ public final long getNativePointer(){ return nativePointer; }
}
public final class OutputPointer {
public static final class Int32 {
private int value;
- public final void setValue(int v){value = v;}
public final int getValue(){return value;}
}
public static final class Int64 {
private long value;
- public final void setValue(long v){value = v;}
public final long getValue(){return value;}
}
}
public static native int sqlite3_close_v2(@NotNull sqlite3 db);
- //TODO? public static native int sqlite3_collation_needed(sqlite3*,void*,void(*)(void*,sqlite3*,int eTextRep,const char*));
- //TODO? public static native int sqlite3_collation_needed16(sqlite3*,void*,void(*)(void*,sqlite3*,int eTextRep,const void*));
-
public static native byte[] sqlite3_column_blob(@NotNull sqlite3_stmt stmt, int ndx);
public static native int sqlite3_column_bytes(@NotNull sqlite3_stmt stmt, int ndx);
the db if the db uses the default encoding of UTF-8.
To extract _standard_ UTF-8, use sqlite3_column_text_utf8().
- This API includes no functions for working with Modified UTF-8.
+ This API includes no functions for working with Java's Modified
+ UTF-8.
[^1]: https://stackoverflow.com/questions/7921016
*/
*/
public static native byte[] sqlite3_column_text_utf8(@NotNull sqlite3_stmt stmt,
int ndx);
- //TODO public static native ?type? sqlite3_column_text16(@NotNull sqlite3_stmt stmt, int ndx);
- //TODO: public static Object sqlite3_column_to_java(@NotNull sqlite3_value v){...}
+
+ // The real utility of this function is questionable.
+ // /**
+ // Returns a Java value representation based on the value of
+ // sqlite_value_type(). For integer types it returns either Integer
+ // or Long, depending on whether the value will fit in an
+ // Integer. For floating-point values it always returns type Double.
+
+ // If the column was bound using sqlite3_result_java_object() then
+ // that value, as an Object, is returned.
+ // */
+ // public static Object sqlite3_column_to_java(@NotNull sqlite3_stmt stmt,
+ // int ndx){
+ // sqlite3_value v = sqlite3_column_value(stmt, ndx);
+ // Object rv = null;
+ // if(null == v) return v;
+ // v = sqlite3_value_dup(v)/*need a protected value*/;
+ // if(null == v) return v /* OOM error in C */;
+ // if(112/* 'p' */ == sqlite3_value_subtype(v)){
+ // rv = sqlite3_value_java_object(v);
+ // }else{
+ // switch(sqlite3_value_type(v)){
+ // case SQLITE_INTEGER: {
+ // final long i = sqlite3_value_int64(v);
+ // rv = (i<=0x7fffffff && i>=-0x7fffffff-1)
+ // ? new Integer((int)i) : new Long(i);
+ // break;
+ // }
+ // case SQLITE_FLOAT: rv = new Double(sqlite3_value_double(v)); break;
+ // case SQLITE_BLOB: rv = sqlite3_value_blob(v); break;
+ // case SQLITE_TEXT: rv = sqlite3_value_text(v); break;
+ // default: break;
+ // }
+ // }
+ // sqlite3_value_free(v);
+ // return rv;
+ // }
public static native int sqlite3_column_type(@NotNull sqlite3_stmt stmt,
int ndx);
allocate such strings and store them somewhere for long-term use
(leaking them more likely than not). Even then, passing around a
pointer via Java like that has little practical use.
+
+ Note that there is no sqlite3_bind_java_object() counterpart.
*/
public static native void sqlite3_result_java_object(@NotNull sqlite3_context cx,
@NotNull Object o);
private static void testUdfJavaObject(){
final sqlite3 db = createNewDb();
- final ValueHolder<Long> testResult = new ValueHolder<>(42L);
+ final ValueHolder<sqlite3> testResult = new ValueHolder<>(db);
SQLFunction func = new SQLFunction.Scalar(){
public void xFunc(sqlite3_context cx, sqlite3_value args[]){
sqlite3_result_java_object(cx, testResult.value);
sqlite3_stmt stmt = new sqlite3_stmt();
sqlite3_prepare(db, "select myfunc()", stmt);
affirm( 0 != stmt.getNativePointer() );
+ affirm( testResult.value == db );
int n = 0;
if( SQLITE_ROW == sqlite3_step(stmt) ){
- sqlite3_value v = sqlite3_column_value(stmt, 0);
+ final sqlite3_value v = sqlite3_column_value(stmt, 0);
affirm( testResult.value == sqlite3_value_java_object(v) );
- affirm( testResult.value == sqlite3_value_java_casted(v, Long.class) );
+ affirm( testResult.value == sqlite3_value_java_casted(v, sqlite3.class) );
affirm( testResult.value ==
sqlite3_value_java_casted(v, testResult.value.getClass()) );
- affirm( null == sqlite3_value_java_casted(v, Double.class) );
+ affirm( testResult.value == sqlite3_value_java_casted(v, Object.class) );
+ affirm( null == sqlite3_value_java_casted(v, String.class) );
++n;
}
sqlite3_finalize(stmt);
-C Bind\ssqlite3_collation_needed()\sto\sJNI.\sRelated\sadjacent\scleanups\sand\sfixes.
-D 2023-07-30T11:36:41.439
+C Minor\stweaks\sand\soptimizations\sin\sthe\sJNI\sbindings.
+D 2023-07-30T13:30:52.663
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a32075a8
F ext/jni/GNUmakefile 56a014dbff9516774d895ec1ae9df0ed442765b556f79a0fc0b5bc438217200d
F ext/jni/README.md c0e6e80935e7761acead89b69c87765b23a6bcb2858c321c3d05681fd338292a
-F ext/jni/src/c/sqlite3-jni.c 1934a72f33fe356d8af810a8a662dd8109026cd0bbf298dda1fe8bd1146603ad
+F ext/jni/src/c/sqlite3-jni.c d3ce5d96feb5eebf8dd171f041704798f3d0a5da1ee93a43788059d1d9f167ff
F ext/jni/src/c/sqlite3-jni.h 28def286ee305c1c89a43ac5918a6862d985d0534f7ccbbd74df4885d3918b73
F ext/jni/src/org/sqlite/jni/BusyHandler.java 1b1d3e5c86cd796a0580c81b6af6550ad943baa25e47ada0dcca3aff3ebe978c
F ext/jni/src/org/sqlite/jni/Collation.java 8dffbb00938007ad0967b2ab424d3c908413af1bbd3d212b9c9899910f1218d1
F ext/jni/src/org/sqlite/jni/CollationNeeded.java ebc7cd96d46a70daa76016a308e80f70a3f21d3282787c8d139aa840fdcb1bd7
F ext/jni/src/org/sqlite/jni/CommitHook.java 87c6a8e5138c61a8eeff018fe16d23f29219150239746032687f245938baca1a
-F ext/jni/src/org/sqlite/jni/NativePointerHolder.java 70dc7bc41f80352ff3d4331e2e24f45fcd23353b3641e2f68a81bd8262215861
-F ext/jni/src/org/sqlite/jni/OutputPointer.java 08a752b58a33696c5eaf0eb9361a0966b188dec40f4a3613eb133123951f6c5f
+F ext/jni/src/org/sqlite/jni/NativePointerHolder.java 9c5d901cce4f7e57c3d623f4e2476f9f79a8eed6e51b2a603f37866018e040ee
+F ext/jni/src/org/sqlite/jni/OutputPointer.java a5cb651df3b3adb65a9aca6cf9a094dea1346fc9ee5f341f79276348ac268351
F ext/jni/src/org/sqlite/jni/ProgressHandler.java 5979450e996416d28543f1d42634d308439565a99332a8bd84e424af667116cc
F ext/jni/src/org/sqlite/jni/RollbackHook.java b04c8abcc6ade44a8a57129e33765793f69df0ba909e49ba18d73f4268d92564
F ext/jni/src/org/sqlite/jni/SQLFunction.java 663a4e479ec65bfbf893586439e12d30b8237898064a22ab64f5658b57315f37
-F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 2c4564b19f5366927c9a5062e36ffb7744e7f69d00b3f8ce35fe59b2f3d60698
-F ext/jni/src/org/sqlite/jni/Tester1.java a89a87f8debd89f3488a65cb42af8e14fb0150b05d5a4a3592fb86d0cfda3287
+F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 42ca7686d009a56e4f5ceb74a0bd32ca69c025f2bf30d3e906696ad36ac72510
+F ext/jni/src/org/sqlite/jni/Tester1.java 1690172fccafbf8d8170b55b950003db182265c26dbb5a510122ec46a44d2611
F ext/jni/src/org/sqlite/jni/Tracer.java c2fe1eba4a76581b93b375a7b95ab1919e5ae60accfb06d6beb067b033e9bae1
F ext/jni/src/org/sqlite/jni/UpdateHook.java e58645a1727f8a9bbe72dc072ec5b40d9f9362cb0aa24acfe93f49ff56a9016d
F ext/jni/src/org/sqlite/jni/ValueHolder.java f022873abaabf64f3dd71ab0d6037c6e71cece3b8819fa10bf26a5461dc973ee
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 7ac6614e69b03304d09745619ed83f12c7eb775aaf4a636a79289b01642ddd14
-R fe2ec7cfe7eced93fd3b168114e0d2e0
+P 16ff167691733350907d2d995c774a885214acd0fe8ec491c16b786f00fe85d4
+R fed5a38c4ce39785114a9bf68d9f61c4
U stephan
-Z 9246dbb52619ce19a9defcf2e690b44f
+Z a065a6cc3e3d2dbbd261c22dc07adbce
# Remove this line to create a well-formed Fossil manifest.
-16ff167691733350907d2d995c774a885214acd0fe8ec491c16b786f00fe85d4
\ No newline at end of file
+41fb5898f1a78d9fd85a020e28a6048a7359b54e35632e9072917cbdbcd8b07d
\ No newline at end of file