]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
fiddle: lots of generic refactoring, restructuring, and cleanup in the higher-level...
authorstephan <stephan@noemail.net>
Tue, 24 May 2022 14:36:45 +0000 (14:36 +0000)
committerstephan <stephan@noemail.net>
Tue, 24 May 2022 14:36:45 +0000 (14:36 +0000)
FossilOrigin-Name: ed19fef3459499abb0a4a010f368b4576d6e068d930c8480446ea677ac87c1c1

ext/fiddle/Makefile
ext/fiddle/fiddle.js
ext/fiddle/sqlite3-api.js
ext/fiddle/testing-common.js
ext/fiddle/testing.css [new file with mode: 0644]
ext/fiddle/testing1.html
ext/fiddle/testing1.js
manifest
manifest.uuid

index 73107d3bde29076071344a6b66b15d96faf16ee9..16f68132adbb847c95a29960d771cf9ec82a7ce2 100644 (file)
@@ -1,10 +1,31 @@
-# This makefile exists primarily to simplify/speed up development from
-# emacs. It is not part of the canonical build process.
+# This GNU makefile exists primarily to simplify/speed up development
+# from emacs. It is not part of the canonical build process.
 default:
        make -C ../.. wasm -e emcc_opt=-O0
 
 clean:
        make -C ../../ clean-wasm
 
-push-demo:
-       rsync -va fiddle*.js fiddle*.wasm fiddle.html *.css wh2:www/wh/sqlite3/.
+demo_files = emscripten.css fiddle.html \
+             fiddle.js fiddle-module.js \
+             fiddle-module.wasm fiddle-worker.js
+
+# demo_target is the remote destination for the fiddle app. It
+# must be a [user@]HOST:/path for rsync.
+# Note that the target "should probably" contain a symlink of
+# index.html -> fiddle.html.
+demo_target ?=
+ifeq (,$(demo_target))
+ifneq (,$(wildcard /home/stephan))
+  demo_target = wh2:www/wh/sqlite3/.
+else ifneq (,$(wildcard /home/drh))
+  #demo_target = if appropriate, add that user@host:/path here
+endif
+endif
+
+push-fiddle: $(demo_files)
+       @if [ x = "x$(demo_target)" ]; then \
+               echo "demo_target must be a [user@]HOST:/path for rsync"; \
+               exit 1; \
+       fi
+       rsync -va $(demo_files) $(demo_target)
index 2353509da72f7729f37e2ac72532485676a07711..5590279d0c06b0df65417fc3571cdb19178be8af 100644 (file)
         return (arguments.length>1 ? arguments[0] : document)
             .querySelector(arguments[arguments.length-1]);
     };
-    
-    const statusElement = E('#module-status');
-    const progressElement = E('#module-progress');
-    const spinnerElement = E('#module-spinner');
 
+    /** Handles status updates from the Module object. */
     SF.addMsgHandler('module', function f(ev){
         ev = ev.data;
         if('status'!==ev.type){
             console.warn("Unexpected module-type message:",ev);
             return;
         }
+        if(!f.ui){
+            f.ui = {
+                status: E('#module-status'),
+                progress: E('#module-progress'),
+                spinner: E('#module-spinner')
+            };
+        }
         const msg = ev.data;
-        progressElement.value = msg.step;
-        progressElement.max = msg.step + 1/*we don't know how many steps to expect*/;
+        if(f.ui.progres){
+            progress.value = msg.step;
+            progress.max = msg.step + 1/*we don't know how many steps to expect*/;
+        }
         if(1==msg.step){
-            progressElement.hidden = false;
-            spinnerElement.hidden = false;
+            f.ui.progress.classList.remove('hidden');
+            f.ui.spinner.classList.remove('hidden');
         }
         if(msg.text){
-            statusElement.classList.remove('hidden');
-            statusElement.innerText = msg.text;
+            f.ui.status.classList.remove('hidden');
+            f.ui.status.innerText = msg.text;
         }else{
-            progressElement.remove();
-            spinnerElement.remove();
-            statusElement.classList.add('hidden');
+            if(f.ui.progress){
+                f.ui.progress.remove();
+                f.ui.spinner.remove();
+                delete f.ui.progress;
+                delete f.ui.spinner;
+            }
+            f.ui.status.classList.add('hidden');
             /* The module can post messages about fatal problems,
                e.g. an exit() being triggered or assertion failure,
                after the last "load" message has arrived, so
-               leave the statusElement and message listener intact. */
+               leave f.ui.status and message listener intact. */
         }
     });
 
         },false);
 
         /** To be called immediately before work is sent to the
-            worker.  Updates some UI elements. The 'working'/'end'
+            worker. Updates some UI elements. The 'working'/'end'
             event will apply the inverse, undoing the bits this
-            function does.  This impl is not in the 'working'/'start'
+            function does. This impl is not in the 'working'/'start'
             event handler because that event is given to us
             asynchronously _after_ we need to have performed this
             work.
         };
 
         SF.addMsgHandler('working',function f(ev){
-            if('start' === ev.data){
-                /* See notes in preStartWork(). */
-            }else if('end' === ev.data){
-                preStartWork._.pageTitle.innerText = preStartWork._.pageTitleOrig;
-                btnShellExec.innerText = preStartWork._.btnLabel;
-                btnShellExec.removeAttribute('disabled');
+            switch(ev.data){
+                case 'start': /* See notes in preStartWork(). */; return;
+                case 'end':
+                    preStartWork._.pageTitle.innerText = preStartWork._.pageTitleOrig;
+                    btnShellExec.innerText = preStartWork._.btnLabel;
+                    btnShellExec.removeAttribute('disabled');
+                    return;
             }
+            console.warn("Unhandled 'working' event:",ev.data);
         });
 
         /* For each checkbox with data-csstgt, set up a handler which
index 90b8de798bc25611d26f874aea5ed003096345df..e97e7c18374dc38d2ae8311f7a0151474e09f050 100644 (file)
   Note that this file is not named sqlite3.js because that file gets
   generated by emscripten as the JS-glue counterpart of sqlite3.wasm.
 
-  This code installs an object named self.sqlite3, where self is
-  expected to be either the global window or Worker object:
+  This code installs an object named self.Module.sqlite3, where self
+  is expected to be either the global window or Worker object and
+  Module is the object set up by the emscripten infrastructure. The
+  sqlite3 object looks like:
 
-  self.sqlite3 = {
-      api: core WASM bindings of sqlite3 APIs,
+  {
+      api: bindings for much of the core sqlite3 APIs,
       SQLite3: high-level OO API wrapper
-  };
+  }
 
   The way we export this module is not _really_ modern-JS-friendly
-  because it exports a global symbol (which is admittedly not
-  ideal). Exporting it "cleanly," without introducing any global-scope
-  symbols, requires using a module loader in all client code. As there
-  are several different approaches, none of which this developer is
-  currently truly familiar with, the current approach will have to do
-  for the time being.
-
-  Because using the low-level API properly requires some degree of
-  WASM-related magic, it is not recommended that that API be used
-  as-is in client-level code. Rather, client code should use the
-  higher-level OO API or write a custom wrapper on top of the
-  lower-level API.
-
-  This file installs namespace.sqlite3, where namespace is `self`,
-  meaning either the global window or worker, depending on where this
-  is loaded from.
+  because it exports/relies on a global symbol (which is admittedly
+  not ideal). Exporting it "cleanly," without introducing any
+  global-scope symbols, requires using a module loader in all client
+  code. As there are several different approaches, none of which this
+  developer is currently truly familiar with, the current approach
+  will have to do for the time being.
+
+  Because using certain parts of the low-level API properly requires
+  some degree of WASM-related magic, it is not recommended that that
+  API be used as-is in client-level code. Rather, client code should
+  use the higher-level OO API or write a custom wrapper on top of the
+  lower-level API. In short, using any C-style APIs which take
+  pointers-to-pointer arguments require WASM-specific interfaces
+  installed by emcscripten-generated code. Those which take or return
+  only integers, doubles, strings, or "plain" pointers to db or
+  statement objects can be used in a straightforward manner.
 
   # Goals and Non-goals of this API
 
     can be interacted with, but keeping the DB operations out of the
     UI thread is generally desirable.
 
+  - Insofar as possible, support client-side storage using JS
+    filesystem APIs. As of this writing, such things are still very
+    much TODO.
 
   Non-goals:
 
-  - As WASM is a web-based technology and UTF-8 is the King of
-    Encodings in that realm, there are no plans to support the
-    UTF16-related APIs will not be. They would add a complication to
-    the bindings for no appreciable benefit.
+  - As WASM is a web-centric technology and UTF-8 is the King of
+    Encodings in that realm, there are no current plans to support the
+    UTF16-related APIs. They would add a complication to the bindings
+    for no appreciable benefit.
 
   - Supporting old or niche-market platforms. WASM is built for a
     modern web and requires modern platforms.
         throw new Error(Array.prototype.join.call(arguments, ' '));
     };
 
-    const S/*convenience alias*/ = api;
-
     /**
        The DB class wraps a sqlite3 db handle.
     */
             toss("TODO: support blob image of db here.");
         }
         setValue(pPtrArg, 0, "i32");
-        this.checkRc(S.sqlite3_open(name, pPtrArg));
+        this.checkRc(api.sqlite3_open(name, pPtrArg));
         this._pDb = getValue(pPtrArg, "i32");
         this.filename = name;
         this._statements = {/*map of open Stmt _pointers_ to Stmt*/};
         }
         this.db = arguments[0];
         this._pStmt = arguments[1];
-        this.columnCount = S.sqlite3_column_count(this._pStmt);
-        this.parameterCount = S.sqlite3_bind_parameter_count(this._pStmt);
+        this.columnCount = api.sqlite3_column_count(this._pStmt);
+        this.parameterCount = api.sqlite3_bind_parameter_count(this._pStmt);
         this._allocs = [/*list of alloc'd memory blocks for bind() values*/]
     };
 
         checkRc: function(sqliteResultCode){
             if(!sqliteResultCode) return this;
             toss("sqlite result code",sqliteResultCode+":",
-                 S.sqlite3_errmsg(this._pDb) || "Unknown db error.");
+                 api.sqlite3_errmsg(this._pDb) || "Unknown db error.");
         },
         /**
            Finalizes all open statements and closes this database
                 Object.values(this._udfs).forEach(Module.removeFunction);
                 delete this._udfs;
                 delete this._statements;
-                S.sqlite3_close_v2(this._pDb);
+                api.sqlite3_close_v2(this._pDb);
                 delete this._pDb;
             }
         },
            a name of `main`.
         */
         fileName: function(dbName){
-            return S.sqlite3_db_filename(affirmDbOpen(this)._pDb, dbName||"main");
+            return api.sqlite3_db_filename(affirmDbOpen(this)._pDb, dbName||"main");
         },
         /**
            Compiles the given SQL and returns a prepared Stmt. This is
         prepare: function(sql){
             affirmDbOpen(this);
             setValue(pPtrArg,0,"i32");
-            this.checkRc(S.sqlite3_prepare_v2(this._pDb, sql, -1, pPtrArg, null));
+            this.checkRc(api.sqlite3_prepare_v2(this._pDb, sql, -1, pPtrArg, null));
             const pStmt = getValue(pPtrArg, "i32");
             if(!pStmt) toss("Empty SQL is not permitted.");
             const stmt = new Stmt(this, pStmt, BindTypes);
                 while(getValue(pSql, "i8")){
                     setValue(pPtrArg, 0, "i32");
                     setValue(pzTail, 0, "i32");
-                    this.checkRc(S.sqlite3_prepare_v2_sqlptr(
+                    this.checkRc(api.sqlite3_prepare_v2_sqlptr(
                         this._pDb, pSql, -1, pPtrArg, pzTail
                     ));
                     const pStmt = getValue(pPtrArg, "i32");
                     pSql = getValue(pzTail, "i32");
                     if(!pStmt) continue;
                     if(opt.saveSql){
-                        opt.saveSql.push(S.sqlite3_sql(pStmt).trim());
+                        opt.saveSql.push(api.sqlite3_sql(pStmt).trim());
                     }
                     stmt = new Stmt(this, pStmt, BindTypes);
                     if(bind && stmt.parameterCount){
                     const tgt = [];
                     for(i = 0; i < argc; ++i){
                         pVal = getValue(pArgv + (4 * i), "i32");
-                        valType = S.sqlite3_value_type(pVal);
+                        valType = api.sqlite3_value_type(pVal);
                         switch(valType){
-                            case S.SQLITE_INTEGER:
-                            case S.SQLITE_FLOAT:
-                                arg = S.sqlite3_value_double(pVal);
+                            case api.SQLITE_INTEGER:
+                            case api.SQLITE_FLOAT:
+                                arg = api.sqlite3_value_double(pVal);
                                 break;
                             case SQLITE_TEXT:
-                                arg = S.sqlite3_value_text(pVal);
+                                arg = api.sqlite3_value_text(pVal);
                                 break;
                             case SQLITE_BLOB:{
-                                const n = S.sqlite3_value_bytes(ptr);
-                                const pBlob = S.sqlite3_value_blob(ptr);
+                                const n = api.sqlite3_value_bytes(ptr);
+                                const pBlob = api.sqlite3_value_blob(ptr);
                                 arg = new Uint8Array(n);
                                 let i;
                                 for(i = 0; i < n; ++i) arg[i] = HEAP8[pBlob+i];
                 f._setResult = function(pCx, val){
                     switch(typeof val) {
                         case 'boolean':
-                            S.sqlite3_result_int(pCx, val ? 1 : 0);
+                            api.sqlite3_result_int(pCx, val ? 1 : 0);
                             break;
                         case 'number': {
                             (isInt32(val)
-                             ? S.sqlite3_result_int
-                             : S.sqlite3_result_double)(pCx, val);
+                             ? api.sqlite3_result_int
+                             : api.sqlite3_result_double)(pCx, val);
                             break;
                         }
                         case 'string':
-                            S.sqlite3_result_text(pCx, val, -1,
+                            api.sqlite3_result_text(pCx, val, -1,
                                                   -1/*==SQLITE_TRANSIENT*/);
                             break;
                         case 'object':
                             if(null===val) {
-                                S.sqlite3_result_null(pCx);
+                                api.sqlite3_result_null(pCx);
                                 break;
                             }else if(undefined!==val.length){
                                 const pBlob = Module.allocate(val, ALLOC_NORMAL);
-                                S.sqlite3_result_blob(pCx, pBlob, val.length, -1/*==SQLITE_TRANSIENT*/);
+                                api.sqlite3_result_blob(pCx, pBlob, val.length, -1/*==SQLITE_TRANSIENT*/);
                                 Module._free(blobptr);
                                 break;
                             }
                 try{
                     f._setResult(pCx, callback.apply(null, f._extractArgs(argc, pArgv)));
                 }catch(e){
-                    S.sqlite3_result_error(pCx, e.message, -1);
+                    api.sqlite3_result_error(pCx, e.message, -1);
                 }
             };
             const pUdf = Module.addFunction(wrapper, "viii");
             let fFlags = 0;
-            if(getOwnOption(opt, 'deterministic')) fFlags |= S.SQLITE_DETERMINISTIC;
-            if(getOwnOption(opt, 'directOnly')) fFlags |= S.SQLITE_DIRECTONLY;
-            if(getOwnOption(opt, 'innocuous')) fFlags |= S.SQLITE_INNOCUOUS;
+            if(getOwnOption(opt, 'deterministic')) fFlags |= api.SQLITE_DETERMINISTIC;
+            if(getOwnOption(opt, 'directOnly')) fFlags |= api.SQLITE_DIRECTONLY;
+            if(getOwnOption(opt, 'innocuous')) fFlags |= api.SQLITE_INNOCUOUS;
             name = name.toLowerCase();
             try {
-                this.checkRc(S.sqlite3_create_function_v2(
+                this.checkRc(api.sqlite3_create_function_v2(
                     this._pDb, name,
                     (opt.hasOwnProperty('arity') ? +opt.arity : callback.length),
-                    S.SQLITE_UTF8 | fFlags, null/*pApp*/, pUdf,
+                    api.SQLITE_UTF8 | fFlags, null/*pApp*/, pUdf,
                     null/*xStep*/, null/*xFinal*/, null/*xDestroy*/));
             }catch(e){
                 Module.removeFunction(pUdf);
     */
     const affirmParamIndex = function(stmt,key){
         const n = ('number'===typeof key)
-              ? key : S.sqlite3_bind_parameter_index(stmt._pStmt, key);
+              ? key : api.sqlite3_bind_parameter_index(stmt._pStmt, key);
         if(0===n || (n===key && (n!==(n|0)/*floating point*/))){
             toss("Invalid bind() parameter name: "+key);
         }
                     const bytes = intArrayFromString(val,true);
                     const pStr = Module.allocate(bytes, ALLOC_NORMAL);
                     stmt._allocs.push(pStr);
-                    const func =  asBlob ? S.sqlite3_bind_blob : S.sqlite3_bind_text;
+                    const func =  asBlob ? api.sqlite3_bind_blob : api.sqlite3_bind_text;
                     return func(stmt._pStmt, ndx, pStr, bytes.length, 0);
                 }
             };
         let rc = 0;
         switch((null===val || undefined===val) ? BindTypes.null : bindType){
             case BindTypes.null:
-                rc = S.sqlite3_bind_null(stmt._pStmt, ndx);
+                rc = api.sqlite3_bind_null(stmt._pStmt, ndx);
                 break;
             case BindTypes.string:{
                 rc = f._.string(stmt, ndx, val, false);
             }
             case BindTypes.number: {
                 const m = (isInt32(val)
-                           ? S.sqlite3_bind_int
+                           ? api.sqlite3_bind_int
                            /*It's illegal to bind a 64-bit int
                              from here*/
-                           : S.sqlite3_bind_double);
+                           : api.sqlite3_bind_double);
                 rc = m(stmt._pStmt, ndx, val);
                 break;
             }
             case BindTypes.boolean:
-                rc = S.sqlite3_bind_int(stmt._pStmt, ndx, val ? 1 : 0);
+                rc = api.sqlite3_bind_int(stmt._pStmt, ndx, val ? 1 : 0);
                 break;
             case BindTypes.blob: {
                 if('string'===typeof val){
                     }
                     const pBlob = Module.allocate(val, ALLOC_NORMAL);
                     stmt._allocs.push(pBlob);
-                    rc = S.sqlite3_bind_blob(stmt._pStmt, ndx, pBlob, len, 0);
+                    rc = api.sqlite3_bind_blob(stmt._pStmt, ndx, pBlob, len, 0);
                 }
             }
             default: toss("Unsupported bind() argument type.");
                 affirmUnlocked(this,'finalize()');
                 freeBindMemory(this);
                 delete this.db._statements[this._pStmt];
-                S.sqlite3_finalize(this._pStmt);
+                api.sqlite3_finalize(this._pStmt);
                 delete this.columnCount;
                 delete this.parameterCount;
                 delete this._pStmt;
             freeBindMemory(
                 affirmUnlocked(affirmStmtOpen(this), 'clearBindings()')
             );
-            S.sqlite3_clear_bindings(this._pStmt);
+            api.sqlite3_clear_bindings(this._pStmt);
             this._mayGet = false;
             return this;
         },
         reset: function(alsoClearBinds){
             affirmUnlocked(this,'reset()');
             if(alsoClearBinds) this.clearBindings();
-            S.sqlite3_reset(affirmStmtOpen(this)._pStmt);
+            api.sqlite3_reset(affirmStmtOpen(this)._pStmt);
             this._mayGet = false;
             return this;
         },
         */
         step: function(){
             affirmUnlocked(this, 'step()');
-            const rc = S.sqlite3_step(affirmStmtOpen(this)._pStmt);
+            const rc = api.sqlite3_step(affirmStmtOpen(this)._pStmt);
             switch(rc){
-                case S.SQLITE_DONE: return this._mayGet = false;
-                case S.SQLITE_ROW: return this._mayGet = true;
+                case api.SQLITE_DONE: return this._mayGet = false;
+                case api.SQLITE_ROW: return this._mayGet = true;
                 default:
                     this._mayGet = false;
                     console.warn("sqlite3_step() rc=",rc,"SQL =",
-                                 S.sqlite3_sql(this._pStmt));
+                                 api.sqlite3_sql(this._pStmt));
                     this.db.checkRc(rc);
             };
         },
             }else if(ndx && 'object'===typeof ndx){
                 let i = 0;
                 while(i<this.columnCount){
-                    ndx[S.sqlite3_column_name(this._pStmt,i)] = this.get(i++);
+                    ndx[api.sqlite3_column_name(this._pStmt,i)] = this.get(i++);
                 }
                 return ndx;
             }
             affirmColIndex(this, ndx);
             switch(undefined===asType
-                   ? S.sqlite3_column_type(this._pStmt, ndx)
+                   ? api.sqlite3_column_type(this._pStmt, ndx)
                    : asType){
-                case S.SQLITE_NULL: return null;
-                case S.SQLITE_INTEGER:{
-                    return 0 | S.sqlite3_column_double(this._pStmt, ndx);
+                case api.SQLITE_NULL: return null;
+                case api.SQLITE_INTEGER:{
+                    return 0 | api.sqlite3_column_double(this._pStmt, ndx);
                     /* ^^^^^^^^ strips any fractional part and handles
                        handles >32bits */
                 }
-                case S.SQLITE_FLOAT:
-                    return S.sqlite3_column_double(this._pStmt, ndx);
-                case S.SQLITE_TEXT:
-                    return S.sqlite3_column_text(this._pStmt, ndx);
-                case S.SQLITE_BLOB: {
-                    const n = S.sqlite3_column_bytes(this._pStmt, ndx);
-                    const ptr = S.sqlite3_column_blob(this._pStmt, ndx);
+                case api.SQLITE_FLOAT:
+                    return api.sqlite3_column_double(this._pStmt, ndx);
+                case api.SQLITE_TEXT:
+                    return api.sqlite3_column_text(this._pStmt, ndx);
+                case api.SQLITE_BLOB: {
+                    const n = api.sqlite3_column_bytes(this._pStmt, ndx);
+                    const ptr = api.sqlite3_column_blob(this._pStmt, ndx);
                     const rc = new Uint8Array(n);
                     for(let i = 0; i < n; ++i) rc[i] = HEAP8[ptr + i];
                     return rc;
         },
         /** Equivalent to get(ndx) but coerces the result to an
             integer. */
-        getInt: function(ndx){return this.get(ndx,S.SQLITE_INTEGER)},
+        getInt: function(ndx){return this.get(ndx,api.SQLITE_INTEGER)},
         /** Equivalent to get(ndx) but coerces the result to a
             float. */
-        getFloat: function(ndx){return this.get(ndx,S.SQLITE_FLOAT)},
+        getFloat: function(ndx){return this.get(ndx,api.SQLITE_FLOAT)},
         /** Equivalent to get(ndx) but coerces the result to a
             string. */
-        getString: function(ndx){return this.get(ndx,S.SQLITE_TEXT)},
+        getString: function(ndx){return this.get(ndx,api.SQLITE_TEXT)},
         /** Equivalent to get(ndx) but coerces the result to a
             Uint8Array. */
-        getBlob: function(ndx){return this.get(ndx,S.SQLITE_BLOB)},
+        getBlob: function(ndx){return this.get(ndx,api.SQLITE_BLOB)},
         /**
            A convenience wrapper around get() which fetches the value
            as a string and then, if it is not null, passes it to
            string, on the other hand, will trigger an exception.
         */
         getJSON: function(ndx){
-            const s = this.get(ndx, S.SQLITE_STRING);
+            const s = this.get(ndx, api.SQLITE_STRING);
             return null===s ? s : JSON.parse(s);
         },
         /**
            Returns the result column name of the given index, or
            throws if index is out of bounds or this statement has been
-           finalized.
+           finalized. This can be used without having run step()
+           first.
         */
         getColumnName: function(ndx){
-            return S.sqlite3_column_name(
+            return api.sqlite3_column_name(
                 affirmColIndex(affirmStmtOpen(this),ndx)._pStmt, ndx
             );
         },
             affirmColIndex(affirmStmtOpen(this),0);
             if(!tgt) tgt = [];
             for(let i = 0; i < this.columnCount; ++i){
-                tgt.push(S.sqlite3_column_name(this._pStmt, i));
+                tgt.push(api.sqlite3_column_name(this._pStmt, i));
             }
             return tgt;
         },
         */
         getParamIndex: function(name){
             return (affirmStmtOpen(this).parameterCount
-                    ? S.sqlite3_bind_parameter_index(this._pStmt, name)
+                    ? api.sqlite3_bind_parameter_index(this._pStmt, name)
                     : undefined);
         }
     }/*Stmt.prototype*/;
     /** OO binding's namespace. */
     const SQLite3 = {
         version: {
-            lib: S.sqlite3_libversion(),
+            lib: api.sqlite3_libversion(),
             ooApi: "0.0.1"
         },
         DB,
                 }
                 const rc = {}, ov = [0,0];
                 let i = 0, k;
-                while((k = S.sqlite3_compileoption_get(i++))){
+                while((k = api.sqlite3_compileoption_get(i++))){
                     f._opt(k,ov);
                     rc[ov[0]] = ov[1];
                 }
             else if(Array.isArray(optName)){
                 const rc = {};
                 optName.forEach((v)=>{
-                    rc[v] = S.sqlite3_compileoption_used(v);
+                    rc[v] = api.sqlite3_compileoption_used(v);
                 });
                 return rc;
             }
             else if('object' === typeof optName){
                 Object.keys(optName).forEach((k)=> {
-                    optName[k] = S.sqlite3_compileoption_used(k);
+                    optName[k] = api.sqlite3_compileoption_used(k);
                 });
                 return optName;
             }
             return (
                 'string'===typeof optName
-            ) ? !!S.sqlite3_compileoption_used(optName) : false;
+            ) ? !!api.sqlite3_compileoption_used(optName) : false;
         }
     };
 
         api: api,
         SQLite3
     };
-})(self/*worker or window*/);
+})(self/*worker or window*/.Module);
index 27014812891a6072c6e2e16ec72b2944c66d4d6e..9a51dfad419b83bcbe8c5b3c80d5a731fa8af460 100644 (file)
@@ -26,9 +26,6 @@
     };
 
     /* emscripten-related bits... */
-    const statusElement = E('#module-status');
-    const progressElement = E('#module-progress');
-    const spinnerElement = E('#module-spinner');
     self.Module = {
         /* ^^^ cannot declare that const because sqlite3.js
            (auto-generated) includes a decl for it and runs in this
             console.error.apply(console, Array.prototype.slice.call(arguments));
         },
         setStatus: function f(text){
-            if(!f.last) f.last = { time: Date.now(), text: '' };
+            if(!f.last){
+                f.last = { text: '', step: 0 };
+                f.ui = {
+                    status: E('#module-status'),
+                    progress: E('#module-progress'),
+                    spinner: E('#module-spinner')
+                };
+            }
             if(text === f.last.text) return;
-            const m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/);
-            const now = Date.now();
-            if(m && now - f.last.time < 30) return; // if this is a progress update, skip it if too soon
-            f.last.time = now;
             f.last.text = text;
-            if(m) {
-                text = m[1];
-                progressElement.value = parseInt(m[2])*100;
-                progressElement.max = parseInt(m[4])*100;
-                progressElement.hidden = false;
-                spinnerElement.hidden = false;
-            } else {
-                progressElement.remove();
-                if(!text) spinnerElement.remove();
+            if(f.ui.progress){
+                f.ui.progress.value = f.last.step;
+                f.ui.progress.max = f.last.step + 1;
+            }
+            ++f.last.step;
+            if(text) {
+                f.ui.status.classList.remove('hidden');
+                f.ui.status.innerText = text;
+            }else{
+                if(f.ui.progress){
+                    f.ui.progress.remove();
+                    f.ui.spinner.remove();
+                    delete f.ui.progress;
+                    delete f.ui.spinner;
+                }
+                f.ui.status.classList.add('hidden');
             }
-            if(text) statusElement.innerText = text;
-            else statusElement.remove();
         },
         totalDependencies: 0,
         monitorRunDependencies: function(left) {
                               + '/' + this.totalDependencies + ')')
                            : 'All downloads complete.');
         },
-        /* Loads sqlite3-api.js and calls the given callback (if
-           provided), passing it an object which contains the sqlite3
-           and SQLite3 modules. Whether this is synchronous or async
-           depends on whether it's run in the main thread (async) or a
-           worker (synchronous). */
-        loadSqliteAPI: function(callback){
+        /**
+           Loads sqlite3-api.js and calls the given callback (if
+           provided), passing it an object:
+
+           {
+             api:sqlite3_c-like API wrapper,
+             SQLite3: OO wrapper
+           }
+
+           Whether this is synchronous or async depends on whether
+           it's run in the main thread (async) or a worker
+           (synchronous).
+
+           If called after the module has been loaded, it uses a
+           cached reference, noting that multiple async calls may end
+           up loading it multiple times.
+        */
+        loadSqliteAPI: function f(callback){
+            const namespace = self.Module;
+            if(namespace.sqlite3){
+                if(callback) callback(namespace.sqlite3);
+                return;
+            }
             const theScript = 'sqlite3-api.js';
             if(self.importScripts){/*worker*/
                 importScripts(theScript);
-                if(callback) callback(self.sqlite3);
+                if(callback) callback(namespace.sqlite3);
             }else{/*main thread*/
                 new Promise((resolve, reject) => {
                     const script = document.createElement('script');
                     script.async = true;
                     script.src = theScript;
                 }).then(() => {
-                    if(callback) callback({sqlite3:self.sqlite3,
-                                           SQLite3:self.SQLite3});
+                    if(callback) callback(namespace.sqlite3);
                 });
             }
         }
diff --git a/ext/fiddle/testing.css b/ext/fiddle/testing.css
new file mode 100644 (file)
index 0000000..f87dbd2
--- /dev/null
@@ -0,0 +1,31 @@
+textarea {
+    font-family: monospace;
+}
+header {
+    font-size: 130%;
+    font-weight: bold;
+}
+.hidden, .initially-hidden {
+    position: absolute !important;
+    opacity: 0 !important;
+    pointer-events: none !important;
+    display: none !important;
+}
+fieldset.options {
+    font-size: 75%;
+}
+fieldset > legend {
+    padding: 0 0.5em;
+}
+span.labeled-input {
+    padding: 0.25em;
+    margin: 0.25em 0.5em;
+    border-radius: 0.25em;
+    white-space: nowrap;
+    background: #0002;
+}
+.center { text-align: center; }
+.error {
+    color: red;
+    background-color: yellow;
+}
index d428f12f653f3adbbe765e03486a55cb34dbab45..08a0009c60a3415f92e5093e7916aab162648106 100644 (file)
@@ -4,6 +4,7 @@
     <meta charset="utf-8">
     <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
     <link rel="stylesheet" href="emscripten.css"/>
+    <link rel="stylesheet" href="testing.css"/>
     <title>sqlite3-api.js tests</title>
     <style></style>
   </head>
index 5dfc9cd8e949fdb95ce73aa91f73b265e63652ff..d35557865d308801faa311eb90422b4951643466 100644 (file)
 
   A basic test script for sqlite3-api.js.
 */
-
-const mainTest1 = function(namespace){
+(function(){
     const T = self.SqliteTestUtil;
-    T.assert(Module._free instanceof Function).
-        assert(Module.allocate instanceof Function).
-        assert(Module.addFunction instanceof Function).
-        assert(Module.removeFunction instanceof Function);
-
-    const S = namespace.sqlite3.api;
-    const oo = namespace.sqlite3.SQLite3;
-    console.log("Loaded module:",S.sqlite3_libversion(),
-                S.sqlite3_sourceid());
-    const db = new oo.DB();
     const log = console.log.bind(console);
-    try {
 
+    const test1 = function(db,api){
+        log("Basic sanity tests...");
         T.assert(db._pDb);
-        log("DB:",db.filename);
-        log("Build options:",oo.compileOptionUsed());
         let st = db.prepare("select 3 as a");
-        log("statement =",st);
+        //log("statement =",st);
         T.assert(st._pStmt)
             .assert(!st._mayGet)
             .assert('a' === st.getColumnName(0))
@@ -43,14 +31,14 @@ const mainTest1 = function(namespace){
             .assert(true===st.step())
             .assert(3 === st.get(0))
             .mustThrow(()=>st.get(1))
-            .mustThrow(()=>st.get(0,~S.SQLITE_INTEGER))
-            .assert(3 === st.get(0,S.SQLITE_INTEGER))
+            .mustThrow(()=>st.get(0,~api.SQLITE_INTEGER))
+            .assert(3 === st.get(0,api.SQLITE_INTEGER))
             .assert(3 === st.getInt(0))
-            .assert('3' === st.get(0,S.SQLITE_TEXT))
+            .assert('3' === st.get(0,api.SQLITE_TEXT))
             .assert('3' === st.getString(0))
-            .assert(3.0 === st.get(0,S.SQLITE_FLOAT))
+            .assert(3.0 === st.get(0,api.SQLITE_FLOAT))
             .assert(3.0 === st.getFloat(0))
-            .assert(st.get(0,S.SQLITE_BLOB) instanceof Uint8Array)
+            .assert(st.get(0,api.SQLITE_BLOB) instanceof Uint8Array)
             .assert(st.getBlob(0) instanceof Uint8Array)
             .assert(3 === st.get([])[0])
             .assert(3 === st.get({}).a)
@@ -73,7 +61,7 @@ INSERT INTO t(a,b) VALUES(1,2),(3,4),(?,?);`,
             bind: [5,6]
         });
         T.assert(2 === list.length);
-        log("Exec'd SQL:", list);
+        //log("Exec'd SQL:", list);
         let counter = 0, colNames = [];
         db.exec("SELECT a a, b b FROM t",{
             rowMode: 'object',
@@ -95,7 +83,9 @@ INSERT INTO t(a,b) VALUES(1,2),(3,4),(?,?);`,
             }
         });
         T.assert(6 === counter);
+    };
 
+    const testUDF = function(db){
         log("Testing UDF...");
         db.createFunction("foo",function(a,b){return a+b});
         T.assert(7===db.selectValue("select foo(3,4)")).
@@ -110,6 +100,8 @@ INSERT INTO t(a,b) VALUES(1,2),(3,4),(?,?);`,
                 return rc;
             }
         });
+
+        log("Testing DB::selectValue() w/ UDF...");
         T.assert(0===db.selectValue("select bar()")).
             assert(1===db.selectValue("select bar(1)")).
             assert(3===db.selectValue("select bar(1,2)")).
@@ -120,18 +112,34 @@ INSERT INTO t(a,b) VALUES(1,2),(3,4),(?,?);`,
             assert(null === db.selectValue("select ?",null)).
             assert(null === db.selectValue("select ?",[null])).
             assert(null === db.selectValue("select $a",{$a:null}));
-                 
-    }finally{
-        db.close();
-    }
-    log("Total Test count:",T.counter);
-};
+    };
+
+    const runTests = function(namespace){
+        T.assert(Module._free instanceof Function).
+            assert(Module.allocate instanceof Function).
+            assert(Module.addFunction instanceof Function).
+            assert(Module.removeFunction instanceof Function);
+        const api = namespace.api;
+        const oo = namespace.SQLite3;
+        console.log("Loaded module:",api.sqlite3_libversion(),
+                    api.sqlite3_sourceid());
+        log("Build options:",oo.compileOptionUsed());
+        const db = new oo.DB();
+        try {
+            log("DB:",db.filename);
+            [
+                test1, testUDF
+            ].forEach((f)=>f(db, api));
+        }finally{
+            db.close();
+        }
+        log("Total Test count:",T.counter);
+    };
 
-self/*window or worker*/.Module.postRun.push(function(theModule){
-    /** Use a timeout so that we are (hopefully) out from under the
-        module init stack when our setup gets run. */
-    
-    setTimeout(function(){
-        theModule.loadSqliteAPI(mainTest1);
-    },0);
-});
+    self.Module.postRun.push(function(theModule){
+        /** Use a timeout so that we are (hopefully) out from under the
+            module init stack when our setup gets run. Just on principle,
+            not because we _need_ to be. */
+        setTimeout(()=>theModule.loadSqliteAPI(runTests), 0);
+    });
+})(self/*window or worker*/);
index e9f9fc3988f9ecfffd0a5e24c0dfe81451c2422e..04cee010953fe694518b48c7b747ad0c504f4d9a 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C wasm/JS:\sminor\sdoc\supdates,\scorrected\sbind()ing\sof\sthe\sundefined\svalue\sto\sbehave\sas\sdocumented,\sremoved\ssome\ssuperfluous\scode.
-D 2022-05-24T01:15:21.052
+C fiddle:\slots\sof\sgeneric\srefactoring,\srestructuring,\sand\scleanup\sin\sthe\shigher-level\scode.\sAdded\spush-fiddle\sext/fiddle/Makefile\starget\sto\spush\sthe\sfiddle\sapp\sto\sa\sremote\sserver\svia\srsync.
+D 2022-05-24T14:36:45.563
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -58,17 +58,18 @@ F ext/expert/test_expert.c d56c194b769bdc90cf829a14c9ecbc1edca9c850b837a4d0b13be
 F ext/fiddle/EXPORTED_FUNCTIONS.fiddle 487fc7c83d45c48326f731c89162ed17ab15767e5efede8999d7d6c6e2d04c0f
 F ext/fiddle/EXPORTED_FUNCTIONS.sqlite3 07b573a1830cb2d38ed347cf2a4139ec3b9c0f69748da6a2d8356b426c807694
 F ext/fiddle/EXPORTED_RUNTIME_METHODS ff64aea52779b0d4a838268275fe02adf6f2fdf4d9ce21c22d104bf3d7597398
-F ext/fiddle/Makefile 9277c73e208b9c8093659256c9f07409c877e366480c7c22ec545ee345451d95
+F ext/fiddle/Makefile 8eb51a07b4ff7e5684ca829906233c07d0dccb5d14a9d8b4ec737a2a6f3f0d7e
 F ext/fiddle/SqliteTestUtil.js e3094833660a6ddd40766b802901b5861b37f0b89c6c577ee0ce4c9d36399e61
 F ext/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f
 F ext/fiddle/fiddle-worker.js e87c17070b979bd057a6849332f2a86660a4255ff7f1b6671e3e6026182ffd5a
 F ext/fiddle/fiddle.html 657c6c3f860c322fba3c69fa4f7a1209e2d2ce44b4bc65a3e154e3a97c047a7c
-F ext/fiddle/fiddle.js 68f5bb45fc1ae7f8ae3f6b85f465257db514d12bf50ec492259685178c452a88
+F ext/fiddle/fiddle.js 0263a1ebf7e09ecd8b37ff8e00b9ba27c543b65b6c3dbf2f9def90e6c71c4580
 F ext/fiddle/index.md d9c1c308d8074341bc3b11d1d39073cd77754cb3ca9aeb949f23fdd8323d81cf
-F ext/fiddle/sqlite3-api.js 3f41887a66d620ae506fea4a735d909c3dc0023045265736958de6d3016fbfc9
-F ext/fiddle/testing-common.js 723aada13d90a5ee3f0f8f5b5b88e46954becae5d2b04ded811d90106057f4ac
-F ext/fiddle/testing1.html 026502e5d5e6a250e4101f8e8948708a1295ce831a094d741839ecaf788d8533
-F ext/fiddle/testing1.js b9dd06fd02fbcf947794ceb0bcca1a00e3440d80bf1d819a73bbcac25c87086e
+F ext/fiddle/sqlite3-api.js 5492d48b4167179fd979fae99f0c21dc2d0f03460be9ff2d35e62225c58c4c9c
+F ext/fiddle/testing-common.js a2527fd8dfb500bad9b434ae2645bb91489792115ee1e1b4b53cac4e9198992a
+F ext/fiddle/testing.css 750572dded671d2cf142bbcb27af5542522ac08db128245d0b9fe410aa1d7f2a
+F ext/fiddle/testing1.html c00236d71b7f7523b722ae2f79cb2b734e6ed4ff16102fa69974145f6e2bfc95
+F ext/fiddle/testing1.js a2cee7ee12c2e1756e775125b0f9950dc5e5faeeeb4979c6d9894626d90cb5d9
 F ext/fts1/README.txt 20ac73b006a70bcfd80069bdaf59214b6cf1db5e
 F ext/fts1/ft_hash.c 3927bd880e65329bdc6f506555b228b28924921b
 F ext/fts1/ft_hash.h 06df7bba40dadd19597aa400a875dbc2fed705ea
@@ -1968,8 +1969,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 70f91fab825d365f505750acdb8d3ae532880c4cdb64d1e61bb21b24a115958b
-R aa6cc8d5c8c48a5dcba3187d74399b46
+P 526c8c728019b317624a93f6f07840ca524bca84e7c03ce5e86e38953146236f
+R 8d308c6cc994c31188b8c779dc67218e
 U stephan
-Z 0033b446f56e6779d5a11407c4e1444a
+Z 3a03c1dbb04138d0aaacfe24b6f85ef3
 # Remove this line to create a well-formed Fossil manifest.
index 7ae63bdd66ee7be11d57e50ff1db3224151b5a08..4f241efce6781971f8533a9cf3836eea9c345c03 100644 (file)
@@ -1 +1 @@
-526c8c728019b317624a93f6f07840ca524bca84e7c03ce5e86e38953146236f
\ No newline at end of file
+ed19fef3459499abb0a4a010f368b4576d6e068d930c8480446ea677ac87c1c1
\ No newline at end of file