mirror of
https://github.com/DoneJS-Runtime/quickjs-done-nextgen.git
synced 2025-01-09 17:43:15 +00:00
Refactor JSMallocFunctions to simplify the implementation
Rather than having the user take care of JSMallocState, take care of the bookkeeping internally (and make JSMallocState non-public since it's no longer necessary) and keep the allocation functions to the bare minimum. This has the advantage that using a different allocator is just a few lines of code, and there is no need to copy the default implementation just to moficy the call to the allocation function. Fixes: https://github.com/quickjs-ng/quickjs/issues/285
This commit is contained in:
parent
5f5170796e
commit
cfeeff91db
3 changed files with 109 additions and 154 deletions
81
qjs.c
81
qjs.c
|
@ -165,12 +165,6 @@ static JSContext *JS_NewCustomContext(JSRuntime *rt)
|
||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(__APPLE__)
|
|
||||||
#define MALLOC_OVERHEAD 0
|
|
||||||
#else
|
|
||||||
#define MALLOC_OVERHEAD 8
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct trace_malloc_data {
|
struct trace_malloc_data {
|
||||||
uint8_t *base;
|
uint8_t *base;
|
||||||
};
|
};
|
||||||
|
@ -188,7 +182,7 @@ __attribute__((format(gnu_printf, 2, 3)))
|
||||||
#else
|
#else
|
||||||
__attribute__((format(printf, 2, 3)))
|
__attribute__((format(printf, 2, 3)))
|
||||||
#endif
|
#endif
|
||||||
js_trace_malloc_printf(JSMallocState *s, const char *fmt, ...)
|
js_trace_malloc_printf(void *opaque, const char *fmt, ...)
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
int c;
|
int c;
|
||||||
|
@ -203,7 +197,7 @@ __attribute__((format(printf, 2, 3)))
|
||||||
printf("NULL");
|
printf("NULL");
|
||||||
} else {
|
} else {
|
||||||
printf("H%+06lld.%zd",
|
printf("H%+06lld.%zd",
|
||||||
js_trace_malloc_ptr_offset(ptr, s->opaque),
|
js_trace_malloc_ptr_offset(ptr, opaque),
|
||||||
js__malloc_usable_size(ptr));
|
js__malloc_usable_size(ptr));
|
||||||
}
|
}
|
||||||
fmt++;
|
fmt++;
|
||||||
|
@ -226,87 +220,36 @@ static void js_trace_malloc_init(struct trace_malloc_data *s)
|
||||||
free(s->base = malloc(8));
|
free(s->base = malloc(8));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *js_trace_calloc(JSMallocState *s, size_t count, size_t size)
|
static void *js_trace_calloc(void *opaque, size_t count, size_t size)
|
||||||
{
|
{
|
||||||
void *ptr;
|
void *ptr;
|
||||||
|
|
||||||
/* Do not allocate zero bytes: behavior is platform dependent */
|
|
||||||
assert(count != 0 && size != 0);
|
|
||||||
|
|
||||||
if (size > 0)
|
|
||||||
if (unlikely(count != (count * size) / size))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
/* When malloc_limit is 0 (unlimited), malloc_limit - 1 will be SIZE_MAX. */
|
|
||||||
if (unlikely(s->malloc_size + (count * size) > s->malloc_limit - 1))
|
|
||||||
return NULL;
|
|
||||||
ptr = calloc(count, size);
|
ptr = calloc(count, size);
|
||||||
js_trace_malloc_printf(s, "C %zd %zd -> %p\n", count, size, ptr);
|
js_trace_malloc_printf(opaque, "C %zd %zd -> %p\n", count, size, ptr);
|
||||||
if (ptr) {
|
|
||||||
s->malloc_count++;
|
|
||||||
s->malloc_size += js__malloc_usable_size(ptr) + MALLOC_OVERHEAD;
|
|
||||||
}
|
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *js_trace_malloc(JSMallocState *s, size_t size)
|
static void *js_trace_malloc(void *opaque, size_t size)
|
||||||
{
|
{
|
||||||
|
(void) opaque;
|
||||||
void *ptr;
|
void *ptr;
|
||||||
|
|
||||||
/* Do not allocate zero bytes: behavior is platform dependent */
|
|
||||||
assert(size != 0);
|
|
||||||
|
|
||||||
/* When malloc_limit is 0 (unlimited), malloc_limit - 1 will be SIZE_MAX. */
|
|
||||||
if (unlikely(s->malloc_size + size > s->malloc_limit - 1))
|
|
||||||
return NULL;
|
|
||||||
ptr = malloc(size);
|
ptr = malloc(size);
|
||||||
js_trace_malloc_printf(s, "A %zd -> %p\n", size, ptr);
|
js_trace_malloc_printf(opaque, "A %zd -> %p\n", size, ptr);
|
||||||
if (ptr) {
|
|
||||||
s->malloc_count++;
|
|
||||||
s->malloc_size += js__malloc_usable_size(ptr) + MALLOC_OVERHEAD;
|
|
||||||
}
|
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void js_trace_free(JSMallocState *s, void *ptr)
|
static void js_trace_free(void *opaque, void *ptr)
|
||||||
{
|
{
|
||||||
if (!ptr)
|
if (!ptr)
|
||||||
return;
|
return;
|
||||||
|
js_trace_malloc_printf(opaque, "F %p\n", ptr);
|
||||||
js_trace_malloc_printf(s, "F %p\n", ptr);
|
|
||||||
s->malloc_count--;
|
|
||||||
s->malloc_size -= js__malloc_usable_size(ptr) + MALLOC_OVERHEAD;
|
|
||||||
free(ptr);
|
free(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *js_trace_realloc(JSMallocState *s, void *ptr, size_t size)
|
static void *js_trace_realloc(void *opaque, void *ptr, size_t size)
|
||||||
{
|
{
|
||||||
size_t old_size;
|
js_trace_malloc_printf(opaque, "R %zd %p", size, ptr);
|
||||||
|
|
||||||
if (!ptr) {
|
|
||||||
if (size == 0)
|
|
||||||
return NULL;
|
|
||||||
return js_trace_malloc(s, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (unlikely(size == 0)) {
|
|
||||||
js_trace_free(s, ptr);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
old_size = js__malloc_usable_size(ptr);
|
|
||||||
|
|
||||||
/* When malloc_limit is 0 (unlimited), malloc_limit - 1 will be SIZE_MAX. */
|
|
||||||
if (s->malloc_size + size - old_size > s->malloc_limit - 1)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
js_trace_malloc_printf(s, "R %zd %p", size, ptr);
|
|
||||||
|
|
||||||
ptr = realloc(ptr, size);
|
ptr = realloc(ptr, size);
|
||||||
js_trace_malloc_printf(s, " -> %p\n", ptr);
|
js_trace_malloc_printf(opaque, " -> %p\n", ptr);
|
||||||
if (ptr) {
|
|
||||||
s->malloc_size += js__malloc_usable_size(ptr) - old_size;
|
|
||||||
}
|
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
167
quickjs.c
167
quickjs.c
|
@ -213,6 +213,13 @@ typedef enum {
|
||||||
JS_GC_PHASE_REMOVE_CYCLES,
|
JS_GC_PHASE_REMOVE_CYCLES,
|
||||||
} JSGCPhaseEnum;
|
} JSGCPhaseEnum;
|
||||||
|
|
||||||
|
typedef struct JSMallocState {
|
||||||
|
size_t malloc_count;
|
||||||
|
size_t malloc_size;
|
||||||
|
size_t malloc_limit;
|
||||||
|
void *opaque; /* user opaque */
|
||||||
|
} JSMallocState;
|
||||||
|
|
||||||
struct JSRuntime {
|
struct JSRuntime {
|
||||||
JSMallocFunctions mf;
|
JSMallocFunctions mf;
|
||||||
JSMallocState malloc_state;
|
JSMallocState malloc_state;
|
||||||
|
@ -1380,22 +1387,91 @@ static size_t js_malloc_usable_size_unknown(const void *ptr)
|
||||||
|
|
||||||
void *js_calloc_rt(JSRuntime *rt, size_t count, size_t size)
|
void *js_calloc_rt(JSRuntime *rt, size_t count, size_t size)
|
||||||
{
|
{
|
||||||
return rt->mf.js_calloc(&rt->malloc_state, count, size);
|
void *ptr;
|
||||||
|
JSMallocState *s;
|
||||||
|
|
||||||
|
/* Do not allocate zero bytes: behavior is platform dependent */
|
||||||
|
assert(count != 0 && size != 0);
|
||||||
|
|
||||||
|
if (size > 0)
|
||||||
|
if (unlikely(count != (count * size) / size))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
s = &rt->malloc_state;
|
||||||
|
/* When malloc_limit is 0 (unlimited), malloc_limit - 1 will be SIZE_MAX. */
|
||||||
|
if (unlikely(s->malloc_size + (count * size) > s->malloc_limit - 1))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
ptr = rt->mf.js_calloc(s->opaque, count, size);
|
||||||
|
if (!ptr)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
s->malloc_count++;
|
||||||
|
s->malloc_size += js__malloc_usable_size(ptr) + MALLOC_OVERHEAD;
|
||||||
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *js_malloc_rt(JSRuntime *rt, size_t size)
|
void *js_malloc_rt(JSRuntime *rt, size_t size)
|
||||||
{
|
{
|
||||||
return rt->mf.js_malloc(&rt->malloc_state, size);
|
void *ptr;
|
||||||
|
JSMallocState *s;
|
||||||
|
|
||||||
|
/* Do not allocate zero bytes: behavior is platform dependent */
|
||||||
|
assert(size != 0);
|
||||||
|
|
||||||
|
s = &rt->malloc_state;
|
||||||
|
/* When malloc_limit is 0 (unlimited), malloc_limit - 1 will be SIZE_MAX. */
|
||||||
|
if (unlikely(s->malloc_size + size > s->malloc_limit - 1))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
ptr = rt->mf.js_malloc(s->opaque, size);
|
||||||
|
if (!ptr)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
s->malloc_count++;
|
||||||
|
s->malloc_size += js__malloc_usable_size(ptr) + MALLOC_OVERHEAD;
|
||||||
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void js_free_rt(JSRuntime *rt, void *ptr)
|
void js_free_rt(JSRuntime *rt, void *ptr)
|
||||||
{
|
{
|
||||||
rt->mf.js_free(&rt->malloc_state, ptr);
|
JSMallocState *s;
|
||||||
|
|
||||||
|
if (!ptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
s = &rt->malloc_state;
|
||||||
|
s->malloc_count--;
|
||||||
|
s->malloc_size -= js__malloc_usable_size(ptr) + MALLOC_OVERHEAD;
|
||||||
|
rt->mf.js_free(s->opaque, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void *js_realloc_rt(JSRuntime *rt, void *ptr, size_t size)
|
void *js_realloc_rt(JSRuntime *rt, void *ptr, size_t size)
|
||||||
{
|
{
|
||||||
return rt->mf.js_realloc(&rt->malloc_state, ptr, size);
|
size_t old_size;
|
||||||
|
JSMallocState *s;
|
||||||
|
|
||||||
|
if (!ptr) {
|
||||||
|
if (size == 0)
|
||||||
|
return NULL;
|
||||||
|
return js_malloc_rt(rt, size);
|
||||||
|
}
|
||||||
|
if (unlikely(size == 0)) {
|
||||||
|
js_free_rt(rt, ptr);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
old_size = js__malloc_usable_size(ptr);
|
||||||
|
s = &rt->malloc_state;
|
||||||
|
/* When malloc_limit is 0 (unlimited), malloc_limit - 1 will be SIZE_MAX. */
|
||||||
|
if (s->malloc_size + size - old_size > s->malloc_limit - 1)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
ptr = rt->mf.js_realloc(s->opaque, ptr, size);
|
||||||
|
if (!ptr)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
s->malloc_size += js__malloc_usable_size(ptr) - old_size;
|
||||||
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *js_dbuf_realloc(void *opaque, void *ptr, size_t size)
|
static void *js_dbuf_realloc(void *opaque, void *ptr, size_t size)
|
||||||
|
@ -1651,9 +1727,12 @@ JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque)
|
||||||
ms.opaque = opaque;
|
ms.opaque = opaque;
|
||||||
ms.malloc_limit = 0;
|
ms.malloc_limit = 0;
|
||||||
|
|
||||||
rt = mf->js_calloc(&ms, 1, sizeof(JSRuntime));
|
rt = mf->js_calloc(opaque, 1, sizeof(JSRuntime));
|
||||||
if (!rt)
|
if (!rt)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
/* Inline what js_malloc_rt does since we cannot use it here. */
|
||||||
|
ms.malloc_count++;
|
||||||
|
ms.malloc_size += js__malloc_usable_size(rt) + MALLOC_OVERHEAD;
|
||||||
rt->mf = *mf;
|
rt->mf = *mf;
|
||||||
if (!rt->mf.js_malloc_usable_size) {
|
if (!rt->mf.js_malloc_usable_size) {
|
||||||
/* use dummy function if none provided */
|
/* use dummy function if none provided */
|
||||||
|
@ -1718,84 +1797,24 @@ void JS_SetRuntimeOpaque(JSRuntime *rt, void *opaque)
|
||||||
rt->user_opaque = opaque;
|
rt->user_opaque = opaque;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *js_def_calloc(JSMallocState *s, size_t count, size_t size)
|
static void *js_def_calloc(void *opaque, size_t count, size_t size)
|
||||||
{
|
{
|
||||||
void *ptr;
|
return calloc(count, size);
|
||||||
|
|
||||||
/* Do not allocate zero bytes: behavior is platform dependent */
|
|
||||||
assert(count != 0 && size != 0);
|
|
||||||
|
|
||||||
if (size > 0)
|
|
||||||
if (unlikely(count != (count * size) / size))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
/* When malloc_limit is 0 (unlimited), malloc_limit - 1 will be SIZE_MAX. */
|
|
||||||
if (unlikely(s->malloc_size + (count * size) > s->malloc_limit - 1))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
ptr = calloc(count, size);
|
|
||||||
if (!ptr)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
s->malloc_count++;
|
|
||||||
s->malloc_size += js__malloc_usable_size(ptr) + MALLOC_OVERHEAD;
|
|
||||||
return ptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *js_def_malloc(JSMallocState *s, size_t size)
|
static void *js_def_malloc(void *opaque, size_t size)
|
||||||
{
|
{
|
||||||
void *ptr;
|
return malloc(size);
|
||||||
|
|
||||||
/* Do not allocate zero bytes: behavior is platform dependent */
|
|
||||||
assert(size != 0);
|
|
||||||
|
|
||||||
/* When malloc_limit is 0 (unlimited), malloc_limit - 1 will be SIZE_MAX. */
|
|
||||||
if (unlikely(s->malloc_size + size > s->malloc_limit - 1))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
ptr = malloc(size);
|
|
||||||
if (!ptr)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
s->malloc_count++;
|
|
||||||
s->malloc_size += js__malloc_usable_size(ptr) + MALLOC_OVERHEAD;
|
|
||||||
return ptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void js_def_free(JSMallocState *s, void *ptr)
|
static void js_def_free(void *opaque, void *ptr)
|
||||||
{
|
{
|
||||||
if (!ptr)
|
|
||||||
return;
|
|
||||||
|
|
||||||
s->malloc_count--;
|
|
||||||
s->malloc_size -= js__malloc_usable_size(ptr) + MALLOC_OVERHEAD;
|
|
||||||
free(ptr);
|
free(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *js_def_realloc(JSMallocState *s, void *ptr, size_t size)
|
static void *js_def_realloc(void *opaque, void *ptr, size_t size)
|
||||||
{
|
{
|
||||||
size_t old_size;
|
return realloc(ptr, size);
|
||||||
|
|
||||||
if (!ptr) {
|
|
||||||
if (size == 0)
|
|
||||||
return NULL;
|
|
||||||
return js_def_malloc(s, size);
|
|
||||||
}
|
|
||||||
if (unlikely(size == 0)) {
|
|
||||||
js_def_free(s, ptr);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
old_size = js__malloc_usable_size(ptr);
|
|
||||||
/* When malloc_limit is 0 (unlimited), malloc_limit - 1 will be SIZE_MAX. */
|
|
||||||
if (s->malloc_size + size - old_size > s->malloc_limit - 1)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
ptr = realloc(ptr, size);
|
|
||||||
if (!ptr)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
s->malloc_size += js__malloc_usable_size(ptr) - old_size;
|
|
||||||
return ptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const JSMallocFunctions def_malloc_funcs = {
|
static const JSMallocFunctions def_malloc_funcs = {
|
||||||
|
@ -2159,8 +2178,8 @@ void JS_FreeRuntime(JSRuntime *rt)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
{
|
{
|
||||||
JSMallocState ms = rt->malloc_state;
|
JSMallocState *ms = &rt->malloc_state;
|
||||||
rt->mf.js_free(&ms, rt);
|
rt->mf.js_free(ms->opaque, rt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
15
quickjs.h
15
quickjs.h
|
@ -283,18 +283,11 @@ typedef JSValue JSCFunction(JSContext *ctx, JSValue this_val, int argc, JSValue
|
||||||
typedef JSValue JSCFunctionMagic(JSContext *ctx, JSValue this_val, int argc, JSValue *argv, int magic);
|
typedef JSValue JSCFunctionMagic(JSContext *ctx, JSValue this_val, int argc, JSValue *argv, int magic);
|
||||||
typedef JSValue JSCFunctionData(JSContext *ctx, JSValue this_val, int argc, JSValue *argv, int magic, JSValue *func_data);
|
typedef JSValue JSCFunctionData(JSContext *ctx, JSValue this_val, int argc, JSValue *argv, int magic, JSValue *func_data);
|
||||||
|
|
||||||
typedef struct JSMallocState {
|
|
||||||
size_t malloc_count;
|
|
||||||
size_t malloc_size;
|
|
||||||
size_t malloc_limit;
|
|
||||||
void *opaque; /* user opaque */
|
|
||||||
} JSMallocState;
|
|
||||||
|
|
||||||
typedef struct JSMallocFunctions {
|
typedef struct JSMallocFunctions {
|
||||||
void *(*js_calloc)(JSMallocState *s, size_t count, size_t size);
|
void *(*js_calloc)(void *opaque, size_t count, size_t size);
|
||||||
void *(*js_malloc)(JSMallocState *s, size_t size);
|
void *(*js_malloc)(void *opaque, size_t size);
|
||||||
void (*js_free)(JSMallocState *s, void *ptr);
|
void (*js_free)(void *opaque, void *ptr);
|
||||||
void *(*js_realloc)(JSMallocState *s, void *ptr, size_t size);
|
void *(*js_realloc)(void *opaque, void *ptr, size_t size);
|
||||||
size_t (*js_malloc_usable_size)(const void *ptr);
|
size_t (*js_malloc_usable_size)(const void *ptr);
|
||||||
} JSMallocFunctions;
|
} JSMallocFunctions;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue