Skip to content

Commit d72c3af

Browse files
gh-120: Fix extension hook registration.
The previous commit's message was incorrect. That commit was actually addressing gh-119.
1 parent c52b7d1 commit d72c3af

5 files changed

Lines changed: 149 additions & 7 deletions

File tree

src/extensions.c

Lines changed: 102 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,26 @@ static char* g_cwd_dir = NULL;
3939
static const char* g_loading_extension_name = NULL;
4040
static const char* g_loading_scope_name = NULL;
4141

42+
// Registered event handlers and periodic hooks
43+
typedef struct EventHandler {
44+
char* event_name;
45+
prefix_event_fn fn;
46+
char* owner;
47+
struct EventHandler* next;
48+
} EventHandler;
49+
50+
typedef struct PeriodicHook {
51+
int n;
52+
prefix_event_fn fn;
53+
char* owner;
54+
struct PeriodicHook* next;
55+
} PeriodicHook;
56+
57+
static EventHandler* g_event_handlers = NULL;
58+
static PeriodicHook* g_periodic_hooks = NULL;
59+
static prefix_repl_fn g_repl_handler = NULL;
60+
static char* g_repl_owner = NULL;
61+
4262
static void set_error(char** error_out, const char* msg) {
4363
if (!error_out) return;
4464
free(*error_out);
@@ -389,19 +409,41 @@ static int ctx_register_operator(const char* name, prefix_operator_fn fn, int fl
389409
}
390410

391411
static int ctx_register_periodic_hook(int n, prefix_event_fn fn) {
392-
(void)n;
393-
(void)fn;
412+
if (n <= 0 || !fn) return -1;
413+
PeriodicHook* h = calloc(1, sizeof(PeriodicHook));
414+
if (!h) return -1;
415+
h->n = n;
416+
h->fn = fn;
417+
h->owner = g_loading_extension_name ? strdup(g_loading_extension_name) : strdup("extension");
418+
if (!h->owner) { free(h); return -1; }
419+
h->next = g_periodic_hooks;
420+
g_periodic_hooks = h;
394421
return 0;
395422
}
396423

397424
static int ctx_register_event_handler(const char* event_name, prefix_event_fn fn) {
398-
(void)event_name;
399-
(void)fn;
425+
if (!event_name || !fn) return -1;
426+
EventHandler* h = calloc(1, sizeof(EventHandler));
427+
if (!h) return -1;
428+
h->event_name = strdup(event_name);
429+
if (!h->event_name) { free(h); return -1; }
430+
h->fn = fn;
431+
h->owner = g_loading_extension_name ? strdup(g_loading_extension_name) : strdup("extension");
432+
if (!h->owner) { free(h->event_name); free(h); return -1; }
433+
h->next = g_event_handlers;
434+
g_event_handlers = h;
400435
return 0;
401436
}
402437

403438
static int ctx_register_repl_handler(prefix_repl_fn repl_fn) {
404-
(void)repl_fn;
439+
if (!repl_fn) return -1;
440+
g_repl_handler = repl_fn;
441+
free(g_repl_owner);
442+
g_repl_owner = g_loading_extension_name ? strdup(g_loading_extension_name) : strdup("extension");
443+
if (!g_repl_owner) {
444+
// keep handler but owner strdup failed; set to NULL
445+
g_repl_owner = NULL;
446+
}
405447
return 0;
406448
}
407449

@@ -448,6 +490,35 @@ void extensions_set_runtime_dirs(const char* interpreter_dir, const char* cwd_di
448490
g_cwd_dir = cwd_dir ? strdup(cwd_dir) : NULL;
449491
}
450492

493+
int extensions_fire_event(Interpreter* interp, const char* event_name) {
494+
if (!event_name) return 0;
495+
int invoked = 0;
496+
for (EventHandler* h = g_event_handlers; h; h = h->next) {
497+
if (h->event_name && strcmp(h->event_name, event_name) == 0) {
498+
if (h->fn) h->fn(interp, event_name);
499+
invoked++;
500+
}
501+
}
502+
return invoked;
503+
}
504+
505+
void extensions_run_periodic_hooks(Interpreter* interp) {
506+
if (!interp) return;
507+
if (!g_periodic_hooks) return;
508+
int step_index = interp->trace_next_step_index - 1;
509+
if (step_index < 0) return;
510+
for (PeriodicHook* p = g_periodic_hooks; p; p = p->next) {
511+
if (p->n > 0 && (step_index % p->n) == 0) {
512+
if (p->fn) p->fn(interp, "periodic");
513+
}
514+
}
515+
}
516+
517+
int extensions_call_repl_handler(void) {
518+
if (!g_repl_handler) return -1;
519+
return g_repl_handler();
520+
}
521+
451522
static int extension_register_exposure(LoadedExtension* le,
452523
const char* ext_name,
453524
const char* scope_name,
@@ -647,4 +718,30 @@ void extensions_shutdown(void) {
647718

648719
g_loading_extension_name = NULL;
649720
g_loading_scope_name = NULL;
721+
722+
// Free registered event handlers
723+
EventHandler* eh = g_event_handlers;
724+
while (eh) {
725+
EventHandler* en = eh->next;
726+
free(eh->event_name);
727+
free(eh->owner);
728+
free(eh);
729+
eh = en;
730+
}
731+
g_event_handlers = NULL;
732+
733+
// Free periodic hooks
734+
PeriodicHook* ph = g_periodic_hooks;
735+
while (ph) {
736+
PeriodicHook* pn = ph->next;
737+
free(ph->owner);
738+
free(ph);
739+
ph = pn;
740+
}
741+
g_periodic_hooks = NULL;
742+
743+
// Repl handler owner
744+
free(g_repl_owner);
745+
g_repl_owner = NULL;
746+
g_repl_handler = NULL;
650747
}

src/extensions.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#define EXTENSIONS_H
33

44
#include "common.h"
5+
#include "interpreter.h"
56

67
// Configure directories used for extension-path fallback resolution.
78
// interpreter_dir should be the directory containing the interpreter executable.
@@ -28,4 +29,17 @@ int extensions_load_named(const char* spec,
2829
// Unload all loaded extension libraries.
2930
void extensions_shutdown(void);
3031

32+
// Fire a named event to all registered event handlers. Returns number of
33+
// handlers invoked (may be 0).
34+
int extensions_fire_event(Interpreter* interp, const char* event_name);
35+
36+
// Evaluate and invoke any periodic hooks that are due at the interpreter's
37+
// current rewrite step. Safe to call after a step has been logged.
38+
void extensions_run_periodic_hooks(Interpreter* interp);
39+
40+
// If a REPL replacement handler has been registered by an extension, call
41+
// it and return its integer return-code. Returns -1 if no repl handler is
42+
// registered.
43+
int extensions_call_repl_handler(void);
44+
3145
#endif // EXTENSIONS_H

src/interpreter.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "interpreter.h"
22
#include "builtins.h"
33
#include "ns_buffer.h"
4+
#include "extensions.h"
45
#include <stdio.h>
56
#include <stdlib.h>
67
#include <string.h>
@@ -1666,7 +1667,10 @@ Value eval_expr(Interpreter* interp, Expr* expr, Env* env) {
16661667
}
16671668

16681669
LabelMap local_labels = {0};
1670+
// Notify extensions about call boundaries
1671+
extensions_fire_event(interp, "before_call");
16691672
ExecResult res = exec_stmt(interp, user_func->body, call_env, &local_labels);
1673+
extensions_fire_event(interp, "after_call");
16701674

16711675
// Clean up labels
16721676
for (size_t i = 0; i < local_labels.count; i++) value_free(local_labels.items[i].key);
@@ -2553,6 +2557,11 @@ static ExecResult exec_stmt(Interpreter* interp, Stmt* stmt, Env* env, LabelMap*
25532557
if (!stmt) return make_ok(value_null());
25542558
trace_log_step(interp, stmt, env);
25552559

2560+
// Run any periodic hooks that are due for this rewrite step and notify
2561+
// extensions that a statement is about to execute.
2562+
extensions_run_periodic_hooks(interp);
2563+
extensions_fire_event(interp, "before_statement");
2564+
25562565
// If running in a background thread and a STOP has been requested for
25572566
// this thread, terminate execution cooperatively by returning early.
25582567
if (interpreter_thr_should_stop(interp)) {
@@ -3555,6 +3564,8 @@ static ExecResult exec_stmt_list(Interpreter* interp, StmtList* list, Env* env,
35553564
while (i < list->count) {
35563565
wait_if_paused(interp);
35573566
ExecResult res = exec_stmt(interp, list->items[i], env, labels);
3567+
// Notify extensions that a statement has completed (regardless of outcome)
3568+
extensions_fire_event(interp, "after_statement");
35583569

35593570
if (res.status == EXEC_ERROR || res.status == EXEC_RETURN ||
35603571
res.status == EXEC_BREAK || res.status == EXEC_CONTINUE) {
@@ -3667,12 +3678,16 @@ ExecResult exec_program(Stmt* program, const char* source_path) {
36673678
interpreter_init(&interp, source_path, false, false);
36683679

36693680
LabelMap labels = {0};
3681+
extensions_fire_event(&interp, "program_start");
36703682
ExecResult res = exec_stmt_list(&interp, &program->as.block, interp.global_env, &labels);
36713683

36723684
if (res.status == EXEC_ERROR) {
3685+
extensions_fire_event(&interp, "on_error");
36733686
char* tb = interpreter_format_traceback(&interp, res.error, res.error_line, res.error_column);
36743687
free(res.error);
36753688
res.error = tb;
3689+
} else {
3690+
extensions_fire_event(&interp, "program_end");
36763691
}
36773692

36783693
// Clean up
@@ -3745,12 +3760,19 @@ ExecResult exec_program_in_env(Interpreter* interp, Stmt* program, Env* env) {
37453760
}
37463761

37473762
LabelMap labels = {0};
3763+
// Notify extensions program is starting
3764+
extensions_fire_event(interp, "program_start");
37483765
ExecResult res = exec_stmt_list(interp, &program->as.block, env, &labels);
37493766

37503767
if (res.status == EXEC_ERROR) {
3768+
// Notify extensions about the error
3769+
extensions_fire_event(interp, "on_error");
37513770
char* tb = interpreter_format_traceback(interp, res.error, res.error_line, res.error_column);
37523771
free(res.error);
37533772
res.error = tb;
3773+
} else {
3774+
// Notify extensions program ended normally
3775+
extensions_fire_event(interp, "program_end");
37543776
}
37553777

37563778
for (size_t i = 0; i < labels.count; i++) value_free(labels.items[i].key);

src/main.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,13 @@ int main(int argc, char** argv) {
317317
}
318318

319319
if (!path && !source_mode) {
320-
int repl_rc = run_repl(verbose_flag, private_flag);
320+
int repl_rc = extensions_call_repl_handler();
321+
if (repl_rc >= 0) {
322+
extensions_shutdown();
323+
builtins_reset_dynamic();
324+
return repl_rc;
325+
}
326+
repl_rc = run_repl(verbose_flag, private_flag);
321327
extensions_shutdown();
322328
builtins_reset_dynamic();
323329
return repl_rc;

src/prefix_runtime.def

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,4 +144,7 @@ EXPORTS
144144
value_alias
145145
value_deep_copy
146146
value_free
147-
value_type_name
147+
value_type_name
148+
extensions_fire_event
149+
extensions_run_periodic_hooks
150+
extensions_call_repl_handler

0 commit comments

Comments
 (0)