commit a9d2a10f9dfc2cacf17e574928e6b851e084beef
parent 6dba524755c9b5a3dccc4372cf74cf1ed6d46586
Author: Florian Dold <florian@dold.me>
Date: Mon, 2 Jan 2023 22:51:14 +0100
wip
Diffstat:
9 files changed, 300 insertions(+), 60 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -1,2 +1,5 @@
tags
.vscode
+
+# compiled wallet core file
+taler-wallet-core-qjs.mjs
diff --git a/meson.build b/meson.build
@@ -56,16 +56,34 @@ qjsc_exe = executable('qjsc', [
curl_dep,
sodium_dep])
-repl_c = custom_target('repl',
+repl_c = custom_target('repl_c',
input : ['quickjs/repl.js'],
output : ['repl.c'],
command : [qjsc_exe, '-c', '-m',
'-o', '@OUTPUT@',
'@INPUT@'])
+prelude_c = custom_target('prelude_c',
+ input : ['prelude.js'],
+ output : ['prelude.c'],
+ command : [qjsc_exe, '-c', '-m', '-M', 'tart',
+ '-o', '@OUTPUT@',
+ '@INPUT@'])
+
+wallet_core_c = custom_target('wallet_core_c',
+ input : ['taler-wallet-core-qjs.mjs'],
+ output : ['wallet_core.c'],
+ command : [qjsc_exe, '-c', '-m', '-M', 'tart', '-N', 'qjsc_wallet_core',
+ '-o', '@OUTPUT@',
+ '@INPUT@'])
+
repl = static_library('repl', repl_c)
+prelude = static_library('prelude', prelude_c)
+wallet_core = static_library('wallet_core', wallet_core_c)
-tart = static_library('tart', 'tart_module.c')
+tart = static_library('tart', 'tart_module.c', dependencies : [
+ mbedcrypto_dep,
+ ])
qtart_exe = executable('qtart', [
'qtart.c',
@@ -78,6 +96,8 @@ qtart_exe = executable('qtart', [
quickjs_libc,
quickjs,
repl,
+ wallet_core,
+ prelude,
tart,
],
dependencies: [
@@ -97,6 +117,8 @@ talerwalletcore_lib = shared_library('talerwalletcore', 'taler_wallet_core_lib.c
quickjs_libc,
quickjs,
tart,
+ wallet_core,
+ prelude,
],
dependencies: [
m_dep,
@@ -109,4 +131,4 @@ talerwalletcore_lib = shared_library('talerwalletcore', 'taler_wallet_core_lib.c
wallet_client_example_exe = executable('wallet-client-example',
'wallet-client-example.c',
- link_with : [talerwalletcore_lib])
-\ No newline at end of file
+ link_with : [talerwalletcore_lib])
diff --git a/prelude.js b/prelude.js
@@ -1,3 +1,7 @@
+// The prelude defines basic functionality
+// that is expected by the Taler wallet core JavaScript,
+// but not provided by quickjs or the "tart" module directly.
+
import * as os from "os";
import * as tart from "tart";
diff --git a/qtart.c b/qtart.c
@@ -46,6 +46,12 @@
extern const uint8_t qjsc_repl[];
extern const uint32_t qjsc_repl_size;
+extern const uint8_t qjsc_prelude[];
+extern const uint32_t qjsc_prelude_size;
+
+extern const uint8_t qjsc_wallet_core[];
+extern const uint32_t qjsc_wallet_core_size;
+
static int eval_buf(JSContext *ctx, const void *buf, int buf_len,
const char *filename, int eval_flags)
{
@@ -294,6 +300,12 @@ void help(void)
exit(1);
}
+void
+handle_host_message(void *cls, const char *msg)
+{
+ printf("message from JS to host: %s\n", msg);
+}
+
int main(int argc, char **argv)
{
JSRuntime *rt;
@@ -436,6 +448,7 @@ int main(int argc, char **argv)
js_std_set_worker_new_context_func(JS_NewCustomContext);
js_std_init_handlers(rt);
ctx = JS_NewCustomContext(rt);
+ js_os_set_host_message_handler(ctx, handle_host_message, NULL);
if (!ctx) {
fprintf(stderr, "qjs: cannot allocate JS context\n");
exit(2);
@@ -461,6 +474,9 @@ int main(int argc, char **argv)
eval_buf(ctx, str, strlen(str), "<input>", JS_EVAL_TYPE_MODULE);
}
+ js_std_eval_binary(ctx, qjsc_prelude, qjsc_prelude_size, 0);
+ js_std_eval_binary(ctx, qjsc_wallet_core, qjsc_wallet_core_size, 0);
+
for(i = 0; i < include_count; i++) {
if (eval_file(ctx, include_list[i], module))
goto fail;
diff --git a/quickjs/quickjs-libc.c b/quickjs/quickjs-libc.c
@@ -2340,8 +2340,10 @@ static void call_handler(JSContext *ctx, JSValueConst func)
func1 = JS_DupValue(ctx, func);
ret = JS_Call(ctx, func1, JS_UNDEFINED, 0, NULL);
JS_FreeValue(ctx, func1);
- if (JS_IsException(ret))
+ if (JS_IsException(ret)) {
+ fprintf(stderr, "exception in handler\n");
js_std_dump_error(ctx);
+ }
JS_FreeValue(ctx, ret);
}
@@ -2499,6 +2501,63 @@ static int handle_posted_message(JSRuntime *rt, JSContext *ctx,
}
#endif
+/* return 1 if a message was handled, 0 if no message */
+static int handle_host_message(JSRuntime *rt, JSContext *ctx)
+{
+ JSThreadState *ts = JS_GetRuntimeOpaque(JS_GetRuntime(ctx));
+ JSHostMessagePipe *hp = ts->host_pipe;
+ int ret;
+ struct list_head *el;
+ JSHostMessage *msg;
+ JSValue obj, func, retval;
+
+ pthread_mutex_lock(&hp->mutex);
+ if (!list_empty(&hp->msg_queue)) {
+ el = hp->msg_queue.next;
+ msg = list_entry(el, JSHostMessage, link);
+
+ /* remove the message from the queue */
+ list_del(&msg->link);
+
+ if (list_empty(&hp->msg_queue)) {
+ uint8_t buf[16];
+ int ret;
+ for(;;) {
+ ret = read(hp->read_fd, buf, sizeof(buf));
+ if (ret >= 0)
+ break;
+ if (errno != EAGAIN && errno != EINTR)
+ break;
+ }
+ }
+
+ obj = JS_NewString(ctx, msg->msg_data);
+
+ free(msg->msg_data);
+ free(msg);
+
+ pthread_mutex_unlock(&hp->mutex);
+
+ /* 'func' might be destroyed when calling itself (if it frees the
+ handler), so must take extra care */
+ func = JS_DupValue(ctx, ts->on_host_message_func);
+ retval = JS_Call(ctx, func, JS_UNDEFINED, 1, (JSValueConst *)&obj);
+ JS_FreeValue(ctx, obj);
+ JS_FreeValue(ctx, func);
+ if (JS_IsException(retval)) {
+ fail:
+ js_std_dump_error(ctx);
+ } else {
+ JS_FreeValue(ctx, retval);
+ }
+ ret = 1;
+ } else {
+ pthread_mutex_unlock(&hp->mutex);
+ ret = 0;
+ }
+ return ret;
+}
+
static int js_os_poll(JSContext *ctx)
{
JSRuntime *rt = JS_GetRuntime(ctx);
@@ -2611,6 +2670,12 @@ static int js_os_poll(JSContext *ctx)
}
}
}
+
+ if (FD_ISSET(ts->host_pipe->read_fd, &rfds)) {
+ if (handle_host_message(rt, ctx)) {
+ goto done;
+ }
+ }
}
done:
return 0;
@@ -3474,6 +3539,27 @@ static JSWorkerMessagePipe *js_new_message_pipe(void)
return ps;
}
+static JSHostMessagePipe *js_new_host_message_pipe(void)
+{
+ JSHostMessagePipe *ps;
+ int pipe_fds[2];
+
+ if (pipe(pipe_fds) < 0)
+ return NULL;
+
+ ps = malloc(sizeof(*ps));
+ if (!ps) {
+ close(pipe_fds[0]);
+ close(pipe_fds[1]);
+ return NULL;
+ }
+ init_list_head(&ps->msg_queue);
+ pthread_mutex_init(&ps->mutex, NULL);
+ ps->read_fd = pipe_fds[0];
+ ps->write_fd = pipe_fds[1];
+ return ps;
+}
+
static JSWorkerMessagePipe *js_dup_message_pipe(JSWorkerMessagePipe *ps)
{
atomic_add_int(&ps->ref_count, 1);
@@ -3515,6 +3601,34 @@ static void js_free_message_pipe(JSWorkerMessagePipe *ps)
}
}
+static void js_free_host_message(JSHostMessage *msg)
+{
+ free(msg->msg_data);
+ free(msg);
+}
+
+static void js_free_host_message_pipe(JSHostMessagePipe *ps)
+{
+ struct list_head *el, *el1;
+ JSHostMessage *msg;
+ int ref_count;
+
+ if (!ps)
+ return;
+
+ assert(ref_count >= 0);
+ if (ref_count == 0) {
+ list_for_each_safe(el, el1, &ps->msg_queue) {
+ msg = list_entry(el, JSHostMessage, link);
+ js_free_host_message(msg);
+ }
+ pthread_mutex_destroy(&ps->mutex);
+ close(ps->read_fd);
+ close(ps->write_fd);
+ free(ps);
+ }
+}
+
static void js_free_port(JSRuntime *rt, JSWorkerMessageHandler *port)
{
if (port) {
@@ -3874,6 +3988,23 @@ js_os_post_message_from_host(JSContext *ctx, const char *msg_str)
return -1;
}
+static JSValue js_os_simulateHostMessage(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSThreadState *ts = JS_GetRuntimeOpaque(JS_GetRuntime(ctx));
+ const char *s;
+
+ s = JS_ToCString(ctx, argv[0]);
+
+ if (!s) {
+ return JS_EXCEPTION;
+ }
+
+ js_os_post_message_from_host(ctx, s);
+
+ return JS_UNDEFINED;
+}
+
void js_os_set_host_message_handler(JSContext *ctx, JSHostMessageHandlerFn f, void *cls)
{
JSThreadState *ts = JS_GetRuntimeOpaque(JS_GetRuntime(ctx));
@@ -3896,26 +4027,16 @@ static JSValue js_os_postHostMessage(JSContext *ctx, JSValueConst this_val,
if (ts->host_message_handler_f) {
ts->host_message_handler_f(ts->host_message_handler_cls, s);
}
-}
-static JSValue js_os_get_onhostmessage(JSContext *ctx, JSValueConst this_val)
-{
- JSThreadState *ts = JS_GetRuntimeOpaque(JS_GetRuntime(ctx));
- if (!ts) {
- return JS_EXCEPTION;
- }
- if (JS_IsFunction(ctx, ts->on_host_message_func)) {
- return JS_DupValue(ctx, ts->on_host_message_func);
- } else {
- return JS_NULL;
- }
+ return JS_UNDEFINED;
}
-static JSValue js_os_set_onhostmessage(JSContext *ctx, JSValueConst this_val,
- JSValueConst func)
+static JSValue js_os_setMessageFromHostHandler(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
{
JSRuntime *rt = JS_GetRuntime(ctx);
JSThreadState *ts = JS_GetRuntimeOpaque(rt);
+ JSValue func = argv[0];
if (JS_IsNull(func)) {
ts->on_host_message_func = JS_NULL;
@@ -4028,8 +4149,9 @@ static const JSCFunctionListEntry js_os_funcs[] = {
JS_CFUNC_DEF("dup", 1, js_os_dup ),
JS_CFUNC_DEF("dup2", 2, js_os_dup2 ),
#endif
- JS_CFUNC_DEF("postHostMessage", 1, js_os_postHostMessage ),
- JS_CGETSET_DEF("onhostmessage", js_os_get_onhostmessage, js_os_set_onhostmessage ),
+ JS_CFUNC_DEF("postMessageToHost", 1, js_os_postHostMessage ),
+ JS_CFUNC_DEF("simulateHostMessageFromHost", 1, js_os_simulateHostMessage ),
+ JS_CFUNC_DEF("setMessageFromHostHandler", 1, js_os_setMessageFromHostHandler ),
};
static int js_os_init(JSContext *ctx, JSModuleDef *m)
@@ -4157,6 +4279,11 @@ oom_fail:
init_list_head(&ts->os_signal_handlers);
init_list_head(&ts->os_timers);
init_list_head(&ts->port_list);
+ ts->on_host_message_func = JS_NULL;
+ ts->host_pipe = js_new_host_message_pipe();
+ if (!ts->host_pipe) {
+ goto oom_fail;
+ }
JS_SetRuntimeOpaque(rt, ts);
@@ -4195,12 +4322,16 @@ void js_std_free_handlers(JSRuntime *rt)
free_timer(rt, th);
}
+ JS_FreeValueRT(rt, ts->on_host_message_func);
+
#ifdef USE_WORKER
/* XXX: free port_list ? */
js_free_message_pipe(ts->recv_pipe);
js_free_message_pipe(ts->send_pipe);
#endif
+ js_free_host_message_pipe(ts->host_pipe);
+
free(ts);
JS_SetRuntimeOpaque(rt, NULL); /* fail safe */
}
@@ -4215,6 +4346,7 @@ static void js_dump_obj(JSContext *ctx, FILE *f, JSValueConst val)
JS_FreeCString(ctx, str);
} else {
fprintf(f, "[exception]\n");
+ abort();
}
}
@@ -4222,9 +4354,11 @@ static void js_std_dump_error1(JSContext *ctx, JSValueConst exception_val)
{
JSValue val;
BOOL is_error;
-
+
is_error = JS_IsError(ctx, exception_val);
+ fprintf(stderr, "dumping error, is_error: %u\n", is_error);
js_dump_obj(ctx, stderr, exception_val);
+ fprintf(stderr, "dumped value\n");
if (is_error) {
val = JS_GetPropertyStr(ctx, exception_val, "stack");
if (!JS_IsUndefined(val)) {
diff --git a/taler_wallet_core_lib.c b/taler_wallet_core_lib.c
@@ -22,6 +22,13 @@
#include <pthread.h>
#include "quickjs/list.h"
#include <unistd.h>
+#include <string.h>
+
+extern const uint8_t qjsc_prelude[];
+extern const uint32_t qjsc_prelude_size;
+
+extern const uint8_t qjsc_wallet_core[];
+extern const uint32_t qjsc_wallet_core_size;
struct HostMessage {
struct list_head link;
@@ -29,18 +36,32 @@ struct HostMessage {
};
struct TALER_WALLET_Handle {
+ char *test;
+
JSRuntime *rt;
JSContext *ctx;
- TALER_WALLET_MessageHandlerFn *handler_f;
- void *handler_cls;
+ pthread_t wallet_thread;
- int hostmsg_send_pipe;
- int hostmsg_recv_pipe;
- pthread_mutex_t hostmsg_mutex;
- struct list_head hostmg_queue; /* list of HostMessage.link */
+ TALER_WALLET_MessageHandlerFn handler_f;
+ void *handler_cls;
};
+static int eval_buf(JSContext *ctx, const char *codestr, const char *filename)
+{
+ JSValue val;
+ int ret;
+ val = JS_Eval(ctx, codestr, strlen(codestr), filename, 0);
+ if (JS_IsException(val)) {
+ js_std_dump_error(ctx);
+ ret = -1;
+ } else {
+ ret = 0;
+ }
+ JS_FreeValue(ctx, val);
+ return ret;
+}
+
/* also used to initialize the worker context */
static JSContext *JS_NewCustomContext(JSRuntime *rt)
{
@@ -59,9 +80,10 @@ static JSContext *JS_NewCustomContext(JSRuntime *rt)
void
TALER_WALLET_set_handler(struct TALER_WALLET_Handle *h,
TALER_WALLET_MessageHandlerFn handler_f,
- void *handler_p)
+ void *handler_cls)
{
-
+ h->handler_cls = handler_cls;
+ h->handler_f = handler_f;
}
int
@@ -71,50 +93,84 @@ TALER_WALLET_send_message (struct TALER_WALLET_Handle *h,
return js_os_post_message_from_host(h->ctx, msg);
}
+static void
+wallet_host_message_handler(void *cls, const char *msg)
+{
+ struct TALER_WALLET_Handle *wh = cls;
+
+ if (wh->handler_f) {
+ wh->handler_f(wh->handler_cls, msg);
+ }
+}
+
struct TALER_WALLET_Handle *
TALER_WALLET_create(void)
{
struct TALER_WALLET_Handle *wh;
- JSRuntime *rt;
- JSContext *ctx;
+ wh = malloc(sizeof (*wh));
+ memset(wh, 0, sizeof *wh);
+ wh->test = "foo";
+
+ wh->rt = JS_NewRuntime();
+
+ return wh;
+}
+
+static void *
+run(void *cls)
+{
+ struct TALER_WALLET_Handle *wh = cls;
- rt = JS_NewRuntime();
- js_std_init_handlers(rt);
- ctx = JS_NewCustomContext(rt);
+ printf("TEST: %s\n", wh->test);
- if (!ctx) {
+ js_std_init_handlers(wh->rt);
+ wh->ctx = JS_NewCustomContext(wh->rt);
+
+
+ if (!wh->ctx) {
fprintf(stderr, "qjs: cannot allocate JS context\n");
return NULL;
}
- JS_SetHostPromiseRejectionTracker(rt, js_std_promise_rejection_tracker,
+ eval_buf(wh->ctx, "console.log('hi');", "<talerwallet>");
+
+ JS_SetHostPromiseRejectionTracker(wh->rt, js_std_promise_rejection_tracker,
NULL);
- wh = malloc(sizeof (*wh));
- wh->ctx = ctx;
- wh->rt = rt;
- wh->handler_cls = NULL;
- wh->handler_f = NULL;
- if (0 != pthread_mutex_init(&wh->hostmsg_mutex, NULL)) {
- return NULL;
- }
+ js_std_add_helpers(wh->ctx, 0, NULL);
+ js_std_eval_binary(wh->ctx, qjsc_prelude, qjsc_prelude_size, 0);
+ js_std_eval_binary(wh->ctx, qjsc_wallet_core, qjsc_wallet_core_size, 0);
- return wh;
-}
+ js_os_set_host_message_handler(wh->ctx, wallet_host_message_handler, wh);
-static void *
-run(void *cls)
-{
- struct TALER_WALLET_Handle *wh = cls;
+ printf("starting main loop\n");
- js_std_loop(wh->ctx);
+ eval_buf(wh->ctx, "console.log('hi');", "<talerwallet>");
+ eval_buf(wh->ctx, "console.log(typeof testWithLocal);", "<talerwallet>");
+ eval_buf(wh->ctx, "console.log('hello, world, bla!');", "<talerwallet>");
+ eval_buf(wh->ctx, "testWithLocal();", "<talerwallet>");
+
+ js_std_loop(wh->ctx);
+
+ printf("done with main loop\n");
}
void
TALER_WALLET_run (struct TALER_WALLET_Handle *wh)
{
- pthread_t wallet_thread;
+ pthread_t wallet_thread;
+ char *line;
+ size_t line_sz;
- pthread_create(&wallet_thread, NULL, run, wh);
+ //run(wh);
+
+ pthread_create(&wallet_thread, NULL, run, wh);
+
+ wh->wallet_thread = wallet_thread;
+}
+
+void TALER_WALLET_join(struct TALER_WALLET_Handle *wh)
+{
+ pthread_join(wh->wallet_thread, NULL);
}
diff --git a/taler_wallet_core_lib.h b/taler_wallet_core_lib.h
@@ -82,6 +82,9 @@ TALER_WALLET_send_message (struct TALER_WALLET_Handle *h,
void
TALER_WALLET_run (struct TALER_WALLET_Handle *h);
+void
+TALER_WALLET_join (struct TALER_WALLET_Handle *wh);
+
/**
* Destroy the wallet handle and free resources associated with it.
*
diff --git a/tart_module.c b/tart_module.c
@@ -578,9 +578,9 @@ static JSValue js_talercrypto_eddsa_verify(JSContext *ctx, JSValue this_val,
*/
static int
kdf(void *okm, size_t okm_len,
- const void *ikm, size_t ikm_len,
- const void *salt, size_t salt_len,
- const void *info, size_t info_len)
+ const void *ikm, size_t ikm_len,
+ const void *salt, size_t salt_len,
+ const void *info, size_t info_len)
{
const mbedtls_md_info_t *md_extract;
const mbedtls_md_info_t *md_expand;
@@ -599,7 +599,7 @@ kdf(void *okm, size_t okm_len,
ret = mbedtls_hkdf_extract(md_extract, salt, salt_len, ikm, ikm_len, prk);
if (ret != 0) {
- return -1;
+ return ret;
}
ret = mbedtls_hkdf_expand(md_expand, prk, mbedtls_md_get_size(md_extract),
@@ -660,7 +660,8 @@ static JSValue js_talercrypto_kdf(JSContext *ctx, JSValue this_val,
ret = kdf(okm, okm_len, ikm, ikm_len, salt, salt_len, info, info_len);
if (ret != 0) {
- return JS_EXCEPTION;
+ JS_ThrowInternalError(ctx, "kdf() call failed");
+ goto exception;
}
ret_val = make_js_ta_copy(ctx, okm, okm_len);
done:
diff --git a/wallet-client-example.c b/wallet-client-example.c
@@ -38,5 +38,7 @@ int main(int argc, char **argv)
TALER_WALLET_run(wh);
- TALER_WALLET_send_message(wh, "{}");
+ //TALER_WALLET_send_message(wh, "{}");
+
+ TALER_WALLET_join(wh);
}