vovkos / jancy

The first and only scripting language with safe pointer arithmetics, high level of ABI and source compatibility with C, spreadsheet-like reactive programming, built-in lexer generator, and more.
http://jancy.org
MIT License
61 stars 9 forks source link

SQLite extension #3

Closed mingodad closed 1 year ago

mingodad commented 3 years ago

After reading the documentation and looking at the source code I still do not have it clear how to create an extension for jancy like SQLite, there is any plan to have a builtin SQLite extension ?

Could the documentation have some simple example/tutorial of one extension ?

Thanks in advance for your great job so far !

mingodad commented 3 years ago

Also there is any sort of ffi functionality ? Another interesting extension would be libcurl.

vovkos commented 3 years ago

An SQLite extension is not currently on our TODO list. The development of Jancy is mostly driven by the IO Ninja project, and SQL is not really required there, so... However, a PR with an SQL extension would certainly be welcome.

Documentation is not enough, I know. A tutorial for the .jncx development and a general overview of Jancy extension architecture is absolutely required, but there is nothing available at the moment, sorry about that... At the same time, there are quite a few extensions available at ./src/ext/, both dynamic (.jncx) and static. You can try developing a .jncx for SQLite by analogy, and feel free to ask me anything.

mingodad commented 3 years ago

Thank you for reply ! Looking at jancy/src/jnc_ext/jnc_io_base and jnc_io_File I can see that there is some kind of definition duplication in the jnc folder io_File.jnc.

There is a tool that can generate the .jnc file from the .[h,cpp] file ?

Also it seems that only one global interpreter exists (like python GIL, ruby, ...)

vovkos commented 3 years ago

Looking at jancy/src/jnc_ext/jnc_io_base and jnc_io_File I can see that there is some kind of definition duplication in the jnc folder io_File.jnc.

Not quite sure which duplication you mean. Could you provide some details?

There is a tool that can generate the .jnc file from the .[h,cpp] file ?

There is no such tool, and it's not even planned. However, I'm considering a tool (or rather, a command-line switch for the jancy executable), which would work the other way around, i.e. generate .h and .cpp and a skeleton of implementation for an opaque class from a .jnc file. However, I don't think such a tool is an absolute necessity. I would start by taking the implementation of an existing extension class (e.g. io_File.jnc, jnc_io_File.h, jnc_io_File.cpp) and modifying it to your liking.

Also it seems that only one global interpreter exists (like python GIL, ruby, ...)

Jancy doesn't even have an interpreter, it JITs the source into native machine code.

mingodad commented 3 years ago

Looking through the documentation it's not clear the thin qualifier purpose, also how to pass pointer addresses like sqlite3 **ppDb and function pointers for callbacks like int (*callback)(void*,int,char**,char**), /* Callback function */ could someone give examples to the sqlite3 prototypes bellow ?

dynamiclib SQLite3Lib // will use jnc.DynamicLib behind the scene
{
    int sqlite3_open(
        const char thin* filename,   /* Database filename (UTF-8) */
        sqlite3 **ppDb          /* OUT: SQLite db handle */
        );
    int sqlite3_close(sqlite3*);

    int sqlite3_prepare(
        sqlite3 thin* db,            /* Database handle */
        const char thin* zSql,       /* SQL statement, UTF-8 encoded */
        int nByte,              /* Maximum length of zSql in bytes. */
        sqlite3_stmt **ppStmt,  /* OUT: Statement handle */
        const char **pzTail     /* OUT: Pointer to unused portion of zSql */
        );

    int sqlite3_bind_text(sqlite3_stmt*,int,const char*,int,void(*)(void*));
    int sqlite3_step(sqlite3_stmt*);
    const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol);
    int sqlite3_finalize(sqlite3_stmt *pStmt);

    int sqlite3_exec(
        sqlite3*,                                  /* An open database */
        const char *sql,                           /* SQL to be evaluated */
        int (*callback)(void*,int,char**,char**),  /* Callback function */
        void *,                                    /* 1st argument to callback */
        char **errmsg                              /* Error msg written here */
        );
}

//import "sys_Dynamic2.jnc";

typedef int cdecl Fprintf(
        char const thin* format,
        ...
        );

int main()
{
    printf("main ()\n");

    SQLite3Lib sqlite3_lib;
    sqlite3_lib.open("libsqlite3.so");

    jnc.DynamicLib lib;
    lib.open("libc.so.6");

    //sys.Dynamic2 lib2;
    //lib2.open("libc.so.6");

    Fprintf thin* tprintf;

    unsafe
    {
        tprintf = (Fprintf thin*) lib.getFunction("printf");
    }

    tprintf("c:/windows/media/tada.wav%d\n", 33);
    return 0;

catch:
    printf("error caught: %s\n", std.getLastError ().m_description);
    return -1;
}
mingodad commented 3 years ago

Ok I found in the docs a brief description of thin in https://vovkos.github.io/jancy/language/type_ptr_function.html?highlight=thin and after some new tries I'm still stuck with this error message:

Test.jnc(51,54): cannot convert from 'void thin* safe lean*' to 'void thin* thin* thin*'
Test.jnc(51,54): unable to recover from previous error(s)
2 error(s); compilation failed

Test.jnc:

typedef void thin* sqlite3;
typedef void thin* sqlite3_stmt;

dynamiclib SQLite3Lib // will use jnc.DynamicLib behind the scene
{
    int sqlite3_open(
        const char thin* filename,   /* Database filename (UTF-8) */
        sqlite3 thin*thin* ppDb          /* OUT: SQLite db handle */
        );
    int sqlite3_close(sqlite3 thin* pDb);

    int sqlite3_prepare(
        sqlite3 thin* pDb,            /* Database handle */
        const char thin* zSql,       /* SQL statement, UTF-8 encoded */
        int nByte,              /* Maximum length of zSql in bytes. */
        sqlite3_stmt thin* thin* ppStmt,  /* OUT: Statement handle */
        const char thin*thin*pzTail     /* OUT: Pointer to unused portion of zSql */
        );

//  int sqlite3_bind_text(sqlite3_stmt thin* pDb, int col, const char thin* value, int flags, void(*cb)(void*));
//  int sqlite3_step(sqlite3_stmt thin* pStmt);
//  const unsigned char *sqlite3_column_text(sqlite3_stmt thin* pStmt, int iCol);
//  int sqlite3_finalize(sqlite3_stmt thin* pStmt);
//
//  int sqlite3_exec(
//      sqlite3 thin* pDb,                                  /* An open database */
//      const char thin* sql,                           /* SQL to be evaluated */
//      int (*callback)(void*,int,char**,char**),  /* Callback function */
//      void thin*,                                    /* 1st argument to callback */
//      char **errmsg                              /* Error msg written here */
//      );
}

//import "sys_Dynamic2.jnc";

typedef int cdecl Fprintf(
        char const thin* format,
        ...
        );

int main()
{
    printf("main ()\n");

    SQLite3Lib sqlite3_lib;
    sqlite3_lib.open("libsqlite3.so");

    sqlite3 db;
    int rc = sqlite3_lib.lib.sqlite3_open("test.db", &db);
    printf("%p\n", db);

    jnc.DynamicLib lib;
    lib.open("libc.so.6");

    //sys.Dynamic2 lib2;
    //lib2.open("libc.so.6");

    Fprintf thin* tprintf;

    unsafe
    {
        tprintf = (Fprintf thin*) lib.getFunction("printf");
    }

    tprintf("c:/windows/media/tada.wav%d\n", 33);
    return 0;

catch:
    printf("error caught: %s\n", std.getLastError ().m_description);
    return -1;
}
vovkos commented 3 years ago

Pointers in Jancy are fat by default. Data pointers carry range & type metadata, function & property pointers carry a closure object -- basically, fat pointers occupy two-pointer slots. On the other hand, thin pointers are just like pointers in C/C++; whenever ABI compatibility with external C/C++ code is required, you need to employ thin pointers.

Now, to your code. It doesn't compile because you have an indirection level mismatch. When you declare a sqlite3 handle, you should do it like so:

sqlite3 thin* db;

Also, I would declare sqlite3 and sqlite3_stmt handle types like this:

struct sqlite3 { 
  protected construct() {}
}
struct sqlite3_stmt { 
  protected construct() {}
}

This way, the compiler will catch situations (a) when you use wrong handle types (e.g. try to use sqlite3 where sqlite3_stmt is expected) and (b) when you attempt to instantiate a handle instead of using a pointer to it (as you did in your snippet above).

mingodad commented 3 years ago

Thanks for reply ! I'm getting a bit closer to a functioning demo but I'm still having problem with sqlite3_column_text, I can insert data correctly on the database but when trying to retrieve sqlite3_column_text I'm not getting the correct data, I tested with the generated disk database with a scripting language and I'm getting back the correct data (see bellow).

Could someone give another help here ?

Output:

jancy "sqlite-dad.jnc"
main ()
3022000
0 : 0x1cb8db8
0

0 : exec
0 : prepare
101 : 101 : step
0 : finalize
0 : prepare
0 : bind_int
val = val_1
0 : bind_text
101 : step
0  : reset
0 : bind_int
val = val_2
0 : bind_text
101 : step
0  : reset
0 : bind_int
val = val_3
0 : bind_text
101 : step
0  : reset
0 : bind_int
val = val_4
0 : bind_text
101 : step
0  : reset
0 : bind_int
val = val_5
0 : bind_text
101 : step
0  : reset
0 : finalize
0 : prepare
Values 1 : 2 : 1 : 1   ///!!! here should be "val_1"
Values 2 : 2 : 1 : 2
Values 3 : 2 : 1 : 3
Values 4 : 2 : 1 : 4
Values 5 : 2 : 1 : 5
0 : finalize
0 : close
c:/windows/media/tada.wav33

struct sqlite3 {
  protected construct() {}
}
struct sqlite3_stmt {
  protected construct() {}
}

typedef  int64  sqlite3_int64;
typedef void sqlite3_destructor_cb(void thin*);
typedef int sqlite3_exec_cb(void thin*,int , char thin**, char thin**);

enum SQLite3DestructorType {
    SQLITE_STATIC  = 0, //    ((sqlite3_destructor_type)0)
    SQLITE_TRANSIENT  = -1, // ((sqlite3_destructor_type)-1)
};

enum SQLite3ErrorCode {
    SQLITE_OK =          0,   /* Successful result */
    /* beginning-of-error-codes */
    SQLITE_ERROR  =      1,   /* Generic error */
    SQLITE_INTERNAL =    2,   /* Internal logic error in SQLite */
    SQLITE_PERM  =       3,   /* Access permission denied */
    SQLITE_ABORT =       4,   /* Callback routine requested an abort */
    SQLITE_BUSY  =       5,   /* The database file is locked */
    SQLITE_LOCKED  =     6,   /* A table in the database is locked */
    SQLITE_NOMEM  =      7,   /* A malloc() failed */
    SQLITE_READONLY =    8,   /* Attempt to write a readonly database */
    SQLITE_INTERRUPT =   9,   /* Operation terminated by sqlite3_interrupt()*/
    SQLITE_IOERR  =     10,   /* Some kind of disk I/O error occurred */
    SQLITE_CORRUPT  =   11,   /* The database disk image is malformed */
    SQLITE_NOTFOUND =   12,   /* Unknown opcode in sqlite3_file_control() */
    SQLITE_FULL  =      13,   /* Insertion failed because database is full */
    SQLITE_CANTOPEN  =  14,   /* Unable to open the database file */
    SQLITE_PROTOCOL =   15,   /* Database lock protocol error */
    SQLITE_EMPTY  =     16,   /* Internal use only */
    SQLITE_SCHEMA  =    17,   /* The database schema changed */
    SQLITE_TOOBIG =     18,   /* String or BLOB exceeds size limit */
    SQLITE_CONSTRAINT = 19,   /* Abort due to constraint violation */
    SQLITE_MISMATCH  =  20,   /* Data type mismatch */
    SQLITE_MISUSE  =    21,   /* Library used incorrectly */
    SQLITE_NOLFS  =     22,   /* Uses OS features not supported on host */
    SQLITE_AUTH =       23,   /* Authorization denied */
    SQLITE_FORMAT  =    24,   /* Not used */
    SQLITE_RANGE  =     25,   /* 2nd parameter to sqlite3_bind out of range */
    SQLITE_NOTADB =     26,   /* File opened that is not a database file */
    SQLITE_NOTICE =     27,   /* Notifications from sqlite3_log() */
    SQLITE_WARNING =    28,   /* Warnings from sqlite3_log() */
    SQLITE_ROW =       100,  /* sqlite3_step() has another row ready */
    SQLITE_DONE  =      101,  /* sqlite3_step() has finished executing */
};

dynamiclib SQLite3Lib // will use jnc.DynamicLib behind the scene
{
    int sqlite3_libversion_number();
    int sqlite3_errcode(sqlite3 *db);
    int sqlite3_open(
        const char thin* filename,   /* Database filename (UTF-8) */
        sqlite3 ** ppDb          /* OUT: SQLite db handle */
        );
    int sqlite3_close(sqlite3 thin* pDb);

    const char thin* sqlite3_db_filename(sqlite3 thin* db, const char thin* zDbName);

    int sqlite3_prepare(
        sqlite3 thin* pDb,              /* Database handle */
        const char thin* zSql,       /* SQL statement, UTF-8 encoded */
        int nByte,                   /* Maximum length of zSql in bytes. */
        sqlite3_stmt ** ppStmt,  /* OUT: Statement handle */
        const char thin*thin*pzTail     /* OUT: Pointer to unused portion of zSql */
        );

    int sqlite3_prepare_v2(
        sqlite3 thin* db,            /* Database handle */
        const char thin* zSql,       /* SQL statement, UTF-8 encoded */
        int nByte,              /* Maximum length of zSql in bytes. */
        sqlite3_stmt **ppStmt,  /* OUT: Statement handle */
        const char thin*thin* pzTail     /* OUT: Pointer to unused portion of zSql */
        );

    int sqlite3_bind_text(sqlite3_stmt thin* pStmt, int col, const char thin* value, int flags, sqlite3_destructor_cb *cb);
    int sqlite3_bind_double(sqlite3_stmt thin*, int, double);
    int sqlite3_bind_int(sqlite3_stmt thin*, int, int);
    int sqlite3_bind_int64(sqlite3_stmt thin*, int, sqlite3_int64);
    int sqlite3_bind_null(sqlite3_stmtthin *, int);

    const void *sqlite3_column_blob(sqlite3_stmt*, int iCol);
    double sqlite3_column_double(sqlite3_stmt*, int iCol);
    int sqlite3_column_int(sqlite3_stmt*, int iCol);
    sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol);
    const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol);
    const void *sqlite3_column_text16(sqlite3_stmt*, int iCol);
    sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol);
    int sqlite3_column_bytes(sqlite3_stmt*, int iCol);
    int sqlite3_column_bytes16(sqlite3_stmt*, int iCol);
    int sqlite3_column_type(sqlite3_stmt*, int iCol);

    int sqlite3_column_count(sqlite3_stmt *pStmt);

    int sqlite3_step(sqlite3_stmt thin* pStmt);
    int sqlite3_reset(sqlite3_stmt thin*pStmt);
    int sqlite3_finalize(sqlite3_stmt thin*pStmt);

    int sqlite3_exec(
        sqlite3 thin* pDb,                                  /* An open database */
        const char thin* sql,                           /* SQL to be evaluated */
        sqlite3_exec_cb *cb,  /* Callback function */
        void *cb_arg,                                          /* 1st argument to callback */
        char thin**errmsg                              /* Error msg written here */
        );
}

//import "sys_Dynamic2.jnc";

typedef int cdecl Fprintf(
        char const thin* format,
        ...
        );

int main()
{
    printf("main ()\n");

    SQLite3Lib sqlite3_lib;
    //sqlite3_lib.open("/home/mingo/dev/dadbiz++/third-party/dad/sqlite3/.libs/libsqlite3.so");
    sqlite3_lib.open("libsqlite3.so");

    printf("%d\n", sqlite3_lib.lib.sqlite3_libversion_number());

    sqlite3 *db;
    //int rc = sqlite3_lib.lib.sqlite3_open("test.db", &db);
    int rc = sqlite3_lib.lib.sqlite3_open(":memory:", &db);
    printf("%d : %p\n", rc, db);

    printf("%d\n", sqlite3_lib.lib.sqlite3_errcode(db));
    printf("%s\n", sqlite3_lib.lib.sqlite3_db_filename(db, "main"));

    rc = sqlite3_lib.lib.sqlite3_exec(db, "create table test(id integer primary key, val text);", null, null, null);
    printf("%d : exec\n", rc);

    sqlite3_stmt *stmt = null;
    rc = sqlite3_lib.lib.sqlite3_prepare_v2(db, "create table test2(id integer primary key, val text);", -1, &stmt, null);
    printf("%d : prepare\n", rc);

    rc = sqlite3_lib.lib.sqlite3_step(stmt);
    printf("%d : %d : step\n", rc, SQLite3ErrorCode.SQLITE_DONE);

    rc = sqlite3_lib.lib.sqlite3_finalize(stmt);
    printf("%d : finalize\n", rc);

    rc = sqlite3_lib.lib.sqlite3_prepare_v2(db, "insert into test2(id, val) values(?, ?);", -1, &stmt, null);
    printf("%d : prepare\n", rc);

    for(int i=1; i <= 5; ++i) {
        rc = sqlite3_lib.lib.sqlite3_bind_int(stmt, 1, i);
        printf("%d : bind_int\n", rc);

        char const* strval = $"val_$i";
        printf("val = %s\n", strval);
        rc = sqlite3_lib.lib.sqlite3_bind_text(stmt, 2, strval, SQLite3DestructorType.SQLITE_TRANSIENT, null);
        printf("%d : bind_text\n", rc);

        rc = sqlite3_lib.lib.sqlite3_step(stmt);
        printf("%d : step\n", rc);

        rc = sqlite3_lib.lib.sqlite3_reset(stmt);
        printf("%d  : reset\n", rc);
    }

    rc = sqlite3_lib.lib.sqlite3_finalize(stmt);
    printf("%d : finalize\n", rc);

    rc = sqlite3_lib.lib.sqlite3_prepare_v2(db, "select * from test2;", -1, &stmt, null);
    printf("%d : prepare\n", rc);

    int col_count = sqlite3_lib.lib.sqlite3_column_count(stmt);
    while ((rc =  sqlite3_lib.lib.sqlite3_step(stmt)) == SQLite3ErrorCode.SQLITE_ROW) {
        int id = sqlite3_lib.lib.sqlite3_column_int(stmt, 0);
        int val_sz = sqlite3_lib.lib.sqlite3_column_bytes(stmt, 1);
        const unsigned char *val = sqlite3_lib.lib.sqlite3_column_text(stmt, 1);
        printf("Values %d : %d : %d : %s\n", id, col_count, val_sz, val);
    }

    rc = sqlite3_lib.lib.sqlite3_finalize(stmt);
    printf("%d : finalize\n", rc);

    rc = sqlite3_lib.lib.sqlite3_close(db);
    printf("%d : close\n", rc);

    jnc.DynamicLib lib;
    lib.open("libc.so.6");

    //sys.Dynamic2 lib2;
    //lib2.open("libc.so.6");

    Fprintf thin* tprintf;

    unsafe
    {
        tprintf = (Fprintf thin*) lib.getFunction("printf");
    }

    tprintf("c:/windows/media/tada.wav%d\n", 33);
    return 0;

catch:
    printf("error caught: %s\n", std.getLastError ().m_description);
    return -1;
}

Test disk database (sqlite-dad.nut):

auto db = SQLite3("test.db");

auto stmt = db.prepare("select * from test2;");
local rc;
while(stmt.next_row()) {
    print(stmt.col(0), stmt.col(1));
}

Output:

squilu "sqlite-dad.nut"
1   val_1
2   val_2
3   val_3
4   val_4
5   val_5
mingodad commented 3 years ago

There is something weird because only adding thin to the definitions gives the expected result, but jancy do not give any clue/warning/error message:

    const void *sqlite3_column_blob(sqlite3_stmt thin* , int iCol);
    double sqlite3_column_double(sqlite3_stmt thin* , int iCol);
    int sqlite3_column_int(sqlite3_stmt thin* , int iCol);
    sqlite3_int64 sqlite3_column_int64(sqlite3_stmt thin* , int iCol);
    const unsigned char *sqlite3_column_text(sqlite3_stmt thin* , int iCol);
    const void *sqlite3_column_text16(sqlite3_stmt thin* , int iCol);
    sqlite3_value *sqlite3_column_value(sqlite3_stmt thin* , int iCol);
    int sqlite3_column_bytes(sqlite3_stmt thin* pStmt, int iCol);
    int sqlite3_column_bytes16(sqlite3_stmt thin* pStmt, int iCol);
    int sqlite3_column_type(sqlite3_stmt thin* pStmt, int iCol);
mingodad commented 3 years ago

Now I need help to figure out how to pass a jancy function as callback (sqlite3_exec_mycb): Output:

jancy "sqlite-dad.jnc"
main ()
3022000
0 : 0x23707a8
0

0 : exec
0 : prepare
101 : 101 : step
0 : finalize
0 : prepare
0 : bind_int
val = val_1
0 : bind_text
101 : step
0  : reset
0 : bind_int
val = val_2
0 : bind_text
101 : step
0  : reset
0 : bind_int
val = val_3
0 : bind_text
101 : step
0  : reset
0 : bind_int
val = val_4
0 : bind_text
101 : step
0  : reset
0 : bind_int
val = val_5
0 : bind_text
101 : step
0  : reset
0 : finalize
0 : prepare
Values 1 : 2 : 5 : val_1
Values 2 : 2 : 5 : val_2
Values 3 : 2 : 5 : val_3
Values 4 : 2 : 5 : val_4
Values 5 : 2 : 5 : val_5
0 : finalize
error caught: 'rc' is not found or not a type ///!!! how to get the callback to work ????

sqlite-dad.jnc:

struct sqlite3 {
  protected construct() {}
}
struct sqlite3_stmt {
  protected construct() {}
}

typedef  int64  sqlite3_int64;
typedef void sqlite3_destructor_cb(void thin*);
typedef int sqlite3_exec_cb(void thin*,int , char thin*thin*, char thin*thin*);

enum SQLite3DestructorType {
    SQLITE_STATIC  = 0, //    ((sqlite3_destructor_type)0)
    SQLITE_TRANSIENT  = -1, // ((sqlite3_destructor_type)-1)
};

enum SQLite3ErrorCode {
    SQLITE_OK =          0,   /* Successful result */
    /* beginning-of-error-codes */
    SQLITE_ERROR  =      1,   /* Generic error */
    SQLITE_INTERNAL =    2,   /* Internal logic error in SQLite */
    SQLITE_PERM  =       3,   /* Access permission denied */
    SQLITE_ABORT =       4,   /* Callback routine requested an abort */
    SQLITE_BUSY  =       5,   /* The database file is locked */
    SQLITE_LOCKED  =     6,   /* A table in the database is locked */
    SQLITE_NOMEM  =      7,   /* A malloc() failed */
    SQLITE_READONLY =    8,   /* Attempt to write a readonly database */
    SQLITE_INTERRUPT =   9,   /* Operation terminated by sqlite3_interrupt()*/
    SQLITE_IOERR  =     10,   /* Some kind of disk I/O error occurred */
    SQLITE_CORRUPT  =   11,   /* The database disk image is malformed */
    SQLITE_NOTFOUND =   12,   /* Unknown opcode in sqlite3_file_control() */
    SQLITE_FULL  =      13,   /* Insertion failed because database is full */
    SQLITE_CANTOPEN  =  14,   /* Unable to open the database file */
    SQLITE_PROTOCOL =   15,   /* Database lock protocol error */
    SQLITE_EMPTY  =     16,   /* Internal use only */
    SQLITE_SCHEMA  =    17,   /* The database schema changed */
    SQLITE_TOOBIG =     18,   /* String or BLOB exceeds size limit */
    SQLITE_CONSTRAINT = 19,   /* Abort due to constraint violation */
    SQLITE_MISMATCH  =  20,   /* Data type mismatch */
    SQLITE_MISUSE  =    21,   /* Library used incorrectly */
    SQLITE_NOLFS  =     22,   /* Uses OS features not supported on host */
    SQLITE_AUTH =       23,   /* Authorization denied */
    SQLITE_FORMAT  =    24,   /* Not used */
    SQLITE_RANGE  =     25,   /* 2nd parameter to sqlite3_bind out of range */
    SQLITE_NOTADB =     26,   /* File opened that is not a database file */
    SQLITE_NOTICE =     27,   /* Notifications from sqlite3_log() */
    SQLITE_WARNING =    28,   /* Warnings from sqlite3_log() */
    SQLITE_ROW =       100,  /* sqlite3_step() has another row ready */
    SQLITE_DONE  =      101,  /* sqlite3_step() has finished executing */
};

dynamiclib SQLite3Lib // will use jnc.DynamicLib behind the scene
{
    int sqlite3_libversion_number();
    int sqlite3_errcode(sqlite3 *db);
    int sqlite3_open(
        const char thin* filename,   /* Database filename (UTF-8) */
        sqlite3 ** ppDb          /* OUT: SQLite db handle */
        );
    int sqlite3_close(sqlite3 thin* pDb);

    const char thin* sqlite3_db_filename(sqlite3 thin* db, const char thin* zDbName);

    int sqlite3_prepare(
        sqlite3 thin* pDb,              /* Database handle */
        const char thin* zSql,       /* SQL statement, UTF-8 encoded */
        int nByte,                   /* Maximum length of zSql in bytes. */
        sqlite3_stmt ** ppStmt,  /* OUT: Statement handle */
        const char thin*thin*pzTail     /* OUT: Pointer to unused portion of zSql */
        );

    int sqlite3_prepare_v2(
        sqlite3 thin* db,            /* Database handle */
        const char thin* zSql,       /* SQL statement, UTF-8 encoded */
        int nByte,              /* Maximum length of zSql in bytes. */
        sqlite3_stmt **ppStmt,  /* OUT: Statement handle */
        const char thin*thin* pzTail     /* OUT: Pointer to unused portion of zSql */
        );

    int sqlite3_bind_text(sqlite3_stmt thin* pStmt, int col, const char thin* value, int flags, sqlite3_destructor_cb *cb);
    int sqlite3_bind_double(sqlite3_stmt thin*, int, double);
    int sqlite3_bind_int(sqlite3_stmt thin*, int, int);
    int sqlite3_bind_int64(sqlite3_stmt thin*, int, sqlite3_int64);
    int sqlite3_bind_null(sqlite3_stmtthin *, int);

    const void *sqlite3_column_blob(sqlite3_stmt thin* , int iCol);
    double sqlite3_column_double(sqlite3_stmt thin* , int iCol);
    int sqlite3_column_int(sqlite3_stmt thin* , int iCol);
    sqlite3_int64 sqlite3_column_int64(sqlite3_stmt thin* , int iCol);
    const unsigned char *sqlite3_column_text(sqlite3_stmt thin* , int iCol);
    const void *sqlite3_column_text16(sqlite3_stmt thin* , int iCol);
    sqlite3_value *sqlite3_column_value(sqlite3_stmt thin* , int iCol);
    int sqlite3_column_bytes(sqlite3_stmt thin* pStmt, int iCol);
    int sqlite3_column_bytes16(sqlite3_stmt thin* pStmt, int iCol);
    int sqlite3_column_type(sqlite3_stmt thin* pStmt, int iCol);

    int sqlite3_column_count(sqlite3_stmt *pStmt);

    int sqlite3_step(sqlite3_stmt thin* pStmt);
    int sqlite3_reset(sqlite3_stmt thin*pStmt);
    int sqlite3_finalize(sqlite3_stmt thin*pStmt);

    int sqlite3_exec(
        sqlite3 thin* pDb,                                  /* An open database */
        const char thin* sql,                           /* SQL to be evaluated */
        sqlite3_exec_cb *cb,  /* Callback function */
        void thin* cb_arg,                                          /* 1st argument to callback */
        char thin**errmsg                              /* Error msg written here */
        );
}

//import "sys_Dynamic2.jnc";

typedef int cdecl Fprintf(
        char const thin* format,
        ...
        );

int sqlite3_exec_mycb(void thin* arg, int  col_count, char thin*thin* col_values, char thin*thin* col_names) {
    for(int i=0; i < col_count; ++i) {
        printf("%d : %d : %s : %s\n", col_count, i, col_names[i], col_values[i]);
    }
    return 0;
}

int main()
{
    printf("main ()\n");

    SQLite3Lib sqlite3_lib;
    sqlite3_lib.open("libsqlite3.so");

    printf("%d\n", sqlite3_lib.lib.sqlite3_libversion_number());

    sqlite3 *db;
    //int rc = sqlite3_lib.lib.sqlite3_open("test.db", &db);
    int rc = sqlite3_lib.lib.sqlite3_open(":memory:", &db);
    printf("%d : %p\n", rc, db);

    printf("%d\n", sqlite3_lib.lib.sqlite3_errcode(db));
    printf("%s\n", sqlite3_lib.lib.sqlite3_db_filename(db, "main"));

    rc = sqlite3_lib.lib.sqlite3_exec(db, "create table test(id integer primary key, val text);", null, null, null);
    printf("%d : exec\n", rc);

    sqlite3_stmt *stmt = null;
    rc = sqlite3_lib.lib.sqlite3_prepare_v2(db, "create table test2(id integer primary key, val text);", -1, &stmt, null);
    printf("%d : prepare\n", rc);

    rc = sqlite3_lib.lib.sqlite3_step(stmt);
    printf("%d : %d : step\n", rc, SQLite3ErrorCode.SQLITE_DONE);

    rc = sqlite3_lib.lib.sqlite3_finalize(stmt);
    printf("%d : finalize\n", rc);

    rc = sqlite3_lib.lib.sqlite3_prepare_v2(db, "insert into test2(id, val) values(?, ?);", -1, &stmt, null);
    printf("%d : prepare\n", rc);

    for(int i=1; i <= 5; ++i) {
        rc = sqlite3_lib.lib.sqlite3_bind_int(stmt, 1, i);
        printf("%d : bind_int\n", rc);

        char const* strval = $"val_$i";
        printf("val = %s\n", strval);
        rc = sqlite3_lib.lib.sqlite3_bind_text(stmt, 2, strval, SQLite3DestructorType.SQLITE_TRANSIENT, null);
        printf("%d : bind_text\n", rc);

        rc = sqlite3_lib.lib.sqlite3_step(stmt);
        printf("%d : step\n", rc);

        rc = sqlite3_lib.lib.sqlite3_reset(stmt);
        printf("%d  : reset\n", rc);
    }

    rc = sqlite3_lib.lib.sqlite3_finalize(stmt);
    printf("%d : finalize\n", rc);

    rc = sqlite3_lib.lib.sqlite3_prepare_v2(db, "select * from test2;", -1, &stmt, null);
    printf("%d : prepare\n", rc);

    int col_count = sqlite3_lib.lib.sqlite3_column_count(stmt);
    while ((rc =  sqlite3_lib.lib.sqlite3_step(stmt)) == SQLite3ErrorCode.SQLITE_ROW) {
        int id = sqlite3_lib.lib.sqlite3_column_int(stmt, 0);
        int val_sz = sqlite3_lib.lib.sqlite3_column_bytes(stmt, 1);
        const unsigned char *val = sqlite3_lib.lib.sqlite3_column_text(stmt, 1);
        printf("Values %d : %d : %d : %s\n", id, col_count, val_sz, val);
    }

    rc = sqlite3_lib.lib.sqlite3_finalize(stmt);
    printf("%d : finalize\n", rc);

    rc = sqlite3_lib.lib.sqlite3_exec(db, "select * from test2;", sqlite3_exec_mycb, null, null);
    printf("%d : exec\n", rc);

    rc = sqlite3_lib.lib.sqlite3_close(db);
    printf("%d : close\n", rc);

    jnc.DynamicLib lib;
    lib.open("libc.so.6");

    //sys.Dynamic2 lib2;
    //lib2.open("libc.so.6");

    Fprintf thin* tprintf;

    unsafe
    {
        tprintf = (Fprintf thin*) lib.getFunction("printf");
    }

    tprintf("c:/windows/media/tada.wav%d\n", 33);
    return 0;

catch:
    printf("error caught: %s\n", std.getLastError ().m_description);
    return -1;
}
mingodad commented 3 years ago

I changed for debug the jancy callback function and can see that it's called but with absurd high values for col_count (rubbish):

int sqlite3_exec_mycb(void thin* arg, int  col_count, char thin*thin* col_values, char thin*thin* col_names) {
    for(int i=0; i < col_count; ++i) {
        printf("%d : %d\n", col_count, i);
        //printf("%d : %d : %s : %s\n", col_count, i, col_names[i], col_values[i]);
    }
    return 0;
}

Output:

...
13906264 : 0
13906264 : 1
13906264 : 2
13906264 : 3
13906264 : 4
13906264 : 5
...
vovkos commented 3 years ago

As I said earlier, function pointers are fat by default, too; if you need to pass a callback function pointers to the external C/C++ code, declare the function pointer parameter as thin:

sqlite3_exec_cb thin* cb,  /* Callback function */
vovkos commented 3 years ago

There is something weird because only adding thin to the definitions gives the expected result, but jancy do not give any clue/warning/error message:

Well, how Jancy would know what is your intention? Both fat and thin pointers are part of the language and have their respective domains of use.

mingodad commented 3 years ago

Well, how Jancy would know what is your intention? Both fat and thin pointers are part of the language and have their respective domains of use.

How about using something like the C++ compilers do (external "C" {}) so no need to annotate every pointer there since all of then will be thin and maybe also be a bit nut (dynamiclib "C++" some cppLib) and have some predefined mangle/demalngle functions per platform.

dynamiclib "C" SQLite3Lib // will use jnc.DynamicLib behind the scene
{
...
}
mingodad commented 3 years ago

Also to help debug something like this https://github.com/integer32llc/rust-playground/issues/15 and this https://github.com/rust-lang/rust/pull/42971 would be nice !

mingodad commented 3 years ago

I'm getting closer to understanding all the bits to create a proper SQLite3 wrapper in jancy, I'm having trouble now with casting an intptr to void function* (void thin*) when uncommenting the registration of the sqlite3 function sqlite3_xFuncHello as per the documentation of sqlite3 we can pass the special value SQLITE_TRANSIENT = -1 as function pointer for the xDestroy.

Also if there is any undefined type inside dynamiclib SQLite3Lib {} jancy segfaults (try to change sqlite3_xDestroy thin* //void(*xDestroy)(void*) by xxsqlite3_xDestroy thin* //void(*xDestroy)(void*) to see the segfault).

sqlite-dad.jnc(199,135): cannot convert from 'long const' to 'void function* (void thin*)'

Full demo sqlite-dad.jnc:


struct sqlite3 {
  protected construct() {}
}
struct sqlite3_stmt {
  protected construct() {}
}
struct sqlite3_value {
  protected construct() {}
}
struct sqlite3_context {
  protected construct() {}
}

typedef  int64_t  sqlite3_int64;
typedef void sqlite3_destructor_cb(thin*);
typedef const char thin*thin* ccharPtrPtr;
typedef int sqlite3_exec_cb(thin* arg,int col_count, ccharPtrPtr col_values, ccharPtrPtr col_names);

typedef void sqlite3_xFunc(sqlite3_context thin *ctx, int argc, sqlite3_value thin*thin* argv);
typedef void sqlite3_xStep(sqlite3_context thin*,int,sqlite3_value thin*thin*);
typedef void sqlite3_xFinal(sqlite3_context thin*);
typedef void sqlite3_xDestroy(void thin*);

enum SQLite3DestructorType {
    SQLITE_STATIC  = 0, //    ((sqlite3_destructor_type)0)
    SQLITE_TRANSIENT  = -1, // ((sqlite3_destructor_type)-1)
};

enum SQLite3ErrorCode {
    SQLITE_OK =          0,   /* Successful result */
    /* beginning-of-error-codes */
    SQLITE_ERROR  =      1,   /* Generic error */
    SQLITE_INTERNAL =    2,   /* Internal logic error in SQLite */
    SQLITE_PERM  =       3,   /* Access permission denied */
    SQLITE_ABORT =       4,   /* Callback routine requested an abort */
    SQLITE_BUSY  =       5,   /* The database file is locked */
    SQLITE_LOCKED  =     6,   /* A table in the database is locked */
    SQLITE_NOMEM  =      7,   /* A malloc() failed */
    SQLITE_READONLY =    8,   /* Attempt to write a readonly database */
    SQLITE_INTERRUPT =   9,   /* Operation terminated by sqlite3_interrupt()*/
    SQLITE_IOERR  =     10,   /* Some kind of disk I/O error occurred */
    SQLITE_CORRUPT  =   11,   /* The database disk image is malformed */
    SQLITE_NOTFOUND =   12,   /* Unknown opcode in sqlite3_file_control() */
    SQLITE_FULL  =      13,   /* Insertion failed because database is full */
    SQLITE_CANTOPEN  =  14,   /* Unable to open the database file */
    SQLITE_PROTOCOL =   15,   /* Database lock protocol error */
    SQLITE_EMPTY  =     16,   /* Internal use only */
    SQLITE_SCHEMA  =    17,   /* The database schema changed */
    SQLITE_TOOBIG =     18,   /* String or BLOB exceeds size limit */
    SQLITE_CONSTRAINT = 19,   /* Abort due to constraint violation */
    SQLITE_MISMATCH  =  20,   /* Data type mismatch */
    SQLITE_MISUSE  =    21,   /* Library used incorrectly */
    SQLITE_NOLFS  =     22,   /* Uses OS features not supported on host */
    SQLITE_AUTH =       23,   /* Authorization denied */
    SQLITE_FORMAT  =    24,   /* Not used */
    SQLITE_RANGE  =     25,   /* 2nd parameter to sqlite3_bind out of range */
    SQLITE_NOTADB =     26,   /* File opened that is not a database file */
    SQLITE_NOTICE =     27,   /* Notifications from sqlite3_log() */
    SQLITE_WARNING =    28,   /* Warnings from sqlite3_log() */
    SQLITE_ROW =       100,  /* sqlite3_step() has another row ready */
    SQLITE_DONE  =      101,  /* sqlite3_step() has finished executing */
};

enum SQLite3TextEncoding {
    SQLITE_UTF8    =       1,    /* IMP: R-37514-35566 */
    SQLITE_UTF16LE  =      2,    /* IMP: R-03371-37637 */
    SQLITE_UTF16BE  =      3,    /* IMP: R-51971-34154 */
    SQLITE_UTF16   =       4,    /* Use native byte order */
    SQLITE_ANY  =          5,    /* Deprecated */
    SQLITE_UTF16_ALIGNED = 8,    /* sqlite3_create_collation only */
};

enum SQLite3FunctionFlags {
    SQLITE_DETERMINISTIC  =  0x000000800,
    SQLITE_DIRECTONLY  =     0x000080000,
    SQLITE_SUBTYPE  =        0x000100000,
    SQLITE_INNOCUOUS  =      0x000200000,
};

enum SQLite3Types {
    SQLITE_INTEGER = 1,
    SQLITE_FLOAT  =  2,
    SQLITE_TEXT =    3,
    SQLITE_BLOB =    4,
    SQLITE_NULL  =   5,
};

dynamiclib SQLite3Lib // will use jnc.DynamicLib behind the scene
{
    int sqlite3_libversion_number();
    int sqlite3_errcode(sqlite3 thin*db);
    int sqlite3_open(
        const char thin* filename,   /* Database filename (UTF-8) */
        sqlite3 ** ppDb          /* OUT: SQLite db handle */
        );
    int sqlite3_close(sqlite3 thin* pDb);

    const char thin* sqlite3_db_filename(sqlite3 thin* db, const char thin* zDbName);

    int sqlite3_prepare(
        sqlite3 thin* pDb,              /* Database handle */
        const char thin* zSql,       /* SQL statement, UTF-8 encoded */
        int nByte,                   /* Maximum length of zSql in bytes. */
        sqlite3_stmt ** ppStmt,  /* OUT: Statement handle */
        const char thin*thin*pzTail     /* OUT: Pointer to unused portion of zSql */
        );

    int sqlite3_prepare_v2(
        sqlite3 thin* db,            /* Database handle */
        const char thin* zSql,       /* SQL statement, UTF-8 encoded */
        int nByte,              /* Maximum length of zSql in bytes. */
        sqlite3_stmt **ppStmt,  /* OUT: Statement handle */
        const char thin*thin* pzTail     /* OUT: Pointer to unused portion of zSql */
        );

    int sqlite3_bind_text(sqlite3_stmt thin* pStmt, int col, const char thin* value, int flags, sqlite3_destructor_cb thin*cb);
    int sqlite3_bind_double(sqlite3_stmt thin*, int, double);
    int sqlite3_bind_int(sqlite3_stmt thin*, int, int);
    int sqlite3_bind_int64(sqlite3_stmt thin*, int, sqlite3_int64);
    int sqlite3_bind_null(sqlite3_stmt thin *, int);

    const void * sqlite3_column_blob(sqlite3_stmt thin* , int iCol);
    double sqlite3_column_double(sqlite3_stmt thin* , int iCol);
    int sqlite3_column_int(sqlite3_stmt thin* , int iCol);
    sqlite3_int64 sqlite3_column_int64(sqlite3_stmt thin* , int iCol);
    const unsigned char * sqlite3_column_text(sqlite3_stmt thin* , int iCol);
    const void * sqlite3_column_text16(sqlite3_stmt thin* , int iCol);
    sqlite3_value *sqlite3_column_value(sqlite3_stmt thin* , int iCol);
    int sqlite3_column_bytes(sqlite3_stmt thin* pStmt, int iCol);
    int sqlite3_column_bytes16(sqlite3_stmt thin* pStmt, int iCol);
    int sqlite3_column_type(sqlite3_stmt thin* pStmt, int iCol);

    int sqlite3_column_count(sqlite3_stmt thin* pStmt);

    int sqlite3_step(sqlite3_stmt thin* pStmt);
    int sqlite3_reset(sqlite3_stmt thin*pStmt);
    int sqlite3_finalize(sqlite3_stmt thin*pStmt);

    int sqlite3_exec(
        sqlite3 thin* pDb,                                  /* An open database */
        const char thin* sql,                           /* SQL to be evaluated */
        sqlite3_exec_cb thin* cb,  /* Callback function */
        void thin* cb_arg,                                          /* 1st argument to callback */
        char thin**errmsg                              /* Error msg written here */
        );

    int sqlite3_create_function_v2(
        sqlite3 thin* db,
        const char thin* zFunctionName,
        int nArg,
        int eTextRep,
        void thin* pApp,
        sqlite3_xFunc thin*, //void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
        sqlite3_xStep thin*, //void (*xStep)(sqlite3_context*,int,sqlite3_value**),
        sqlite3_xFinal thin*, //void (*xFinal)(sqlite3_context*),
        sqlite3_xDestroy thin* //void(*xDestroy)(void*)
        );

    void *sqlite3_user_data(sqlite3_context thin*);
    void sqlite3_result_text(sqlite3_context thin*, const char thin*, int sz, sqlite3_xDestroy *);
    void sqlite3_result_int(sqlite3_context thin*, int);
    void sqlite3_result_int64(sqlite3_context thin*, sqlite3_int64);
    void sqlite3_result_double(sqlite3_context thin*, double);
    void sqlite3_result_null(sqlite3_context thin*);

    const void *sqlite3_value_blob(sqlite3_value thin*);
    double sqlite3_value_double(sqlite3_value thin*);
    int sqlite3_value_int(sqlite3_value thin*);
    sqlite3_int64 sqlite3_value_int64(sqlite3_value thin*);
    void *sqlite3_value_pointer(sqlite3_value*, const char thin*);
    const unsigned char *sqlite3_value_text(sqlite3_value thin*);

    int sqlite3_value_type(sqlite3_value thin*);
}

SQLite3Lib sqlite3_lib;

//import "sys_Dynamic2.jnc";

typedef int cdecl Fprintf(
        char const thin* format,
        ...
        );

int sqlite3_exec_mycb(void thin* arg, int  col_count, ccharPtrPtr col_values, ccharPtrPtr col_names) {
    for(int i=0; i < col_count; ++i) {
        //printf("%d : %d\n", col_count, i);
        printf("%d : %d : %s : %s\n", col_count, i, col_names[i], col_values[i]);
    }
    return 0;
}

void sqlite3_xFuncHello(sqlite3_context thin *ctx, int argc, sqlite3_value thin*thin* argv) {
    //SQLite3Lib *lib = sqlite3_user_data(ctx);
    if(argc == 1) {
        sqlite3_lib.lib.sqlite3_result_text(ctx, "Hello from Jancy", -1, (sqlite3_xDestroy*)((intptr)SQLite3DestructorType.SQLITE_TRANSIENT));
    }
}

void sqlite3_xFuncAdd2(sqlite3_context thin *ctx, int argc, sqlite3_value thin*thin* argv) {
    //SQLite3Lib *lib = sqlite3_user_data(ctx);
    if(argc == 2) {
        int a, b;
        if(sqlite3_lib.lib.sqlite3_value_type(argv[0]) == SQLite3Types.SQLITE_INTEGER) {
            a = sqlite3_lib.lib.sqlite3_value_int(argv[0]);
            if(sqlite3_lib.lib.sqlite3_value_type(argv[1]) == SQLite3Types.SQLITE_INTEGER) {
                b = sqlite3_lib.lib.sqlite3_value_int(argv[1]);
                sqlite3_lib.lib.sqlite3_result_int(ctx, a+b);
            }
        }
    }
}

int main()
{
    printf("main ()\n");

    //sqlite3_lib.open("/home/mingo/dev/dadbiz++/third-party/dad/sqlite3/.libs/libsqlite3.so");
    sqlite3_lib.open("libsqlite3.so");

    printf("%d\n", sqlite3_lib.lib.sqlite3_libversion_number());

    sqlite3 *db;
    //int rc = sqlite3_lib.lib.sqlite3_open("test.db", &db);
    int rc = sqlite3_lib.lib.sqlite3_open(":memory:", &db);
    printf("%d : %p\n", rc, db);

    printf("%d\n", sqlite3_lib.lib.sqlite3_errcode(db));
    printf("%s\n", sqlite3_lib.lib.sqlite3_db_filename(db, "main"));
/*
    rc = sqlite3_lib.lib.sqlite3_create_function_v2(db, "JancyHello", 2,
        SQLite3TextEncoding.SQLITE_UTF8 | SQLite3FunctionFlags.SQLITE_DETERMINISTIC,
        null, sqlite3_xFuncHello, null, null, null);
    printf("%d : create_function_v2\n", rc);
*/
    rc = sqlite3_lib.lib.sqlite3_create_function_v2(db, "JancyAdd2", 2,
        SQLite3TextEncoding.SQLITE_UTF8 | SQLite3FunctionFlags.SQLITE_DETERMINISTIC,
        null, sqlite3_xFuncAdd2, null, null, null);
    printf("%d : create_function_v2\n", rc);

    rc = sqlite3_lib.lib.sqlite3_exec(db, "create table test(id integer primary key, val text);", null, null, null);
    printf("%d : exec\n", rc);

    sqlite3_stmt *stmt = null;
    rc = sqlite3_lib.lib.sqlite3_prepare_v2(db, "create table test2(id integer primary key, val text);", -1, &stmt, null);
    printf("%d : prepare\n", rc);

    rc = sqlite3_lib.lib.sqlite3_step(stmt);
    printf("%d : %d : step\n", rc, SQLite3ErrorCode.SQLITE_DONE);

    rc = sqlite3_lib.lib.sqlite3_finalize(stmt);
    printf("%d : finalize\n", rc);

    rc = sqlite3_lib.lib.sqlite3_prepare_v2(db, "insert into test2(id, val) values(?, ?);", -1, &stmt, null);
    printf("%d : prepare\n", rc);

    for(int i=1; i <= 5; ++i) {
        rc = sqlite3_lib.lib.sqlite3_bind_int(stmt, 1, i);
        printf("%d : bind_int\n", rc);

        char const* strval = $"val_$i";
        printf("val = %s\n", strval);
        rc = sqlite3_lib.lib.sqlite3_bind_text(stmt, 2, strval, SQLite3DestructorType.SQLITE_TRANSIENT, null);
        printf("%d : bind_text\n", rc);

        rc = sqlite3_lib.lib.sqlite3_step(stmt);
        printf("%d : step\n", rc);

        rc = sqlite3_lib.lib.sqlite3_reset(stmt);
        printf("%d  : reset\n", rc);
    }

    rc = sqlite3_lib.lib.sqlite3_finalize(stmt);
    printf("%d : finalize\n", rc);

    rc = sqlite3_lib.lib.sqlite3_prepare_v2(db, "select * from test2;", -1, &stmt, null);
    printf("%d : prepare\n", rc);

    int col_count = sqlite3_lib.lib.sqlite3_column_count(stmt);
    while ((rc =  sqlite3_lib.lib.sqlite3_step(stmt)) == SQLite3ErrorCode.SQLITE_ROW) {
        int id = sqlite3_lib.lib.sqlite3_column_int(stmt, 0);
        int val_sz = sqlite3_lib.lib.sqlite3_column_bytes(stmt, 1);
        const unsigned char *val = sqlite3_lib.lib.sqlite3_column_text(stmt, 1);
        printf("Values %d : %d : %d : %s\n", id, col_count, val_sz, val);
    }

    rc = sqlite3_lib.lib.sqlite3_finalize(stmt);
    printf("%d : finalize\n", rc);

    rc = sqlite3_lib.lib.sqlite3_exec(db, "select * from test2;", sqlite3_exec_mycb, null, null);
    printf("%d : exec\n", rc);

    rc = sqlite3_lib.lib.sqlite3_exec(db, "select JancyAdd2(12, 45) as j2;", sqlite3_exec_mycb, null, null);
    printf("%d : exec\n", rc);

    rc = sqlite3_lib.lib.sqlite3_close(db);
    printf("%d : close\n", rc);

    jnc.DynamicLib lib;
    lib.open("libc.so.6");

    //sys.Dynamic2 lib2;
    //lib2.open("libc.so.6");

    Fprintf thin* tprintf;

    unsafe
    {
        tprintf = (Fprintf thin*) lib.getFunction("printf");
    }

    tprintf("c:/windows/media/tada.wav%d\n", 33);

    sqlite3_lib.close();
    lib.close();

    return 0;

catch:
    printf("error caught: %s\n", std.getLastError ().m_description);
    return -1;
}
mingodad commented 3 years ago

And here is the output of valgrind for a normal jancy execution:

valgrind jancy sqlite-dad.jnc 
==14733== Memcheck, a memory error detector
==14733== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==14733== Using Valgrind-3.17.0 and LibVEX; rerun with -h for copyright info
==14733== Command: jancy sqlite-dad.jnc
==14733== 
==14733== Conditional jump or move depends on uninitialised value(s)
==14733==    at 0x416CEC: axl::io::getSymbolicLinkTarget(axl::sl::StringBase<char, axl::sl::StringDetailsBase<char> >*, axl::sl::StringRefBase<char, axl::sl::StringDetailsBase<char> > const&) (in /home/mingo/local/jancy/bin/jancy)
==14733==    by 0x415D8E: axl::io::getExeFilePath() (in /home/mingo/local/jancy/bin/jancy)
==14733==    by 0x415E56: axl::io::getExeDir() (in /home/mingo/local/jancy/bin/jancy)
==14733==    by 0x40C639: JncApp::JncApp(CmdLine*) (in /home/mingo/local/jancy/bin/jancy)
==14733==    by 0x4039E2: main (in /home/mingo/local/jancy/bin/jancy)
==14733== 
main ()
3022000
0 : 0x9d91c88
0

0 : create_function_v2
0 : exec
0 : prepare
101 : 101 : step
0 : finalize
0 : prepare
0 : bind_int
val = val_1
0 : bind_text
101 : step
0  : reset
0 : bind_int
val = val_2
0 : bind_text
101 : step
0  : reset
0 : bind_int
val = val_3
0 : bind_text
101 : step
0  : reset
0 : bind_int
val = val_4
0 : bind_text
101 : step
0  : reset
0 : bind_int
val = val_5
0 : bind_text
101 : step
0  : reset
0 : finalize
0 : prepare
Values 1 : 2 : 5 : val_1
Values 2 : 2 : 5 : val_2
Values 3 : 2 : 5 : val_3
Values 4 : 2 : 5 : val_4
Values 5 : 2 : 5 : val_5
0 : finalize
2 : 0 : id : 1
2 : 1 : val : val_1
2 : 0 : id : 2
2 : 1 : val : val_2
2 : 0 : id : 3
2 : 1 : val : val_3
2 : 0 : id : 4
2 : 1 : val : val_4
2 : 0 : id : 5
2 : 1 : val : val_5
0 : exec
1 : 0 : j2 : 57
0 : exec
0 : close
c:/windows/media/tada.wav33
==14733== 
==14733== HEAP SUMMARY:
==14733==     in use at exit: 150,223 bytes in 1,274 blocks
==14733==   total heap usage: 54,211 allocs, 52,937 frees, 16,990,125 bytes allocated
==14733== 
==14733== LEAK SUMMARY:
==14733==    definitely lost: 0 bytes in 0 blocks
==14733==    indirectly lost: 0 bytes in 0 blocks
==14733==      possibly lost: 0 bytes in 0 blocks
==14733==    still reachable: 150,223 bytes in 1,274 blocks
==14733==         suppressed: 0 bytes in 0 blocks
==14733== Rerun with --leak-check=full to see details of leaked memory
==14733== 
==14733== Use --track-origins=yes to see where uninitialised values come from
==14733== For lists of detected and suppressed errors, rerun with: -s
==14733== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
vovkos commented 3 years ago

I'm having trouble now with casting an intptr to void function (void thin) when uncommenting the registration of the sqlite3 function sqlite3_xFuncHello as per the documentation of sqlite3 we can pass the special value SQLITE_TRANSIENT = -1 as function pointer for the xDestroy.

You can use this trick:

sqlite3_xDestroy thin* destroy;
*(intptr thin)*destroy = SQLite3DestructorType.SQLITE_TRANSIENT;

Also, be sure to declare the function pointer as thin:

void sqlite3_result_text(sqlite3_context thin*, const char thin*, int sz, sqlite3_xDestroy *);
vovkos commented 3 years ago

Also if there is any undefined type inside dynamiclib SQLite3Lib {} jancy segfaults (try to change sqlite3_xDestroy thin //void(xDestroy)(void) by xxsqlite3_xDestroy thin //void(xDestroy)(void) to see the segfault).

Didn't work for me, I got an unresolved type error -- as expected.

Could you share a code snippet that crashes the compiler?

vovkos commented 3 years ago

And here is the output of valgrind for a normal jancy execution:

Looked into it, and the

Conditional jump or move depends on uninitialised value(s)

is caused by checking if a byte in a buffer -- which happened to be a heap-allocated block, and hence, considered uninitialized by Valgrind -- is zero or not (to determine if a string is zero-terminated).

Bottom line -- it's a false alarm.

mingodad commented 3 years ago

Sorry ! But just because of checking an heap-allocated block, and hence, not initialized is not a false alarm and prone to random/unintended results.

vovkos commented 3 years ago

Sorry ! But just because of checking an heap-allocated block, and hence, not initialized is not a false alarm and prone to random/unintended results.

Nah, no unexpected/unintended results in this case. We are checking inside a valid range. This block of data might be (just) heap-allocated, or shared, or on-stack, or static. If a byte at the specified offset is zero, the string is zero-terminated, and we are done. If the byte is NOT zero and the buffer is shared we need to do a re-allocation (to ensure zero-termination).

mingodad commented 3 years ago
sqlite3_xDestroy thin* destroy;
*(intptr thin)*destroy = SQLite3DestructorType.SQLITE_TRANSIENT;  ///!!! position 15 is just after "thin)"

jancy "sqlite-dad.jnc" /home/mingo/dev/c/A_programming-languages/jancy_b/sqlite-dad.jnc(261,15): unused modifier 'thin' 1 error(s); compilation failed

vovkos commented 3 years ago

Oh, there's a typo. My bad.

sqlite3_xDestroy thin* destroy;
*(intptr thin*)destroy = SQLite3DestructorType.SQLITE_TRANSIENT;
mingodad commented 3 years ago
jancy "sqlite-dad.jnc"
/home/mingo/dev/c/A_programming-languages/jancy_b/sqlite-dad.jnc(261,25): 'void thin function* (void thin*)' to 'long thin*' cast is only permitted in unsafe regions
1 error(s); compilation failed
mingodad commented 3 years ago

With:

    sqlite3_xDestroy thin* destroy;
    unsafe {
        *(intptr thin*)destroy = SQLite3DestructorType.SQLITE_TRANSIENT;
    }

Output:

jancy "sqlite-dad.jnc"
main ()
3022000
0 : 0x15d4678
0

0 : create_function_v2
0 : exec
0 : prepare
101 : 101 : step
0 : finalize
0 : prepare
error caught: 'rc' is not found or not a type

Commenting it:

    //sqlite3_xDestroy thin* destroy;
    ((unsafe {
    //  *(intptr thin*)destroy = SQLite3DestructorType.SQLITE_TRANSIENT;
    //}

Output is ok, working code:


struct sqlite3 {
  protected construct() {}
}
struct sqlite3_stmt {
  protected construct() {}
}
struct sqlite3_value {
  protected construct() {}
}
struct sqlite3_context {
  protected construct() {}
}

typedef  int64_t  sqlite3_int64;
typedef void sqlite3_destructor_cb(thin*);
typedef const char thin*thin* ccharPtrPtr;
typedef int sqlite3_exec_cb(thin* arg,int col_count, ccharPtrPtr col_values, ccharPtrPtr col_names);

typedef void sqlite3_xFunc(sqlite3_context thin *ctx, int argc, sqlite3_value thin*thin* argv);
typedef void sqlite3_xStep(sqlite3_context thin*,int,sqlite3_value thin*thin*);
typedef void sqlite3_xFinal(sqlite3_context thin*);
typedef void sqlite3_xDestroy(void thin*);

enum SQLite3DestructorType {
    SQLITE_STATIC  = 0, //    ((sqlite3_destructor_type)0)
    SQLITE_TRANSIENT  = -1, // ((sqlite3_destructor_type)-1)
};

enum SQLite3ErrorCode {
    SQLITE_OK =          0,   /* Successful result */
    /* beginning-of-error-codes */
    SQLITE_ERROR  =      1,   /* Generic error */
    SQLITE_INTERNAL =    2,   /* Internal logic error in SQLite */
    SQLITE_PERM  =       3,   /* Access permission denied */
    SQLITE_ABORT =       4,   /* Callback routine requested an abort */
    SQLITE_BUSY  =       5,   /* The database file is locked */
    SQLITE_LOCKED  =     6,   /* A table in the database is locked */
    SQLITE_NOMEM  =      7,   /* A malloc() failed */
    SQLITE_READONLY =    8,   /* Attempt to write a readonly database */
    SQLITE_INTERRUPT =   9,   /* Operation terminated by sqlite3_interrupt()*/
    SQLITE_IOERR  =     10,   /* Some kind of disk I/O error occurred */
    SQLITE_CORRUPT  =   11,   /* The database disk image is malformed */
    SQLITE_NOTFOUND =   12,   /* Unknown opcode in sqlite3_file_control() */
    SQLITE_FULL  =      13,   /* Insertion failed because database is full */
    SQLITE_CANTOPEN  =  14,   /* Unable to open the database file */
    SQLITE_PROTOCOL =   15,   /* Database lock protocol error */
    SQLITE_EMPTY  =     16,   /* Internal use only */
    SQLITE_SCHEMA  =    17,   /* The database schema changed */
    SQLITE_TOOBIG =     18,   /* String or BLOB exceeds size limit */
    SQLITE_CONSTRAINT = 19,   /* Abort due to constraint violation */
    SQLITE_MISMATCH  =  20,   /* Data type mismatch */
    SQLITE_MISUSE  =    21,   /* Library used incorrectly */
    SQLITE_NOLFS  =     22,   /* Uses OS features not supported on host */
    SQLITE_AUTH =       23,   /* Authorization denied */
    SQLITE_FORMAT  =    24,   /* Not used */
    SQLITE_RANGE  =     25,   /* 2nd parameter to sqlite3_bind out of range */
    SQLITE_NOTADB =     26,   /* File opened that is not a database file */
    SQLITE_NOTICE =     27,   /* Notifications from sqlite3_log() */
    SQLITE_WARNING =    28,   /* Warnings from sqlite3_log() */
    SQLITE_ROW =       100,  /* sqlite3_step() has another row ready */
    SQLITE_DONE  =      101,  /* sqlite3_step() has finished executing */
};

enum SQLite3TextEncoding {
    SQLITE_UTF8    =       1,    /* IMP: R-37514-35566 */
    SQLITE_UTF16LE  =      2,    /* IMP: R-03371-37637 */
    SQLITE_UTF16BE  =      3,    /* IMP: R-51971-34154 */
    SQLITE_UTF16   =       4,    /* Use native byte order */
    SQLITE_ANY  =          5,    /* Deprecated */
    SQLITE_UTF16_ALIGNED = 8,    /* sqlite3_create_collation only */
};

enum SQLite3FunctionFlags {
    SQLITE_DETERMINISTIC  =  0x000000800,
    SQLITE_DIRECTONLY  =     0x000080000,
    SQLITE_SUBTYPE  =        0x000100000,
    SQLITE_INNOCUOUS  =      0x000200000,
};

enum SQLite3Types {
    SQLITE_INTEGER = 1,
    SQLITE_FLOAT  =  2,
    SQLITE_TEXT =    3,
    SQLITE_BLOB =    4,
    SQLITE_NULL  =   5,
};

dynamiclib SQLite3Lib // will use jnc.DynamicLib behind the scene
{
    int sqlite3_libversion_number();
    int sqlite3_errcode(sqlite3 thin*db);
    int sqlite3_open(
        const char thin* filename,   /* Database filename (UTF-8) */
        sqlite3 ** ppDb          /* OUT: SQLite db handle */
        );
    int sqlite3_close(sqlite3 thin* pDb);

    const char thin* sqlite3_db_filename(sqlite3 thin* db, const char thin* zDbName);

    int sqlite3_prepare(
        sqlite3 thin* pDb,              /* Database handle */
        const char thin* zSql,       /* SQL statement, UTF-8 encoded */
        int nByte,                   /* Maximum length of zSql in bytes. */
        sqlite3_stmt ** ppStmt,  /* OUT: Statement handle */
        const char thin*thin*pzTail     /* OUT: Pointer to unused portion of zSql */
        );

    int sqlite3_prepare_v2(
        sqlite3 thin* db,            /* Database handle */
        const char thin* zSql,       /* SQL statement, UTF-8 encoded */
        int nByte,              /* Maximum length of zSql in bytes. */
        sqlite3_stmt **ppStmt,  /* OUT: Statement handle */
        const char thin*thin* pzTail     /* OUT: Pointer to unused portion of zSql */
        );

    int sqlite3_bind_text(sqlite3_stmt thin* pStmt, int col, const char thin* value, int flags, sqlite3_destructor_cb thin*cb);
    int sqlite3_bind_double(sqlite3_stmt thin*, int, double);
    int sqlite3_bind_int(sqlite3_stmt thin*, int, int);
    int sqlite3_bind_int64(sqlite3_stmt thin*, int, sqlite3_int64);
    int sqlite3_bind_null(sqlite3_stmt thin *, int);

    const void * sqlite3_column_blob(sqlite3_stmt thin* , int iCol);
    double sqlite3_column_double(sqlite3_stmt thin* , int iCol);
    int sqlite3_column_int(sqlite3_stmt thin* , int iCol);
    sqlite3_int64 sqlite3_column_int64(sqlite3_stmt thin* , int iCol);
    const unsigned char * sqlite3_column_text(sqlite3_stmt thin* , int iCol);
    const void * sqlite3_column_text16(sqlite3_stmt thin* , int iCol);
    sqlite3_value *sqlite3_column_value(sqlite3_stmt thin* , int iCol);
    int sqlite3_column_bytes(sqlite3_stmt thin* pStmt, int iCol);
    int sqlite3_column_bytes16(sqlite3_stmt thin* pStmt, int iCol);
    int sqlite3_column_type(sqlite3_stmt thin* pStmt, int iCol);

    int sqlite3_column_count(sqlite3_stmt thin* pStmt);

    int sqlite3_step(sqlite3_stmt thin* pStmt);
    int sqlite3_reset(sqlite3_stmt thin*pStmt);
    int sqlite3_finalize(sqlite3_stmt thin*pStmt);

    int sqlite3_exec(
        sqlite3 thin* pDb,                                  /* An open database */
        const char thin* sql,                           /* SQL to be evaluated */
        sqlite3_exec_cb thin* cb,  /* Callback function */
        void thin* cb_arg,                                          /* 1st argument to callback */
        char thin**errmsg                              /* Error msg written here */
        );

    int sqlite3_create_function_v2(
        sqlite3 thin* db,
        const char thin* zFunctionName,
        int nArg,
        int eTextRep,
        void thin* pApp,
        sqlite3_xFunc thin*, //void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
        sqlite3_xStep thin*, //void (*xStep)(sqlite3_context*,int,sqlite3_value**),
        sqlite3_xFinal thin*, //void (*xFinal)(sqlite3_context*),
        sqlite3_xDestroy thin* //void(*xDestroy)(void*)
        );

    void *sqlite3_user_data(sqlite3_context thin*);
    void sqlite3_result_text(sqlite3_context thin*, const char thin*, int sz, sqlite3_xDestroy *);
    void sqlite3_result_int(sqlite3_context thin*, int);
    void sqlite3_result_int64(sqlite3_context thin*, sqlite3_int64);
    void sqlite3_result_double(sqlite3_context thin*, double);
    void sqlite3_result_null(sqlite3_context thin*);

    const void *sqlite3_value_blob(sqlite3_value thin*);
    double sqlite3_value_double(sqlite3_value thin*);
    int sqlite3_value_int(sqlite3_value thin*);
    sqlite3_int64 sqlite3_value_int64(sqlite3_value thin*);
    void *sqlite3_value_pointer(sqlite3_value*, const char thin*);
    const unsigned char *sqlite3_value_text(sqlite3_value thin*);

    int sqlite3_value_type(sqlite3_value thin*);
}

SQLite3Lib sqlite3_lib;

//import "sys_Dynamic2.jnc";

typedef int cdecl Fprintf(
        char const thin* format,
        ...
        );

int sqlite3_exec_mycb(void thin* arg, int  col_count, ccharPtrPtr col_values, ccharPtrPtr col_names) {
    for(int i=0; i < col_count; ++i) {
        //printf("%d : %d\n", col_count, i);
        printf("%d : %d : %s : %s\n", col_count, i, col_names[i], col_values[i]);
    }
    return 0;
}

void sqlite3_xFuncHello(sqlite3_context thin *ctx, int argc, sqlite3_value thin*thin* argv) {
    //SQLite3Lib *lib = sqlite3_user_data(ctx);
    if(argc == 1) {
        sqlite3_lib.lib.sqlite3_result_text(ctx, "Hello from Jancy", -1, (sqlite3_xDestroy*)((intptr)SQLite3DestructorType.SQLITE_TRANSIENT));
    }
}

void sqlite3_xFuncAdd2(sqlite3_context thin *ctx, int argc, sqlite3_value thin*thin* argv) {
    //SQLite3Lib *lib = sqlite3_user_data(ctx);
    if(argc == 2) {
        int a, b;
        if(sqlite3_lib.lib.sqlite3_value_type(argv[0]) == SQLite3Types.SQLITE_INTEGER) {
            a = sqlite3_lib.lib.sqlite3_value_int(argv[0]);
            if(sqlite3_lib.lib.sqlite3_value_type(argv[1]) == SQLite3Types.SQLITE_INTEGER) {
                b = sqlite3_lib.lib.sqlite3_value_int(argv[1]);
                sqlite3_lib.lib.sqlite3_result_int(ctx, a+b);
            }
        }
    }
}

int main()
{
    printf("main ()\n");

    //sqlite3_lib.open("/home/mingo/dev/dadbiz++/third-party/dad/sqlite3/.libs/libsqlite3.so");
    sqlite3_lib.open("libsqlite3.so");

    printf("%d\n", sqlite3_lib.lib.sqlite3_libversion_number());

    sqlite3 *db;
    //int rc = sqlite3_lib.lib.sqlite3_open("test.db", &db);
    int rc = sqlite3_lib.lib.sqlite3_open(":memory:", &db);
    printf("%d : %p\n", rc, db);

    printf("%d\n", sqlite3_lib.lib.sqlite3_errcode(db));
    printf("%s\n", sqlite3_lib.lib.sqlite3_db_filename(db, "main"));
/*
    rc = sqlite3_lib.lib.sqlite3_create_function_v2(db, "JancyHello", 2,
        SQLite3TextEncoding.SQLITE_UTF8 | SQLite3FunctionFlags.SQLITE_DETERMINISTIC,
        null, sqlite3_xFuncHello, null, null, null);
    printf("%d : create_function_v2\n", rc);
*/
    rc = sqlite3_lib.lib.sqlite3_create_function_v2(db, "JancyAdd2", 2,
        SQLite3TextEncoding.SQLITE_UTF8 | SQLite3FunctionFlags.SQLITE_DETERMINISTIC,
        null, sqlite3_xFuncAdd2, null, null, null);
    printf("%d : create_function_v2\n", rc);

    rc = sqlite3_lib.lib.sqlite3_exec(db, "create table test(id integer primary key, val text);", null, null, null);
    printf("%d : exec\n", rc);

    sqlite3_stmt *stmt = null;
    rc = sqlite3_lib.lib.sqlite3_prepare_v2(db, "create table test2(id integer primary key, val text);", -1, &stmt, null);
    printf("%d : prepare\n", rc);

    rc = sqlite3_lib.lib.sqlite3_step(stmt);
    printf("%d : %d : step\n", rc, SQLite3ErrorCode.SQLITE_DONE);

    rc = sqlite3_lib.lib.sqlite3_finalize(stmt);
    printf("%d : finalize\n", rc);

    rc = sqlite3_lib.lib.sqlite3_prepare_v2(db, "insert into test2(id, val) values(?, ?);", -1, &stmt, null);
    printf("%d : prepare\n", rc);

    //sqlite3_xDestroy thin* destroy;
    //unsafe {
    //  *(intptr thin*)destroy = SQLite3DestructorType.SQLITE_TRANSIENT;
    //}

    for(int i=1; i <= 5; ++i) {
        rc = sqlite3_lib.lib.sqlite3_bind_int(stmt, 1, i);
        printf("%d : bind_int\n", rc);

        char const* strval = $"val_$i";
        printf("val = %s\n", strval);
        rc = sqlite3_lib.lib.sqlite3_bind_text(stmt, 2, strval, SQLite3DestructorType.SQLITE_TRANSIENT, null);
        printf("%d : bind_text\n", rc);

        rc = sqlite3_lib.lib.sqlite3_step(stmt);
        printf("%d : step\n", rc);

        rc = sqlite3_lib.lib.sqlite3_reset(stmt);
        printf("%d  : reset\n", rc);
    }

    rc = sqlite3_lib.lib.sqlite3_finalize(stmt);
    printf("%d : finalize\n", rc);

    rc = sqlite3_lib.lib.sqlite3_prepare_v2(db, "select * from test2;", -1, &stmt, null);
    printf("%d : prepare\n", rc);

    int col_count = sqlite3_lib.lib.sqlite3_column_count(stmt);
    while ((rc =  sqlite3_lib.lib.sqlite3_step(stmt)) == SQLite3ErrorCode.SQLITE_ROW) {
        int id = sqlite3_lib.lib.sqlite3_column_int(stmt, 0);
        int val_sz = sqlite3_lib.lib.sqlite3_column_bytes(stmt, 1);
        const unsigned char *val = sqlite3_lib.lib.sqlite3_column_text(stmt, 1);
        printf("Values %d : %d : %d : %s\n", id, col_count, val_sz, val);
    }

    rc = sqlite3_lib.lib.sqlite3_finalize(stmt);
    printf("%d : finalize\n", rc);

    rc = sqlite3_lib.lib.sqlite3_exec(db, "select * from test2;", sqlite3_exec_mycb, null, null);
    printf("%d : exec\n", rc);

    rc = sqlite3_lib.lib.sqlite3_exec(db, "select JancyAdd2(12, 45) as j2;", sqlite3_exec_mycb, null, null);
    printf("%d : exec\n", rc);

    rc = sqlite3_lib.lib.sqlite3_close(db);
    printf("%d : close\n", rc);

    jnc.DynamicLib lib;
    lib.open("libc.so.6");

    //sys.Dynamic2 lib2;
    //lib2.open("libc.so.6");

    Fprintf thin* tprintf;

    unsafe
    {
        tprintf = (Fprintf thin*) lib.getFunction("printf");
    }

    tprintf("c:/windows/media/tada.wav%d\n", 33);

    sqlite3_lib.close();
    lib.close();

    return 0;

catch:
    printf("error caught: %s\n", std.getLastError ().m_description);
    return -1;
}
vovkos commented 3 years ago

Sorry :) I should have tried it before posting. Of course, like this:

sqlite3_xDestroy thin* destroy;
*(intptr thin*)&destroy = SQLite3DestructorType.SQLITE_TRANSIENT;
mingodad commented 3 years ago

Thanks ! It's compiling now !

mingodad commented 3 years ago

Now it's almost work but when sqlite3 is calling jancy and jancy return a string I'm getting this error:

error caught: 'rc' is not found or not a type

Code:


struct sqlite3 {
  protected construct() {}
}
struct sqlite3_stmt {
  protected construct() {}
}
struct sqlite3_value {
  protected construct() {}
}
struct sqlite3_context {
  protected construct() {}
}

typedef  int64_t  sqlite3_int64;
typedef void sqlite3_destructor_cb(thin*);
typedef const char thin*thin* ccharPtrPtr;
typedef int sqlite3_exec_cb(thin* arg,int col_count, ccharPtrPtr col_values, ccharPtrPtr col_names);

typedef void sqlite3_xFunc(sqlite3_context thin *ctx, int argc, sqlite3_value thin*thin* argv);
typedef void sqlite3_xStep(sqlite3_context thin*,int,sqlite3_value thin*thin*);
typedef void sqlite3_xFinal(sqlite3_context thin*);
typedef void sqlite3_xDestroy(void thin*);

enum SQLite3DestructorType {
    SQLITE_STATIC  = 0, //    ((sqlite3_destructor_type)0)
    SQLITE_TRANSIENT  = -1, // ((sqlite3_destructor_type)-1)
};

enum SQLite3ErrorCode {
    SQLITE_OK =          0,   /* Successful result */
    /* beginning-of-error-codes */
    SQLITE_ERROR  =      1,   /* Generic error */
    SQLITE_INTERNAL =    2,   /* Internal logic error in SQLite */
    SQLITE_PERM  =       3,   /* Access permission denied */
    SQLITE_ABORT =       4,   /* Callback routine requested an abort */
    SQLITE_BUSY  =       5,   /* The database file is locked */
    SQLITE_LOCKED  =     6,   /* A table in the database is locked */
    SQLITE_NOMEM  =      7,   /* A malloc() failed */
    SQLITE_READONLY =    8,   /* Attempt to write a readonly database */
    SQLITE_INTERRUPT =   9,   /* Operation terminated by sqlite3_interrupt()*/
    SQLITE_IOERR  =     10,   /* Some kind of disk I/O error occurred */
    SQLITE_CORRUPT  =   11,   /* The database disk image is malformed */
    SQLITE_NOTFOUND =   12,   /* Unknown opcode in sqlite3_file_control() */
    SQLITE_FULL  =      13,   /* Insertion failed because database is full */
    SQLITE_CANTOPEN  =  14,   /* Unable to open the database file */
    SQLITE_PROTOCOL =   15,   /* Database lock protocol error */
    SQLITE_EMPTY  =     16,   /* Internal use only */
    SQLITE_SCHEMA  =    17,   /* The database schema changed */
    SQLITE_TOOBIG =     18,   /* String or BLOB exceeds size limit */
    SQLITE_CONSTRAINT = 19,   /* Abort due to constraint violation */
    SQLITE_MISMATCH  =  20,   /* Data type mismatch */
    SQLITE_MISUSE  =    21,   /* Library used incorrectly */
    SQLITE_NOLFS  =     22,   /* Uses OS features not supported on host */
    SQLITE_AUTH =       23,   /* Authorization denied */
    SQLITE_FORMAT  =    24,   /* Not used */
    SQLITE_RANGE  =     25,   /* 2nd parameter to sqlite3_bind out of range */
    SQLITE_NOTADB =     26,   /* File opened that is not a database file */
    SQLITE_NOTICE =     27,   /* Notifications from sqlite3_log() */
    SQLITE_WARNING =    28,   /* Warnings from sqlite3_log() */
    SQLITE_ROW =       100,  /* sqlite3_step() has another row ready */
    SQLITE_DONE  =      101,  /* sqlite3_step() has finished executing */
};

enum SQLite3TextEncoding {
    SQLITE_UTF8    =       1,    /* IMP: R-37514-35566 */
    SQLITE_UTF16LE  =      2,    /* IMP: R-03371-37637 */
    SQLITE_UTF16BE  =      3,    /* IMP: R-51971-34154 */
    SQLITE_UTF16   =       4,    /* Use native byte order */
    SQLITE_ANY  =          5,    /* Deprecated */
    SQLITE_UTF16_ALIGNED = 8,    /* sqlite3_create_collation only */
};

enum SQLite3FunctionFlags {
    SQLITE_DETERMINISTIC  =  0x000000800,
    SQLITE_DIRECTONLY  =     0x000080000,
    SQLITE_SUBTYPE  =        0x000100000,
    SQLITE_INNOCUOUS  =      0x000200000,
};

enum SQLite3Types {
    SQLITE_INTEGER = 1,
    SQLITE_FLOAT  =  2,
    SQLITE_TEXT =    3,
    SQLITE_BLOB =    4,
    SQLITE_NULL  =   5,
};

dynamiclib SQLite3Lib // will use jnc.DynamicLib behind the scene
{
    int sqlite3_libversion_number();
    int sqlite3_errcode(sqlite3 thin*db);
    int sqlite3_open(
        const char thin* filename,   /* Database filename (UTF-8) */
        sqlite3 ** ppDb          /* OUT: SQLite db handle */
        );
    int sqlite3_close(sqlite3 thin* pDb);

    const char thin* sqlite3_db_filename(sqlite3 thin* db, const char thin* zDbName);

    int sqlite3_prepare(
        sqlite3 thin* pDb,              /* Database handle */
        const char thin* zSql,       /* SQL statement, UTF-8 encoded */
        int nByte,                   /* Maximum length of zSql in bytes. */
        sqlite3_stmt ** ppStmt,  /* OUT: Statement handle */
        const char thin*thin*pzTail     /* OUT: Pointer to unused portion of zSql */
        );

    int sqlite3_prepare_v2(
        sqlite3 thin* db,            /* Database handle */
        const char thin* zSql,       /* SQL statement, UTF-8 encoded */
        int nByte,              /* Maximum length of zSql in bytes. */
        sqlite3_stmt **ppStmt,  /* OUT: Statement handle */
        const char thin*thin* pzTail     /* OUT: Pointer to unused portion of zSql */
        );

    int sqlite3_bind_text(sqlite3_stmt thin* pStmt, int col, const char thin* value, int flags, sqlite3_destructor_cb thin*cb);
    int sqlite3_bind_double(sqlite3_stmt thin*, int, double);
    int sqlite3_bind_int(sqlite3_stmt thin*, int, int);
    int sqlite3_bind_int64(sqlite3_stmt thin*, int, sqlite3_int64);
    int sqlite3_bind_null(sqlite3_stmt thin *, int);

    const void * sqlite3_column_blob(sqlite3_stmt thin* , int iCol);
    double sqlite3_column_double(sqlite3_stmt thin* , int iCol);
    int sqlite3_column_int(sqlite3_stmt thin* , int iCol);
    sqlite3_int64 sqlite3_column_int64(sqlite3_stmt thin* , int iCol);
    const unsigned char * sqlite3_column_text(sqlite3_stmt thin* , int iCol);
    const void * sqlite3_column_text16(sqlite3_stmt thin* , int iCol);
    sqlite3_value *sqlite3_column_value(sqlite3_stmt thin* , int iCol);
    int sqlite3_column_bytes(sqlite3_stmt thin* pStmt, int iCol);
    int sqlite3_column_bytes16(sqlite3_stmt thin* pStmt, int iCol);
    int sqlite3_column_type(sqlite3_stmt thin* pStmt, int iCol);

    int sqlite3_column_count(sqlite3_stmt thin* pStmt);

    int sqlite3_step(sqlite3_stmt thin* pStmt);
    int sqlite3_reset(sqlite3_stmt thin*pStmt);
    int sqlite3_finalize(sqlite3_stmt thin*pStmt);

    int sqlite3_exec(
        sqlite3 thin* pDb,                                  /* An open database */
        const char thin* sql,                           /* SQL to be evaluated */
        sqlite3_exec_cb thin* cb,  /* Callback function */
        void thin* cb_arg,                                          /* 1st argument to callback */
        char thin**errmsg                              /* Error msg written here */
        );

    int sqlite3_create_function_v2(
        sqlite3 thin* db,
        const char thin* zFunctionName,
        int nArg,
        int eTextRep,
        void thin* pApp,
        sqlite3_xFunc thin*, //void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
        sqlite3_xStep thin*, //void (*xStep)(sqlite3_context*,int,sqlite3_value**),
        sqlite3_xFinal thin*, //void (*xFinal)(sqlite3_context*),
        sqlite3_xDestroy thin* //void(*xDestroy)(void*)
        );

    void *sqlite3_user_data(sqlite3_context thin*);
    void sqlite3_result_text(sqlite3_context thin*, const char thin*, int sz, sqlite3_xDestroy *);
    void sqlite3_result_int(sqlite3_context thin*, int);
    void sqlite3_result_int64(sqlite3_context thin*, sqlite3_int64);
    void sqlite3_result_double(sqlite3_context thin*, double);
    void sqlite3_result_null(sqlite3_context thin*);

    const void *sqlite3_value_blob(sqlite3_value thin*);
    double sqlite3_value_double(sqlite3_value thin*);
    int sqlite3_value_int(sqlite3_value thin*);
    sqlite3_int64 sqlite3_value_int64(sqlite3_value thin*);
    void *sqlite3_value_pointer(sqlite3_value*, const char thin*);
    const unsigned char *sqlite3_value_text(sqlite3_value thin*);

    int sqlite3_value_type(sqlite3_value thin*);
}

SQLite3Lib sqlite3_lib;
sqlite3_xDestroy thin* destroy;

//import "sys_Dynamic2.jnc";

typedef int cdecl Fprintf(
        char const thin* format,
        ...
        );

int sqlite3_exec_mycb(void thin* arg, int  col_count, ccharPtrPtr col_values, ccharPtrPtr col_names) {
    for(int i=0; i < col_count; ++i) {
        //printf("%d : %d\n", col_count, i);
        printf("%d : %d : %s : %s\n", col_count, i, col_names[i], col_values[i]);
    }
    return 0;
}

void sqlite3_xFuncHello(sqlite3_context thin *ctx, int argc, sqlite3_value thin*thin* argv) {
    //SQLite3Lib *lib = sqlite3_user_data(ctx);
    if(argc == 0) {
        sqlite3_lib.lib.sqlite3_result_text(ctx, "Hello from Jancy", -1, destroy);
    }
}

void sqlite3_xFuncAdd2(sqlite3_context thin *ctx, int argc, sqlite3_value thin*thin* argv) {
    //SQLite3Lib *lib = sqlite3_user_data(ctx);
    if(argc == 2) {
        int a, b;
        if(sqlite3_lib.lib.sqlite3_value_type(argv[0]) == SQLite3Types.SQLITE_INTEGER) {
            a = sqlite3_lib.lib.sqlite3_value_int(argv[0]);
            if(sqlite3_lib.lib.sqlite3_value_type(argv[1]) == SQLite3Types.SQLITE_INTEGER) {
                b = sqlite3_lib.lib.sqlite3_value_int(argv[1]);
                sqlite3_lib.lib.sqlite3_result_int(ctx, a+b);
            }
        }
    }
}

int main()
{
    printf("main ()\n");

    *(intptr thin*)&destroy = SQLite3DestructorType.SQLITE_TRANSIENT;

    //sqlite3_lib.open("/home/mingo/dev/dadbiz++/third-party/dad/sqlite3/.libs/libsqlite3.so");
    sqlite3_lib.open("libsqlite3.so");

    printf("%d\n", sqlite3_lib.lib.sqlite3_libversion_number());

    sqlite3 *db;
    //int rc = sqlite3_lib.lib.sqlite3_open("test.db", &db);
    int rc = sqlite3_lib.lib.sqlite3_open(":memory:", &db);
    printf("%d : %p\n", rc, db);

    printf("%d\n", sqlite3_lib.lib.sqlite3_errcode(db));
    printf("%s\n", sqlite3_lib.lib.sqlite3_db_filename(db, "main"));

    rc = sqlite3_lib.lib.sqlite3_create_function_v2(db, "JancyHello", 0,
        SQLite3TextEncoding.SQLITE_UTF8 | SQLite3FunctionFlags.SQLITE_DETERMINISTIC,
        null, sqlite3_xFuncHello, null, null, null);
    printf("%d : create_function_v2\n", rc);

    rc = sqlite3_lib.lib.sqlite3_create_function_v2(db, "JancyAdd2", 2,
        SQLite3TextEncoding.SQLITE_UTF8 | SQLite3FunctionFlags.SQLITE_DETERMINISTIC,
        null, sqlite3_xFuncAdd2, null, null, null);
    printf("%d : create_function_v2\n", rc);

    rc = sqlite3_lib.lib.sqlite3_exec(db, "create table test(id integer primary key, val text);", null, null, null);
    printf("%d : exec\n", rc);

    sqlite3_stmt *stmt = null;
    rc = sqlite3_lib.lib.sqlite3_prepare_v2(db, "create table test2(id integer primary key, val text);", -1, &stmt, null);
    printf("%d : prepare\n", rc);

    rc = sqlite3_lib.lib.sqlite3_step(stmt);
    printf("%d : %d : step\n", rc, SQLite3ErrorCode.SQLITE_DONE);

    rc = sqlite3_lib.lib.sqlite3_finalize(stmt);
    printf("%d : finalize\n", rc);

    rc = sqlite3_lib.lib.sqlite3_prepare_v2(db, "insert into test2(id, val) values(?, ?);", -1, &stmt, null);
    printf("%d : prepare\n", rc);

    for(int i=1; i <= 5; ++i) {
        rc = sqlite3_lib.lib.sqlite3_bind_int(stmt, 1, i);
        printf("%d : bind_int\n", rc);

        char const* strval = $"val_$i";
        printf("val = %s\n", strval);
        rc = sqlite3_lib.lib.sqlite3_bind_text(stmt, 2, strval, SQLite3DestructorType.SQLITE_TRANSIENT, destroy);
        printf("%d : bind_text\n", rc);

        rc = sqlite3_lib.lib.sqlite3_step(stmt);
        printf("%d : step\n", rc);

        rc = sqlite3_lib.lib.sqlite3_reset(stmt);
        printf("%d  : reset\n", rc);
    }

    rc = sqlite3_lib.lib.sqlite3_finalize(stmt);
    printf("%d : finalize\n", rc);

    rc = sqlite3_lib.lib.sqlite3_prepare_v2(db, "select * from test2;", -1, &stmt, null);
    printf("%d : prepare\n", rc);

    int col_count = sqlite3_lib.lib.sqlite3_column_count(stmt);
    while ((rc =  sqlite3_lib.lib.sqlite3_step(stmt)) == SQLite3ErrorCode.SQLITE_ROW) {
        int id = sqlite3_lib.lib.sqlite3_column_int(stmt, 0);
        int val_sz = sqlite3_lib.lib.sqlite3_column_bytes(stmt, 1);
        const unsigned char *val = sqlite3_lib.lib.sqlite3_column_text(stmt, 1);
        printf("Values %d : %d : %d : %s\n", id, col_count, val_sz, val);
    }

    rc = sqlite3_lib.lib.sqlite3_finalize(stmt);
    printf("%d : finalize\n", rc);

    rc = sqlite3_lib.lib.sqlite3_exec(db, "select * from test2;", sqlite3_exec_mycb, null, null);
    printf("%d : exec\n", rc);

    rc = sqlite3_lib.lib.sqlite3_exec(db, "select JancyAdd2(12, 45) as j2;", sqlite3_exec_mycb, null, null);
    printf("%d : exec\n", rc);

    rc = sqlite3_lib.lib.sqlite3_exec(db, "select JancyHello() as jhello;", sqlite3_exec_mycb, null, null);
    printf("%d : exec\n", rc);

    rc = sqlite3_lib.lib.sqlite3_close(db);
    printf("%d : close\n", rc);

    jnc.DynamicLib lib;
    lib.open("libc.so.6");

    //sys.Dynamic2 lib2;
    //lib2.open("libc.so.6");

    Fprintf thin* tprintf;

    unsafe
    {
        tprintf = (Fprintf thin*) lib.getFunction("printf");
    }

    tprintf("c:/windows/media/tada.wav%d\n", 33);

    sqlite3_lib.close();
    lib.close();

    return 0;

catch:
    printf("error caught: %s\n", std.getLastError ().m_description);
    return -1;
}
vovkos commented 3 years ago

Could you share the output? Where exactly this error is happening?

mingodad commented 3 years ago

Here it is, it's calling rc = sqlite3_lib.lib.sqlite3_exec(db, "select JancyHello() as jhello;", sqlite3_exec_mycb, null, null); and we have the output of calling void sqlite3_xFuncHello(sqlite3_context thin *ctx, int argc, sqlite3_value thin*thin* argv) as the last line on the output (just before the error message):

jancy "sqlite-dad.jnc"
main ()
3022000
0 : 0x1d1b978
0

0 : create_function_v2
0 : create_function_v2
0 : exec
0 : prepare
101 : 101 : step
0 : finalize
0 : prepare
0 : bind_int
val = val_1
0 : bind_text
101 : step
0  : reset
0 : bind_int
val = val_2
0 : bind_text
101 : step
0  : reset
0 : bind_int
val = val_3
0 : bind_text
101 : step
0  : reset
0 : bind_int
val = val_4
0 : bind_text
101 : step
0  : reset
0 : bind_int
val = val_5
0 : bind_text
101 : step
0  : reset
0 : finalize
0 : prepare
Values 1 : 2 : 5 : val_1
Values 2 : 2 : 5 : val_2
Values 3 : 2 : 5 : val_3
Values 4 : 2 : 5 : val_4
Values 5 : 2 : 5 : val_5
0 : finalize
2 : 0 : id : 1
2 : 1 : val : val_1
2 : 0 : id : 2
2 : 1 : val : val_2
2 : 0 : id : 3
2 : 1 : val : val_3
2 : 0 : id : 4
2 : 1 : val : val_4
2 : 0 : id : 5
2 : 1 : val : val_5
0 : exec
1 : 0 : j2 : 57
0 : exec
1 : 0 : jhello : Hello from Jancy
error caught: 'rc' is not found or not a type
>Exit code: 255
vovkos commented 3 years ago

Found some time to run your code on my laptop today. The reason for this exception is that you declare sqlite_result_text with a fat function pointer sqlite3_xDestroy*

Change it to:

    void sqlite3_result_text(sqlite3_context thin*, const char thin*, int sz, sqlite3_xDestroy thin*);

and it will work.

vovkos commented 3 years ago

A quick follow-up comment to explain why the error code is so weird.

The Jancy runtime catches an exception generated in sqlite3_result_text via signal handler on POSIX, and vectored exception handlers on Windows. Unlike the latter, signal handlers are very limited in what they can call (they certainly can't malloc), so currently, the exception information is not saved on POSIX (as it is on Windows); the implementation must be changed a bit -- e.g. using a thread-local pool of buffers to store the exception information.

The error you see must be the last error generated by Jancy parser (probably, during a grammar conflict resolving) -- and is never overwritten.

mingodad commented 3 years ago

What about my proposal ?

Well, how Jancy would know what is your intention? Both fat and thin pointers are part of the language and have their respective domains of use.

How about using something like the C++ compilers do (external "C" {}) so no need to annotate every pointer there since all of then will be thin and maybe also be a bit nut (dynamiclib "C++" some cppLib) and have some predefined mangle/demalngle functions per platform.

dynamiclib "C" SQLite3Lib // will use jnc.DynamicLib behind the scene
{
...
}
mingodad commented 3 years ago

By the way thank you so far for the help and I hope I am also helping improve jancy.

vovkos commented 3 years ago

What about my proposal ?

Yes, it will be good to enable overriding of default pointer thickness for certain portions of code -- mostly, it will be about foreign function calls & declarations. Shouldn't be limited to dynamiclib though -- think of your callback declarations.

However, the more important problems your snippet exposes are (1) the signal information is not saved (should be) and (2) certain syntax conflicts result in errors being stored in the TLS (shouldn't be). I will fix that first. Then I will play a bit with different syntaxes regarding changing the default pointer thickness, then settle on something.

vovkos commented 3 years ago

By the way thank you so far for the help and I hope I am also helping improve `jancy

Sure thing! I absolutely appreciate your feedback!

vovkos commented 3 years ago

1) POSIX signal information (signal number, faulting code address, referenced address) is now properly saved -- on stack first, then moved to TLS after longjmp;

2) Vectored exceptions on Windows now save more information. In addition to NTSTATUS of the exception, we now have faulting code address, referenced address, and access type (read/write/exec);

3) Grammar updated so that grammar conflicts are now resolved without ever setting errors in TLS;

4) Added a new pragma directive to allow overriding default pointer thickness (and other compiler settings). Currently supported pragmas: Alignment, ExposedEnums, ThinPointers. The pragma directive is easy to extend, so more configurable settings can be added later.

pragma(ThinPointers, true);

// here come dynamiclib declarations, foreign callback typedefs, etc.
// for example:

typedef char const* StrchrFunc(char const* p); // both parameter `p` and retval are `thin`

pragma(ThinPointers, default);

When copy-pasting C code, ExposedEnums might also come in handy (makes enums exposed by default, i.e. identifiers inside enum are visible from the parent namespace):

pragma(ExposedEnums, true);

enum Color
{
  Color_Red   = 0xff0000,
  Color_Green = 0x00ff00,
  Color_Blue  = 0x0000ff,
}

pragma(ExposedEnums, default);

enum ColorChannel
{
  Red,
  Green,
  Blue,
  Alpha
}

// no need to qualify enum Color values, e.g.:
// Color color = Color_Red;

// need to qualify enum ColorChannel values, e.g.:
// ColorChannel channel = ColorChannel.Red
mingodad commented 3 years ago

Could you show the sqlite example we've been working in this thread with all of this applied ?

vovkos commented 3 years ago

Re signals/exception -- you should see a proper description of SIGSEGV instead of that weird 'rc' is not found or not a type error.

Re pragmas -- started to play with your example and realized pragmas don't work as expected for nested namespaces (e.g. the lib namespace). Lazy parsing of namespaces is the reason. It's quite clear what must be done, but it's not a one-line fix. I will find time to fix it in the next couple of days and let you know.

mingodad commented 3 years ago

Thank you ! Nothing is like trying it in practice !

vovkos commented 3 years ago

Fixed. Every declaration now has a pointer to a snapshot of all pragma-controlled settings (so that lazy parsing uses correct Alignment, ThinPointers, ExposedEnums, and whatever is going to be added later).

Example of the new pragma directive usage:

// foreign function declarations below -- use thin pointers and exposed enums

pragma(ThinPointers, true) 
pragma(ExposedEnums, true) 

//..............................................................................

struct sqlite3 {
  protected construct() {}
}
struct sqlite3_stmt {
  protected construct() {}
}
struct sqlite3_value {
  protected construct() {}
}
struct sqlite3_context {
  protected construct() {}
}

typedef  int64_t  sqlite3_int64;
typedef void sqlite3_destructor_cb(void*);
typedef int sqlite3_exec_cb(void* arg,int col_count, const char ** col_values, const char ** col_names);

typedef void sqlite3_xFunc(sqlite3_context  *ctx, int argc, sqlite3_value ** argv);
typedef void sqlite3_xStep(sqlite3_context *,int,sqlite3_value **);
typedef void sqlite3_xFinal(sqlite3_context *);
typedef void sqlite3_xDestroy(void *);

enum SQLite3DestructorType {
    SQLITE_STATIC  = 0, //    ((sqlite3_destructor_type)0)
    SQLITE_TRANSIENT  = -1, // ((sqlite3_destructor_type)-1)
};

enum SQLite3ErrorCode {
    SQLITE_OK =          0,   /* Successful result */
    /* beginning-of-error-codes */
    SQLITE_ERROR  =      1,   /* Generic error */
    SQLITE_INTERNAL =    2,   /* Internal logic error in SQLite */
    SQLITE_PERM  =       3,   /* Access permission denied */
    SQLITE_ABORT =       4,   /* Callback routine requested an abort */
    SQLITE_BUSY  =       5,   /* The database file is locked */
    SQLITE_LOCKED  =     6,   /* A table in the database is locked */
    SQLITE_NOMEM  =      7,   /* A malloc() failed */
    SQLITE_READONLY =    8,   /* Attempt to write a readonly database */
    SQLITE_INTERRUPT =   9,   /* Operation terminated by sqlite3_interrupt()*/
    SQLITE_IOERR  =     10,   /* Some kind of disk I/O error occurred */
    SQLITE_CORRUPT  =   11,   /* The database disk image is malformed */
    SQLITE_NOTFOUND =   12,   /* Unknown opcode in sqlite3_file_control() */
    SQLITE_FULL  =      13,   /* Insertion failed because database is full */
    SQLITE_CANTOPEN  =  14,   /* Unable to open the database file */
    SQLITE_PROTOCOL =   15,   /* Database lock protocol error */
    SQLITE_EMPTY  =     16,   /* Internal use only */
    SQLITE_SCHEMA  =    17,   /* The database schema changed */
    SQLITE_TOOBIG =     18,   /* String or BLOB exceeds size limit */
    SQLITE_CONSTRAINT = 19,   /* Abort due to constraint violation */
    SQLITE_MISMATCH  =  20,   /* Data type mismatch */
    SQLITE_MISUSE  =    21,   /* Library used incorrectly */
    SQLITE_NOLFS  =     22,   /* Uses OS features not supported on host */
    SQLITE_AUTH =       23,   /* Authorization denied */
    SQLITE_FORMAT  =    24,   /* Not used */
    SQLITE_RANGE  =     25,   /* 2nd parameter to sqlite3_bind out of range */
    SQLITE_NOTADB =     26,   /* File opened that is not a database file */
    SQLITE_NOTICE =     27,   /* Notifications from sqlite3_log() */
    SQLITE_WARNING =    28,   /* Warnings from sqlite3_log() */
    SQLITE_ROW =       100,  /* sqlite3_step() has another row ready */
    SQLITE_DONE  =      101,  /* sqlite3_step() has finished executing */
};

enum SQLite3TextEncoding {
    SQLITE_UTF8    =       1,    /* IMP: R-37514-35566 */
    SQLITE_UTF16LE  =      2,    /* IMP: R-03371-37637 */
    SQLITE_UTF16BE  =      3,    /* IMP: R-51971-34154 */
    SQLITE_UTF16   =       4,    /* Use native byte order */
    SQLITE_ANY  =          5,    /* Deprecated */
    SQLITE_UTF16_ALIGNED = 8,    /* sqlite3_create_collation only */
};

enum SQLite3FunctionFlags {
    SQLITE_DETERMINISTIC  =  0x000000800,
    SQLITE_DIRECTONLY  =     0x000080000,
    SQLITE_SUBTYPE  =        0x000100000,
    SQLITE_INNOCUOUS  =      0x000200000,
};

enum SQLite3Types {
    SQLITE_INTEGER = 1,
    SQLITE_FLOAT  =  2,
    SQLITE_TEXT =    3,
    SQLITE_BLOB =    4,
    SQLITE_NULL  =   5,
};

dynamiclib SQLite3Lib // will use jnc.DynamicLib behind the scene
{
    int sqlite3_libversion_number();
    int sqlite3_errcode(sqlite3 *db);
    int sqlite3_open(
        const char * filename,   /* Database filename (UTF-8) */
        sqlite3 ** ppDb          /* OUT: SQLite db handle */
        );
    int sqlite3_close(sqlite3 * pDb);

    const char * sqlite3_db_filename(sqlite3 * db, const char * zDbName);

    int sqlite3_prepare(
        sqlite3 * pDb,              /* Database handle */
        const char * zSql,       /* SQL statement, UTF-8 encoded */
        int nByte,                   /* Maximum length of zSql in bytes. */
        sqlite3_stmt ** ppStmt,  /* OUT: Statement handle */
        const char **pzTail     /* OUT: Pointer to unused portion of zSql */
        );

    int sqlite3_prepare_v2(
        sqlite3 * db,            /* Database handle */
        const char * zSql,       /* SQL statement, UTF-8 encoded */
        int nByte,              /* Maximum length of zSql in bytes. */
        sqlite3_stmt **ppStmt,  /* OUT: Statement handle */
        const char ** pzTail     /* OUT: Pointer to unused portion of zSql */
        );

    int sqlite3_bind_text(sqlite3_stmt * pStmt, int col, const char * value, int flags, sqlite3_destructor_cb *cb);
    int sqlite3_bind_double(sqlite3_stmt *, int, double);
    int sqlite3_bind_int(sqlite3_stmt *, int, int);
    int sqlite3_bind_int64(sqlite3_stmt *, int, sqlite3_int64);
    int sqlite3_bind_null(sqlite3_stmt  *, int);

    const void * sqlite3_column_blob(sqlite3_stmt * , int iCol);
    double sqlite3_column_double(sqlite3_stmt * , int iCol);
    int sqlite3_column_int(sqlite3_stmt * , int iCol);
    sqlite3_int64 sqlite3_column_int64(sqlite3_stmt * , int iCol);
    const unsigned char * sqlite3_column_text(sqlite3_stmt * , int iCol);
    const void * sqlite3_column_text16(sqlite3_stmt * , int iCol);
    sqlite3_value *sqlite3_column_value(sqlite3_stmt * , int iCol);
    int sqlite3_column_bytes(sqlite3_stmt * pStmt, int iCol);
    int sqlite3_column_bytes16(sqlite3_stmt * pStmt, int iCol);
    int sqlite3_column_type(sqlite3_stmt * pStmt, int iCol);

    int sqlite3_column_count(sqlite3_stmt * pStmt);

    int sqlite3_step(sqlite3_stmt * pStmt);
    int sqlite3_reset(sqlite3_stmt *pStmt);
    int sqlite3_finalize(sqlite3_stmt *pStmt);

    int sqlite3_exec(
        sqlite3 * pDb,                                  /* An open database */
        const char * sql,                           /* SQL to be evaluated */
        sqlite3_exec_cb * cb,  /* Callback function */
        void * cb_arg,                                          /* 1st argument to callback */
        char **errmsg                              /* Error msg written here */
        );

    int sqlite3_create_function_v2(
        sqlite3 * db,
        const char * zFunctionName,
        int nArg,
        int eTextRep,
        void * pApp,
        sqlite3_xFunc *, //void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
        sqlite3_xStep *, //void (*xStep)(sqlite3_context*,int,sqlite3_value**),
        sqlite3_xFinal *, //void (*xFinal)(sqlite3_context*),
        sqlite3_xDestroy * //void(*xDestroy)(void*)
        );

    void *sqlite3_user_data(sqlite3_context *);
    void sqlite3_result_text(sqlite3_context *, const char *, int sz, sqlite3_xDestroy *);
    void sqlite3_result_int(sqlite3_context *, int);
    void sqlite3_result_int64(sqlite3_context *, sqlite3_int64);
    void sqlite3_result_double(sqlite3_context *, double);
    void sqlite3_result_null(sqlite3_context *);

    const void *sqlite3_value_blob(sqlite3_value *);
    double sqlite3_value_double(sqlite3_value *);
    int sqlite3_value_int(sqlite3_value *);
    sqlite3_int64 sqlite3_value_int64(sqlite3_value *);
    void *sqlite3_value_pointer(sqlite3_value*, const char *);
    const unsigned char *sqlite3_value_text(sqlite3_value *);

    int sqlite3_value_type(sqlite3_value *);
}

SQLite3Lib sqlite3_lib;
sqlite3_xDestroy * destroy;

//import "sys_Dynamic2.jnc";

typedef int cdecl Fprintf(
        char const * format,
        ...
        );

int sqlite3_exec_mycb(void * arg, int  col_count, const char ** col_values, const char ** col_names) {
    for(int i=0; i < col_count; ++i) {
        //printf("%d : %d\n", col_count, i);
        printf("%d : %d : %s : %s\n", col_count, i, col_names[i], col_values[i]);
    }
    return 0;
}

void sqlite3_xFuncHello(sqlite3_context  *ctx, int argc, sqlite3_value ** argv) {
    //SQLite3Lib *lib = sqlite3_user_data(ctx);
    if(argc == 0) {
        sqlite3_lib.lib.sqlite3_result_text(ctx, "Hello from Jancy", -1, destroy);
    }
}

void sqlite3_xFuncAdd2(sqlite3_context  *ctx, int argc, sqlite3_value ** argv) {
    //SQLite3Lib *lib = sqlite3_user_data(ctx);
    if(argc == 2) {
        int a, b;
        if(sqlite3_lib.lib.sqlite3_value_type(argv[0]) == SQLITE_INTEGER) {
            a = sqlite3_lib.lib.sqlite3_value_int(argv[0]);
            if(sqlite3_lib.lib.sqlite3_value_type(argv[1]) == SQLITE_INTEGER) {
                b = sqlite3_lib.lib.sqlite3_value_int(argv[1]);
                sqlite3_lib.lib.sqlite3_result_int(ctx, a+b);
            }
        }
    }
}

//..............................................................................

// back to defaults

pragma(ThinPointers, default) 
pragma(ExposedEnums, default) 

int main()
{
    printf("main ()\n");

    *(intptr thin*)&destroy = SQLITE_TRANSIENT;

    //sqlite3_lib.open("/home/mingo/dev/dadbiz++/third-party/dad/sqlite3/.libs/libsqlite3.so");
    sqlite3_lib.open("libsqlite3.so");

    printf("%d\n", sqlite3_lib.lib.sqlite3_libversion_number());

    sqlite3 thin* db;
    //int rc = sqlite3_lib.lib.sqlite3_open("test.db", &db);
    int rc = sqlite3_lib.lib.sqlite3_open(":memory:", &db);
    printf("%d : %p\n", rc, db);

    printf("%d\n", sqlite3_lib.lib.sqlite3_errcode(db));
    printf("%s\n", sqlite3_lib.lib.sqlite3_db_filename(db, "main"));

    rc = sqlite3_lib.lib.sqlite3_create_function_v2(db, "JancyHello", 0,
        SQLITE_UTF8 | SQLITE_DETERMINISTIC,
        null, sqlite3_xFuncHello, null, null, null);
    printf("%d : create_function_v2\n", rc);

    rc = sqlite3_lib.lib.sqlite3_create_function_v2(db, "JancyAdd2", 2,
        SQLITE_UTF8 | SQLITE_DETERMINISTIC,
        null, sqlite3_xFuncAdd2, null, null, null);
    printf("%d : create_function_v2\n", rc);

    rc = sqlite3_lib.lib.sqlite3_exec(db, "create table test(id integer primary key, val text);", null, null, null);
    printf("%d : exec\n", rc);

    sqlite3_stmt thin*stmt = null;
    rc = sqlite3_lib.lib.sqlite3_prepare_v2(db, "create table test2(id integer primary key, val text);", -1, &stmt, null);
    printf("%d : prepare\n", rc);

    rc = sqlite3_lib.lib.sqlite3_step(stmt);
    printf("%d : %d : step\n", rc, SQLITE_DONE);

    rc = sqlite3_lib.lib.sqlite3_finalize(stmt);
    printf("%d : finalize\n", rc);

    rc = sqlite3_lib.lib.sqlite3_prepare_v2(db, "insert into test2(id, val) values(?, ?);", -1, &stmt, null);
    printf("%d : prepare\n", rc);

    for(int i=1; i <= 5; ++i) {
        rc = sqlite3_lib.lib.sqlite3_bind_int(stmt, 1, i);
        printf("%d : bind_int\n", rc);

        char const* strval = $"val_$i";
        printf("val = %s\n", strval);
        rc = sqlite3_lib.lib.sqlite3_bind_text(stmt, 2, strval, SQLITE_TRANSIENT, destroy);
        printf("%d : bind_text\n", rc);

        rc = sqlite3_lib.lib.sqlite3_step(stmt);
        printf("%d : step\n", rc);

        rc = sqlite3_lib.lib.sqlite3_reset(stmt);
        printf("%d  : reset\n", rc);
    }

    rc = sqlite3_lib.lib.sqlite3_finalize(stmt);
    printf("%d : finalize\n", rc);

    rc = sqlite3_lib.lib.sqlite3_prepare_v2(db, "select * from test2;", -1, &stmt, null);
    printf("%d : prepare\n", rc);

    int col_count = sqlite3_lib.lib.sqlite3_column_count(stmt);
    while ((rc =  sqlite3_lib.lib.sqlite3_step(stmt)) == SQLITE_ROW) {
        int id = sqlite3_lib.lib.sqlite3_column_int(stmt, 0);
        int val_sz = sqlite3_lib.lib.sqlite3_column_bytes(stmt, 1);
        const unsigned char thin*val = sqlite3_lib.lib.sqlite3_column_text(stmt, 1);
        printf("Values %d : %d : %d : %s\n", id, col_count, val_sz, val);
    }

    rc = sqlite3_lib.lib.sqlite3_finalize(stmt);
    printf("%d : finalize\n", rc);

    rc = sqlite3_lib.lib.sqlite3_exec(db, "select * from test2;", sqlite3_exec_mycb, null, null);
    printf("%d : exec\n", rc);

    rc = sqlite3_lib.lib.sqlite3_exec(db, "select JancyAdd2(12, 45) as j2;", sqlite3_exec_mycb, null, null);
    printf("%d : exec\n", rc);

    rc = sqlite3_lib.lib.sqlite3_exec(db, "select JancyHello() as jhello;", sqlite3_exec_mycb, null, null);
    printf("%d : exec\n", rc);

    rc = sqlite3_lib.lib.sqlite3_close(db);
    printf("%d : close\n", rc);

    jnc.DynamicLib lib;
    lib.open("libc.so.6");

    //sys.Dynamic2 lib2;
    //lib2.open("libc.so.6");

    Fprintf thin* tprintf;

    unsafe
    {
        tprintf = (Fprintf thin*) lib.getFunction("printf");
    }

    tprintf("c:/windows/media/tada.wav%d\n", 33);

    sqlite3_lib.close();
    lib.close();

    return 0;

catch:
    printf("error caught: %s\n", std.getLastError ().m_description);
    return -1;
}
mingodad commented 3 years ago

Thank you !

It seems that the jancy_b repository is not updated and the resulting build fail to compile your example !

mingodad commented 3 years ago

To make it a bit easier for new users I think that create a github action to build the jancy_b repository and make the resulting binaries available to download would be a goo step to expand the user base.

I did something like that for https://github.com/mingodad/picat/actions , see https://github.com/mingodad/picat/blob/master/.github/workflows/build.yml for the build actions.

vovkos commented 3 years ago

It seems that the jancy_b repository is not updated

Actually, it is updated automatically (like before). I added a GitHub action to push to jancy_b if the CI workflow completes successfully.

and the resulting build fail to compile your example !

Maybe, you didn't git submodule update after pulling? I just tested and everything compiles with the latest master commit to jancy_b.

vovkos commented 3 years ago

To make it a bit easier for new users I think that create a github action to build the jancy_b repository and make the resulting binaries available to download would be a goo step to expand the user base.

Publishing binaries automatically makes sense, but only when we make a release (on tag). Building and publishing binaries on every commit -- I don't know, it doesn't really feel like a good practice.

Of course, updating the bundle-repo should happen automatically -- and it does now.

mingodad commented 3 years ago

You are wright I did forgot to execute git submodule update and for some till now unknown reason I'm still getting the error shown bellow on my ubuntu 18.04 and I need to manually update several link.txt and flags.cmake .

Scanning dependencies of target axl_gui_pch
[  5%] Generating pch.cpp.h.gch
[  5%] Built target axl_gui_pch
Scanning dependencies of target axl_gui
[  5%] Building CXX object axl/src/axl_gui/CMakeFiles/axl_gui.dir/pch.cpp.o
error: C++14 was enabled in PCH file but is currently disabled
1 error generated.
axl/src/axl_gui/CMakeFiles/axl_gui.dir/build.make:67: recipe for target 'axl/src/axl_gui/CMakeFiles/axl_gui.dir/pch.cpp.o' failed
make[2]: *** [axl/src/axl_gui/CMakeFiles/axl_gui.dir/pch.cpp.o] Error 1
CMakeFiles/Makefile2:468: recipe for target 'axl/src/axl_gui/CMakeFiles/axl_gui.dir/all' failed
make[1]: *** [axl/src/axl_gui/CMakeFiles/axl_gui.dir/all] Error 2
Makefile:162: recipe for target 'all' failed
make: *** [all] Error 2
vovkos commented 3 years ago
make clean && make

should help. Manually editing CMake-generated files is generally not the best approach; if stuck with something like above it's better to clean and start over.

Regarding this error -- both GCC and Clang suck pretty hard in the precompiled header department. MSVC is not ideal but here it shines compared to these two. I am actually inclined to disable precompiled headers by default for both GCC and Clang; the build speed gain we get from PCH on GCC/Clang is really not worth the trouble.

mingodad commented 3 years ago

When trying to run the example you gave on http://jancy.org/demo.html#00_HelloWorld.jnc (Iknow that the libsqlite3.so probably will not be there but at least it should pass the compilation step).

00_HelloWorld.jnc(3,22): unexpected token 'true' in 'function_formal_argument'

--------------------
exit code: 253
time:      0.018s
// foreign function declarations below -- use thin pointers and exposed enums

pragma(ThinPointers, true) ///!!!! here is the line 3
pragma(ExposedEnums, true) 
mingodad commented 3 years ago

I usually use FLTK (https://www.fltk.org/) as a cross platform gui , (it's very straight forward and simple) how is the dynamiclib support for simple C++ ?