From 8f37a8beab473988e094b63d9f5854522761a0bc Mon Sep 17 00:00:00 2001
From: Witold Filipczyk <witekfl@poczta.onet.pl>
Date: Fri, 26 Dec 2025 17:13:24 +0100
Subject: [PATCH] [js] Changed how ecmascript_timeout is stored. Refs #408

Likely there is a memory leak in kill_timer, but this will
adressed later.
---
 src/js/Makefile                |  6 +--
 src/js/ecmascript.c            | 62 +++++++++++++++-------
 src/js/ecmascript.h            |  9 ++--
 src/js/meson.build             |  6 +--
 src/js/mujs/window.c           | 36 +++++--------
 src/js/quickjs/window.c        | 60 ++++++----------------
 src/js/spidermonkey/window.cpp | 34 +++++-------
 src/js/timer.c                 | 94 ----------------------------------
 src/js/timer.cpp               | 61 ++++++++++++++++++++++
 src/js/timer.h                 |  3 +-
 src/main/timer.c               |  5 +-
 11 files changed, 162 insertions(+), 214 deletions(-)
 delete mode 100644 src/js/timer.c
 create mode 100644 src/js/timer.cpp

diff --git a/src/js/Makefile b/src/js/Makefile
index 90b62cd93..a57f9a437 100644
--- a/src/js/Makefile
+++ b/src/js/Makefile
@@ -10,11 +10,11 @@ SUBDIRS-$(CONFIG_QUICKJS)	+= quickjs
 
 SUBDIRS-$(CONFIG_ECMASCRIPT_SMJS)	+= spidermonkey
 
-OBJS-$(CONFIG_ECMASCRIPT_SMJS)		+= ecmascript.obj ecmascript-c.obj localstorage-db.o spidermonkey.obj timer.o
+OBJS-$(CONFIG_ECMASCRIPT_SMJS)		+= ecmascript.obj ecmascript-c.obj localstorage-db.o spidermonkey.obj timer.obj
 
-OBJS-$(CONFIG_MUJS)		+= ecmascript.o ecmascript-c.o localstorage-db.o mujs.o timer.o
+OBJS-$(CONFIG_MUJS)		+= ecmascript.o ecmascript-c.o localstorage-db.o mujs.o timer.obj
 
-OBJS-$(CONFIG_QUICKJS)		+= ecmascript.o ecmascript-c.o localstorage-db.o quickjs.o timer.o
+OBJS-$(CONFIG_QUICKJS)		+= ecmascript.o ecmascript-c.o localstorage-db.o quickjs.o timer.obj
 
 ifeq ($(CONFIG_ECMASCRIPT_SMJS), yes)
 CONFIG_ANY_SPIDERMONKEY = yes
diff --git a/src/js/ecmascript.c b/src/js/ecmascript.c
index 27b167687..1f9027005 100644
--- a/src/js/ecmascript.c
+++ b/src/js/ecmascript.c
@@ -588,10 +588,10 @@ ecmascript_timeout_handler(void *val)
 
 	if (t->timeout_next > 0) {
 		install_timer(&t->tid, t->timeout_next, ecmascript_timeout_handler, t);
-		add_to_map_timer(t);
 	} else {
 skip:
 	/* The expired timer ID has now been erased.  */
+		del_from_map_timer(t);
 		t->tid = TIMER_ID_UNDEF;
 		del_from_list(t);
 		done_string(&t->code);
@@ -674,9 +674,9 @@ ecmascript_timeout_handler2(void *val)
 	}
 	if (t->timeout_next > 0) {
 		install_timer(&t->tid, t->timeout_next, ecmascript_timeout_handler2, t);
-		add_to_map_timer(t);
 	} else {
 skip:
+		del_from_map_timer(t);
 		t->tid = TIMER_ID_UNDEF;
 		/* The expired timer ID has now been erased.  */
 		del_from_list(t);
@@ -697,7 +697,9 @@ ecmascript_timeout_handler2(void *val)
 }
 #endif
 
-struct ecmascript_timeout *
+static uint32_t timeout_id;
+
+uint32_t
 ecmascript_set_timeout(void *c, char *code, int timeout, int timeout_next)
 {
 	ELOG
@@ -715,18 +717,25 @@ ecmascript_set_timeout(void *c, char *code, int timeout, int timeout_next)
 	struct ecmascript_interpreter *interpreter = (struct ecmascript_interpreter *)js_getcontext(ctx);
 #endif
 	assert(interpreter && interpreter->vs->doc_view->document);
-	if (!code) return NULL;
+	if (!code) {
+		return 0;
+	}
 	struct ecmascript_timeout *t = (struct ecmascript_timeout *)mem_calloc(1, sizeof(*t));
 
 	if (!t) {
 		mem_free(code);
-		return NULL;
+		return 0;
 	}
 	if (!init_string(&t->code)) {
 		mem_free(t);
 		mem_free(code);
-		return NULL;
+		return 0;
+	}
+	timeout_id++;
+	if (!timeout_id) {
+		timeout_id++;
 	}
+	t->timeout_id = timeout_id;
 	add_to_string(&t->code, code);
 	mem_free(code);
 
@@ -740,7 +749,7 @@ ecmascript_set_timeout(void *c, char *code, int timeout, int timeout_next)
 	install_timer(&t->tid, timeout, ecmascript_timeout_handler, t);
 	add_to_map_timer(t);
 
-	return t;
+	return timeout_id;
 }
 
 #ifdef CONFIG_ECMASCRIPT_SMJS
@@ -764,7 +773,7 @@ ecmascript_set_request2(void *c, JS::HandleValue f)
 	return interpreter->request;
 }
 
-struct ecmascript_timeout *
+uint32_t
 ecmascript_set_timeout2(void *c, JS::HandleValue f, int timeout, int timeout_next)
 {
 	ELOG
@@ -776,12 +785,17 @@ ecmascript_set_timeout2(void *c, JS::HandleValue f, int timeout, int timeout_nex
 	struct ecmascript_timeout *t = (struct ecmascript_timeout *)mem_calloc(1, sizeof(*t));
 
 	if (!t) {
-		return NULL;
+		return 0;
 	}
 	if (!init_string(&t->code)) {
 		mem_free(t);
-		return NULL;
+		return 0;
 	}
+	timeout_id++;
+	if (!timeout_id) {
+		timeout_id++;
+	}
+	t->timeout_id = timeout_id;
 	t->interpreter = interpreter;
 	t->ctx = ctx;
 	t->timeout_next = timeout_next;
@@ -791,7 +805,7 @@ ecmascript_set_timeout2(void *c, JS::HandleValue f, int timeout, int timeout_nex
 	install_timer(&t->tid, timeout, ecmascript_timeout_handler2, t);
 	add_to_map_timer(t);
 
-	return t;
+	return timeout_id;
 }
 #endif
 
@@ -812,7 +826,7 @@ ecmascript_set_request2(void *c, JSValueConst fun)
 }
 
 
-struct ecmascript_timeout *
+uint32_t
 ecmascript_set_timeout2q(void *c, JSValueConst fun, int timeout, int timeout_next)
 {
 	ELOG
@@ -822,12 +836,17 @@ ecmascript_set_timeout2q(void *c, JSValueConst fun, int timeout, int timeout_nex
 	struct ecmascript_timeout *t = (struct ecmascript_timeout *)mem_calloc(1, sizeof(*t));
 
 	if (!t) {
-		return NULL;
+		return 0;
 	}
 	if (!init_string(&t->code)) {
 		mem_free(t);
-		return NULL;
+		return 0;
+	}
+	timeout_id++;
+	if (!timeout_id) {
+		timeout_id++;
 	}
+	t->timeout_id = timeout_id;
 	t->interpreter = interpreter;
 	t->ctx = ctx;
 	t->timeout_next = timeout_next;
@@ -836,7 +855,7 @@ ecmascript_set_timeout2q(void *c, JSValueConst fun, int timeout, int timeout_nex
 	install_timer(&t->tid, timeout, ecmascript_timeout_handler2, t);
 	add_to_map_timer(t);
 
-	return t;
+	return timeout_id;
 }
 #endif
 
@@ -854,7 +873,7 @@ ecmascript_set_request2(js_State *J, const char *handle)
 	return interpreter->request;
 }
 
-struct ecmascript_timeout *
+uint32_t
 ecmascript_set_timeout2m(js_State *J, const char *handle, int timeout, int timeout_next)
 {
 	ELOG
@@ -864,12 +883,17 @@ ecmascript_set_timeout2m(js_State *J, const char *handle, int timeout, int timeo
 	struct ecmascript_timeout *t = (struct ecmascript_timeout *)mem_calloc(1, sizeof(*t));
 
 	if (!t) {
-		return NULL;
+		return 0;
 	}
 	if (!init_string(&t->code)) {
 		mem_free(t);
-		return NULL;
+		return 0;
+	}
+	timeout_id++;
+	if (!timeout_id) {
+		timeout_id++;
 	}
+	t->timeout_id = timeout_id;
 	t->interpreter = interpreter;
 	t->ctx = J;
 	t->fun = handle;
@@ -879,7 +903,7 @@ ecmascript_set_timeout2m(js_State *J, const char *handle, int timeout, int timeo
 	install_timer(&t->tid, timeout, ecmascript_timeout_handler2, t);
 	add_to_map_timer(t);
 
-	return t;
+	return timeout_id;
 }
 #endif
 
diff --git a/src/js/ecmascript.h b/src/js/ecmascript.h
index 8479600c7..f0b3537ea 100644
--- a/src/js/ecmascript.h
+++ b/src/js/ecmascript.h
@@ -144,6 +144,7 @@ struct ecmascript_timeout {
 	struct ecmascript_interpreter *interpreter;
 	timer_id_T tid;
 	int timeout_next;
+	uint32_t timeout_id;
 };
 
 struct delayed_goto {
@@ -179,20 +180,20 @@ void ecmascript_timeout_dialog(struct terminal *term, int max_exec_time);
 
 void ecmascript_set_action(char **action, char *string);
 
-struct ecmascript_timeout *ecmascript_set_timeout(void *ctx, char *code, int timeout, int timeout_next);
+uint32_t ecmascript_set_timeout(void *ctx, char *code, int timeout, int timeout_next);
 
 #ifdef CONFIG_ECMASCRIPT_SMJS
-struct ecmascript_timeout *ecmascript_set_timeout2(void *ctx, JS::HandleValue f, int timeout, int timeout_next);
+uint32_t ecmascript_set_timeout2(void *ctx, JS::HandleValue f, int timeout, int timeout_next);
 int ecmascript_set_request2(void *ctx, JS::HandleValue f);
 #endif
 
 #ifdef CONFIG_QUICKJS
-struct ecmascript_timeout *ecmascript_set_timeout2q(void *ctx, JSValue f, int timeout, int timeout_next);
+uint32_t ecmascript_set_timeout2q(void *ctx, JSValue f, int timeout, int timeout_next);
 int ecmascript_set_request2(void *ctx, JSValue f);
 #endif
 
 #ifdef CONFIG_MUJS
-struct ecmascript_timeout *ecmascript_set_timeout2m(js_State *J, const char *handle, int timeout, int timeout_next);
+uint32_t ecmascript_set_timeout2m(js_State *J, const char *handle, int timeout, int timeout_next);
 int ecmascript_set_request2(js_State *J, const char *handle);
 #endif
 
diff --git a/src/js/meson.build b/src/js/meson.build
index 8c827913d..f577e704c 100644
--- a/src/js/meson.build
+++ b/src/js/meson.build
@@ -6,7 +6,7 @@ if conf_data.get('CONFIG_ECMASCRIPT_SMJS') or conf_data.get('CONFIG_QUICKJS')
 endif
 
 if conf_data.get('CONFIG_ECMASCRIPT_SMJS')
-	srcs += files('ecmascript.cpp', 'ecmascript-c.cpp', 'localstorage-db.c', 'spidermonkey.cpp', 'timer.c')
+	srcs += files('ecmascript.cpp', 'ecmascript-c.cpp', 'localstorage-db.c', 'spidermonkey.cpp', 'timer.cpp')
 	subdir('spidermonkey')
 endif
 
@@ -23,12 +23,12 @@ if CONFIG_ANY_SPIDERMONKEY
 endif
 
 if conf_data.get('CONFIG_MUJS')
-	srcs += files('ecmascript.c', 'ecmascript-c.c', 'localstorage-db.c', 'mujs.c', 'timer.c')
+	srcs += files('ecmascript.c', 'ecmascript-c.c', 'localstorage-db.c', 'mujs.c', 'timer.cpp')
 	subdir('mujs')
 endif
 
 if conf_data.get('CONFIG_QUICKJS')
-	srcs += files('ecmascript.c', 'ecmascript-c.c', 'localstorage-db.c', 'quickjs.c', 'timer.c')
+	srcs += files('ecmascript.c', 'ecmascript-c.c', 'localstorage-db.c', 'quickjs.c', 'timer.cpp')
 	subdir('quickjs')
 endif
 
diff --git a/src/js/mujs/window.c b/src/js/mujs/window.c
index 97ff5d70c..33a31ce46 100644
--- a/src/js/mujs/window.c
+++ b/src/js/mujs/window.c
@@ -417,10 +417,10 @@ mjs_window_clearInterval(js_State *J)
 		return;
 	}
 
-	uintptr_t number = (uintptr_t)atoll(text);
-	struct ecmascript_timeout *t = (struct ecmascript_timeout *)(number);
+	uint32_t number = atoi(text);
+	struct ecmascript_timeout *t = find_in_map_timer(number);
 
-	if (found_in_map_timer(t)) {
+	if (t) {
 		t->timeout_next = -1;
 	}
 	js_pushundefined(J);
@@ -462,10 +462,10 @@ mjs_window_clearTimeout(js_State *J)
 		return;
 	}
 
-	uintptr_t number = (uintptr_t)atoll(text);
-	struct ecmascript_timeout *t = (struct ecmascript_timeout *)(number);
+	uint32_t number = atoi(text);
+	struct ecmascript_timeout *t = find_in_map_timer(number);
 
-	if (found_in_map_timer(t)) {
+	if (t) {
 		t->timeout_next = -1;
 	}
 	js_pushundefined(J);
@@ -773,19 +773,15 @@ mjs_window_setInterval(js_State *J)
 		char *code2 = stracpy(code);
 
 		if (code2) {
-			struct ecmascript_timeout *id = ecmascript_set_timeout(J, code2, timeout, timeout);
-			char res[32];
-			snprintf(res, 31, "%" PRIuPTR, (uintptr_t)id);
-			js_pushstring(J, res);
+			uint32_t id = ecmascript_set_timeout(J, code2, timeout, timeout);
+			js_pushnumber(J, id);
 			return;
 		}
 	} else {
 		js_copy(J, 1);
 		const char *handle = js_ref(J);
-		struct ecmascript_timeout *id = ecmascript_set_timeout2m(J, handle, timeout, timeout);
-		char res[32];
-		snprintf(res, 31, "%" PRIuPTR, (uintptr_t)id);
-		js_pushstring(J, res);
+		uint32_t id = ecmascript_set_timeout2m(J, handle, timeout, timeout);
+		js_pushnumber(J, id);
 		return;
 	}
 	js_pushundefined(J);
@@ -817,19 +813,15 @@ mjs_window_setTimeout(js_State *J)
 		char *code2 = stracpy(code);
 
 		if (code2) {
-			struct ecmascript_timeout *id = ecmascript_set_timeout(J, code2, timeout, 0);
-			char res[32];
-			snprintf(res, 31, "%" PRIuPTR, (uintptr_t)id);
-			js_pushstring(J, res);
+			uint32_t id = ecmascript_set_timeout(J, code2, timeout, 0);
+			js_pushnumber(J, id);
 			return;
 		}
 	} else {
 		js_copy(J, 1);
 		const char *handle = js_ref(J);
-		struct ecmascript_timeout *id = ecmascript_set_timeout2m(J, handle, timeout, 0);
-		char res[32];
-		snprintf(res, 31, "%" PRIuPTR, (uintptr_t)id);
-		js_pushstring(J, res);
+		uint32_t id = ecmascript_set_timeout2m(J, handle, timeout, 0);
+		js_pushnumber(J, id);
 		return;
 	}
 	js_pushundefined(J);
diff --git a/src/js/quickjs/window.c b/src/js/quickjs/window.c
index 09a607135..bddfa2946 100644
--- a/src/js/quickjs/window.c
+++ b/src/js/quickjs/window.c
@@ -485,13 +485,9 @@ js_window_setInterval(JSContext *ctx, JSValueConst this_val, int argc, JSValueCo
 	func = argv[0];
 
 	if (JS_IsFunction(ctx, func)) {
-		struct ecmascript_timeout *id = ecmascript_set_timeout2q(ctx, func, timeout, timeout);
+		uint32_t id = ecmascript_set_timeout2q(ctx, func, timeout, timeout);
 
-#if SIZEOF_INTPTR_T == 4
-		return JS_NewInt32(ctx, (intptr_t)(id));
-#else
-		return JS_NewInt64(ctx, (intptr_t)(id));
-#endif
+		return JS_NewUint32(ctx, id);
 	}
 
 	if (JS_IsString(func)) {
@@ -504,13 +500,9 @@ js_window_setInterval(JSContext *ctx, JSValueConst this_val, int argc, JSValueCo
 		JS_FreeCString(ctx, code);
 
 		if (code2) {
-			struct ecmascript_timeout *id = ecmascript_set_timeout(ctx, code2, timeout, timeout);
+			uint32_t id = ecmascript_set_timeout(ctx, code2, timeout, timeout);
 
-#if SIZEOF_INTPTR_T == 4
-			return JS_NewInt32(ctx, (intptr_t)(id));
-#else
-			return JS_NewInt64(ctx, (intptr_t)(id));
-#endif
+			return JS_NewUint32(ctx, id);
 		}
 	}
 
@@ -547,12 +539,8 @@ js_window_setTimeout(JSContext *ctx, JSValueConst this_val, int argc, JSValueCon
 	func = argv[0];
 
 	if (JS_IsFunction(ctx, func)) {
-		struct ecmascript_timeout *id = ecmascript_set_timeout2q(ctx, func, timeout, 0);
-#if SIZEOF_INTPTR_T == 4
-		return JS_NewInt32(ctx, (intptr_t)(id));
-#else
-		return JS_NewInt64(ctx, (intptr_t)(id));
-#endif
+		uint32_t id = ecmascript_set_timeout2q(ctx, func, timeout, 0);
+		return JS_NewUint32(ctx, id);
 	}
 
 	if (JS_IsString(func)) {
@@ -565,12 +553,8 @@ js_window_setTimeout(JSContext *ctx, JSValueConst this_val, int argc, JSValueCon
 		JS_FreeCString(ctx, code);
 
 		if (code2) {
-			struct ecmascript_timeout *id = ecmascript_set_timeout(ctx, code2, timeout, 0);
-#if SIZEOF_INTPTR_T == 4
-			return JS_NewInt32(ctx, (intptr_t)(id));
-#else
-			return JS_NewInt64(ctx, (intptr_t)(id));
-#endif
+			uint32_t id = ecmascript_set_timeout(ctx, code2, timeout, 0);
+			return JS_NewUint32(ctx, id);
 		}
 	}
 
@@ -590,20 +574,14 @@ js_window_clearInterval(JSContext *ctx, JSValueConst this_val, int argc, JSValue
 	if (argc != 1 || JS_IsNull(argv[0])) {
 		return JS_UNDEFINED;
 	}
-	intptr_t number;
+	uint32_t number;
 
-#if SIZEOF_INTPTR_T == 4
-	if (JS_ToInt32(ctx, &number, argv[0])) {
-		return JS_UNDEFINED;
-	}
-#else
-	if (JS_ToInt64(ctx, &number, argv[0])) {
+	if (JS_ToUint32(ctx, &number, argv[0])) {
 		return JS_UNDEFINED;
 	}
-#endif
-	struct ecmascript_timeout *t = (struct ecmascript_timeout *)(number);
+	struct ecmascript_timeout *t = find_in_map_timer(number);
 
-	if (found_in_map_timer(t)) {
+	if (t) {
 		t->timeout_next = -1;
 	}
 
@@ -657,20 +635,14 @@ js_window_clearTimeout(JSContext *ctx, JSValueConst this_val, int argc, JSValueC
 	if (argc != 1 || JS_IsNull(argv[0])) {
 		return JS_UNDEFINED;
 	}
-	intptr_t number;
+	uint32_t number;
 
-#if SIZEOF_INTPTR_T == 4
-	if (JS_ToInt32(ctx, &number, argv[0])) {
+	if (JS_ToUint32(ctx, &number, argv[0])) {
 		return JS_UNDEFINED;
 	}
-#else
-	if (JS_ToInt64(ctx, &number, argv[0])) {
-		return JS_UNDEFINED;
-	}
-#endif
-	struct ecmascript_timeout *t = (struct ecmascript_timeout *)(number);
+	struct ecmascript_timeout *t = find_in_map_timer(number);
 
-	if (found_in_map_timer(t)) {
+	if (t) {
 		t->timeout_next = -1;
 	}
 
diff --git a/src/js/spidermonkey/window.cpp b/src/js/spidermonkey/window.cpp
index 9fb73fd6c..141a8694b 100644
--- a/src/js/spidermonkey/window.cpp
+++ b/src/js/spidermonkey/window.cpp
@@ -899,14 +899,12 @@ window_setInterval(JSContext *ctx, unsigned int argc, JS::Value *rval)
 			return true;
 		}
 
-		struct ecmascript_timeout *id = ecmascript_set_timeout(ctx, code, timeout, timeout);
-		JS::BigInt *bi = JS::NumberToBigInt(ctx, reinterpret_cast<int64_t>(id));
-		args.rval().setBigInt(bi);
+		uint32_t id = ecmascript_set_timeout(ctx, code, timeout, timeout);
+		args.rval().setInt32(id);
 		return true;
 	}
-	struct ecmascript_timeout *id = ecmascript_set_timeout2(ctx, args[0], timeout, timeout);
-	JS::BigInt *bi = JS::NumberToBigInt(ctx, reinterpret_cast<int64_t>(id));
-	args.rval().setBigInt(bi);
+	uint32_t id = ecmascript_set_timeout2(ctx, args[0], timeout, timeout);
+	args.rval().setInt32(id);
 
 	return true;
 }
@@ -978,14 +976,12 @@ window_setTimeout(JSContext *ctx, unsigned int argc, JS::Value *rval)
 			return true;
 		}
 
-		struct ecmascript_timeout *id = ecmascript_set_timeout(ctx, code, timeout, 0);
-		JS::BigInt *bi = JS::NumberToBigInt(ctx, reinterpret_cast<int64_t>(id));
-		args.rval().setBigInt(bi);
+		uint32_t id = ecmascript_set_timeout(ctx, code, timeout, 0);
+		args.rval().setInt32(id);
 		return true;
 	}
-	struct ecmascript_timeout *id = ecmascript_set_timeout2(ctx, args[0], timeout, 0);
-	JS::BigInt *bi = JS::NumberToBigInt(ctx, reinterpret_cast<int64_t>(id));
-	args.rval().setBigInt(bi);
+	uint32_t id = ecmascript_set_timeout2(ctx, args[0], timeout, 0);
+	args.rval().setInt32(id);
 
 	return true;
 }
@@ -1010,11 +1006,10 @@ window_clearInterval(JSContext *ctx, unsigned int argc, JS::Value *rval)
 	if (argc != 1 || args[0].isNull()) {
 		return true;
 	}
-	JS::BigInt *bi = JS::ToBigInt(ctx, args[0]);
-	int64_t number = JS::ToBigInt64(bi);
-	struct ecmascript_timeout *t = reinterpret_cast<struct ecmascript_timeout *>(number);
+	uint32_t id  = args[0].toInt32();
+	struct ecmascript_timeout *t = find_in_map_timer(id);
 
-	if (found_in_map_timer(t)) {
+	if (t) {
 		t->timeout_next = -1;
 	}
 	return true;
@@ -1077,11 +1072,10 @@ window_clearTimeout(JSContext *ctx, unsigned int argc, JS::Value *rval)
 	if (argc != 1 || args[0].isNull()) {
 		return true;
 	}
-	JS::BigInt *bi = JS::ToBigInt(ctx, args[0]);
-	int64_t number = JS::ToBigInt64(bi);
-	struct ecmascript_timeout *t = reinterpret_cast<struct ecmascript_timeout *>(number);
+	uint32_t id = args[0].toInt32();
+	struct ecmascript_timeout *t = find_in_map_timer(id);
 
-	if (found_in_map_timer(t)) {
+	if (t) {
 		t->timeout_next = -1;
 	}
 	return true;
diff --git a/src/js/timer.c b/src/js/timer.c
deleted file mode 100644
index bfca96f19..000000000
--- a/src/js/timer.c
+++ /dev/null
@@ -1,94 +0,0 @@
-/* ECMAScript timer */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <stdlib.h>
-#include <stdio.h>
-
-#include "elinks.h"
-
-#include "js/timer.h"
-#include "util/hash.h"
-#include "util/string.h"
-
-static struct hash *map_timer;
-
-void
-init_map_timer(void)
-{
-	ELOG
-	map_timer = init_hash8();
-}
-
-void
-done_map_timer(void)
-{
-	ELOG
-	struct hash_item *item;
-	int i;
-
-	if (!map_timer) {
-		return;
-	}
-
-	foreach_hash_item (item, *map_timer, i) {
-		mem_free_set(&item->key, NULL);
-	}
-	free_hash(&map_timer);
-}
-
-void
-add_to_map_timer(struct ecmascript_timeout *t)
-{
-	ELOG
-	if (map_timer && t) {
-		char *key = memacpy((const char *)&t, sizeof(t));
-
-		if (key) {
-			add_hash_item(map_timer, key, sizeof(t), (void *)(intptr_t)1);
-		}
-	}
-}
-
-void
-del_from_map_timer(struct ecmascript_timeout *t)
-{
-	ELOG
-	if (map_timer && t) {
-		char *key = memacpy((const char *)&t, sizeof(t));
-
-		if (key) {
-			struct hash_item *item = get_hash_item(map_timer, key, sizeof(t));
-
-			if (item) {
-				mem_free_set(&item->key, NULL);
-				del_hash_item(map_timer, item);
-			}
-			mem_free(key);
-		}
-	}
-}
-
-bool
-found_in_map_timer(struct ecmascript_timeout *t)
-{
-	ELOG
-	bool ret = false;
-
-	if (map_timer && t) {
-		char *key = memacpy((const char *)&t, sizeof(t));
-
-		if (key) {
-			struct hash_item *item = get_hash_item(map_timer, key, sizeof(t));
-
-			if (item) {
-				ret = true;
-			}
-			mem_free(key);
-		}
-	}
-
-	return ret;
-}
diff --git a/src/js/timer.cpp b/src/js/timer.cpp
new file mode 100644
index 000000000..6b67ad541
--- /dev/null
+++ b/src/js/timer.cpp
@@ -0,0 +1,61 @@
+/* ECMAScript timer */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+
+#include "elinks.h"
+
+#include "js/ecmascript.h"
+#include "js/timer.h"
+#include "util/string.h"
+
+#include <map>
+
+static std::map<uint32_t, struct ecmascript_timeout *> map_timer;
+
+void
+init_map_timer(void)
+{
+	ELOG
+}
+
+void
+done_map_timer(void)
+{
+	ELOG
+
+	for (auto it = map_timer.begin(); it != map_timer.end(); it++) {
+		map_timer.erase(it);
+	}
+}
+
+void
+add_to_map_timer(struct ecmascript_timeout *t)
+{
+	ELOG
+	map_timer[t->timeout_id] = t;
+}
+
+void
+del_from_map_timer(struct ecmascript_timeout *t)
+{
+	ELOG
+	map_timer.erase(t->timeout_id);
+}
+
+struct ecmascript_timeout *
+find_in_map_timer(uint32_t timeout_id)
+{
+	ELOG
+	auto search = map_timer.find(timeout_id);
+
+	if (search == map_timer.end()) {
+		return NULL;
+	}
+	return search->second;
+}
diff --git a/src/js/timer.h b/src/js/timer.h
index 38aa561d2..bc64f68b7 100644
--- a/src/js/timer.h
+++ b/src/js/timer.h
@@ -2,6 +2,7 @@
 #define EL__JS_TIMER_H
 
 #include <stdbool.h>
+#include <stdint.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -13,7 +14,7 @@ void init_map_timer(void);
 void done_map_timer(void);
 void add_to_map_timer(struct ecmascript_timeout *t);
 void del_from_map_timer(struct ecmascript_timeout *t);
-bool found_in_map_timer(struct ecmascript_timeout *t);
+struct ecmascript_timeout *find_in_map_timer(uint32_t id);
 
 #ifdef __cplusplus
 }
diff --git a/src/main/timer.c b/src/main/timer.c
index 3b5f04611..fdaf41852 100644
--- a/src/main/timer.c
+++ b/src/main/timer.c
@@ -126,9 +126,6 @@ check_timers(timeval_T *last_time)
 			break;
 
 		del_from_list(timer);
-#if defined(CONFIG_ECMASCRIPT_SMJS) || defined(CONFIG_QUICKJS) || defined(CONFIG_MUJS)
-		del_from_map_timer(timer->data);
-#endif
 		/* At this point, *@timer is to be considered invalid
 		 * outside timers.c; if anything e.g. passes it to
 		 * @kill_timer, that's a bug.  However, @timer->func
@@ -244,7 +241,7 @@ kill_timer(timer_id_T *id)
 	timer = *id;
 	del_from_list(timer);
 #if defined(CONFIG_ECMASCRIPT_SMJS) || defined(CONFIG_QUICKJS) || defined(CONFIG_MUJS)
-	del_from_map_timer(timer->data);
+	//del_from_map_timer(timer->data);
 #endif
 
 #ifdef USE_LIBEVENT
