Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
86e653f592 | ||
|
|
8cffc76604 | ||
|
|
4908cc5c62 | ||
|
|
ab8b09f183 |
4
Makefile
4
Makefile
@@ -147,7 +147,7 @@ CFLAGS+=-fwrapv # ensure that signed overflows behave as expected
|
||||
ifdef CONFIG_WERROR
|
||||
CFLAGS+=-Werror
|
||||
endif
|
||||
DEFINES:=-D_GNU_SOURCE -DCONFIG_VERSION=\"$(shell cat VERSION)\"
|
||||
DEFINES:=-D_GNU_SOURCE -DCONFIG_VERSION=\"$(shell cat VERSION.md)\"
|
||||
ifdef CONFIG_WIN32
|
||||
DEFINES+=-D__USE_MINGW_ANSI_STDIO # for standard snprintf behavior
|
||||
endif
|
||||
@@ -423,7 +423,7 @@ build_doc: $(DOCS)
|
||||
clean_doc:
|
||||
rm -f $(DOCS)
|
||||
|
||||
doc/version.texi: VERSION
|
||||
doc/version.texi: VERSION.md
|
||||
@echo "@set VERSION `cat $<`" > $@
|
||||
|
||||
doc/%.pdf: doc/%.texi doc/version.texi
|
||||
|
||||
37
meson.build
37
meson.build
@@ -1,4 +1,4 @@
|
||||
project('quickjs', 'c')
|
||||
project('quickjs', 'c', meson_version: '>=1.1')
|
||||
|
||||
cc = meson.get_compiler('c')
|
||||
|
||||
@@ -8,13 +8,14 @@ deps = []
|
||||
deps += cc.find_library('m', required:false)
|
||||
deps += threads
|
||||
|
||||
add_project_arguments('-DCONFIG_VERSION="2024-02-14"', '-mcmodel=large', language: 'c')
|
||||
fs = import('fs')
|
||||
|
||||
if get_option('bignum')
|
||||
add_project_arguments('-DCONFIG_BIGNUM', language : 'c')
|
||||
endif
|
||||
ver_file = join_paths(meson.current_source_dir(), 'VERSION.md')
|
||||
qjs_ver = fs.read(ver_file).strip()
|
||||
|
||||
lib_sources = ['libbf.c', 'libregexp.c', 'quickjs.c', 'libunicode.c', 'cutils.c']
|
||||
add_project_arguments('-DCONFIG_VERSION="@0@"'.format(qjs_ver), language: 'c')
|
||||
|
||||
lib_sources = ['libregexp.c', 'quickjs.c', 'libunicode.c', 'cutils.c', 'dtoa.c']
|
||||
|
||||
libquickjs = library('quickjs',
|
||||
lib_sources,
|
||||
@@ -29,22 +30,15 @@ qjsc = executable('qjsc',
|
||||
build_by_default:false
|
||||
)
|
||||
|
||||
qjscalc_c = custom_target(
|
||||
'qjscalc_c',
|
||||
output: 'qjscalc.c',
|
||||
input: 'qjscalc.js',
|
||||
command: [qjsc, '-fbignum', '-c', '-o', '@OUTPUT@', '@INPUT@'],
|
||||
)
|
||||
|
||||
qjsrepl_c = custom_target(
|
||||
'qjsrepl_c',
|
||||
output: 'repl.c',
|
||||
input: 'repl.js',
|
||||
command: [qjsc, '-fbignum', '-c', '-o', '@OUTPUT@', '-m', '@INPUT@'],
|
||||
command: [qjsc, '-c', '-o', '@OUTPUT@', '-m', '@INPUT@'],
|
||||
)
|
||||
|
||||
qjs = executable('qjs',
|
||||
'qjs.c', 'quickjs-libc.c', qjscalc_c, qjsrepl_c,
|
||||
'qjs.c', 'quickjs-libc.c', qjsrepl_c,
|
||||
dependencies: [quickjs_dep, threads],
|
||||
build_by_default:false
|
||||
)
|
||||
@@ -55,22 +49,11 @@ base_tests = {
|
||||
'closure': ['test_closure.js'],
|
||||
'language': ['test_language.js'],
|
||||
'loop': ['test_loop.js'],
|
||||
'op_overloading': ['--bignum', 'test_op_overloading.js'],
|
||||
'bignum': ['--bignum', 'test_bignum.js'],
|
||||
'qjscalc': ['--qjscalc', 'test_qjscalc.js']
|
||||
'bigint': ['test_bigint.js'],
|
||||
}
|
||||
|
||||
test_suite = base_tests
|
||||
|
||||
is_windows = host_machine.system() == 'windows'
|
||||
if not is_windows
|
||||
test_suite += {
|
||||
'builtin': ['test_builtin.js'],
|
||||
'std': ['test_std.js'],
|
||||
'worker': ['test_worker.js']
|
||||
}
|
||||
endif
|
||||
|
||||
foreach test_name, test_args : test_suite
|
||||
test(test_name, qjs, args: test_args, workdir: test_root)
|
||||
endforeach
|
||||
|
||||
275
quickjs.c
275
quickjs.c
@@ -13243,8 +13243,6 @@ static __maybe_unused void JS_DumpObject(FILE *fp, JSRuntime *rt, JSObject *p)
|
||||
|
||||
static __maybe_unused void JS_DumpGCObject(FILE *fp, JSRuntime *rt, JSGCObjectHeader *p)
|
||||
{
|
||||
struct gc_object obj;
|
||||
|
||||
if (p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {
|
||||
JS_DumpObject(fp, rt, (JSObject *)p);
|
||||
} else {
|
||||
@@ -13423,7 +13421,8 @@ int JS_ArrayLength(JSContext *ctx, JSValueConst val)
|
||||
JSObject *p = JS_VALUE_GET_OBJ(val);
|
||||
if (p->class_id != JS_CLASS_ARRAY) return 0;
|
||||
uint32_t len;
|
||||
js_get_length32(ctx, &len, val);
|
||||
if (js_get_length32(ctx, &len, val))
|
||||
return 0;
|
||||
return len;
|
||||
}
|
||||
|
||||
@@ -54977,7 +54976,7 @@ void js_debug_info(JSContext *js, JSValue fn, js_debug *dbg)
|
||||
if (!JS_IsObject(fn)) return;
|
||||
JSObject *p = JS_VALUE_GET_OBJ(fn);
|
||||
|
||||
char *fn_name = get_func_name(js,fn);
|
||||
const char *fn_name = get_func_name(js,fn);
|
||||
|
||||
if (!fn_name || fn_name[0] == 0)
|
||||
dbg->name = js_strdup(js, "<anonymous>");
|
||||
@@ -54992,7 +54991,7 @@ void js_debug_info(JSContext *js, JSValue fn, js_debug *dbg)
|
||||
case JS_CLASS_BYTECODE_FUNCTION: {
|
||||
JSFunctionBytecode *b = p->u.func.function_bytecode;
|
||||
// get filename
|
||||
char *filename = JS_AtomToCString(js, b->debug.filename);
|
||||
const char *filename = JS_AtomToCString(js, b->debug.filename);
|
||||
if (!filename || filename[0] == 0)
|
||||
dbg->filename = js_strdup(js, "unknown");
|
||||
else {
|
||||
@@ -55164,3 +55163,269 @@ JSValue js_debugger_closure_variables(JSContext *ctx, JSValue fn) {
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static JSValue shape_to_obj(JSContext *ctx, JSShape *sh) {
|
||||
if (!sh)
|
||||
return JS_UNDEFINED;
|
||||
|
||||
JSValue o = JS_NewObject(ctx);
|
||||
|
||||
// For debugging, we’ll store the pointer addresses as strings
|
||||
{
|
||||
char buf[64];
|
||||
snprintf(buf, sizeof(buf), "%p", (void *)sh);
|
||||
JS_SetPropertyStr(ctx, o, "self", JS_NewString(ctx, buf));
|
||||
}
|
||||
|
||||
// Also store proto pointer
|
||||
{
|
||||
char buf[64];
|
||||
snprintf(buf, sizeof(buf), "%p", (void *)sh->proto);
|
||||
JS_SetPropertyStr(ctx, o, "proto", JS_NewString(ctx, buf));
|
||||
}
|
||||
|
||||
JS_SetPropertyStr(ctx, o, "ref_count", JS_NewInt32(ctx, sh->header.ref_count));
|
||||
JS_SetPropertyStr(ctx, o, "is_hashed", JS_NewBool(ctx, sh->is_hashed));
|
||||
JS_SetPropertyStr(ctx, o, "prop_size", JS_NewInt32(ctx, sh->prop_size));
|
||||
JS_SetPropertyStr(ctx, o, "prop_count", JS_NewInt32(ctx, sh->prop_count));
|
||||
|
||||
// Collect property atoms into an array
|
||||
JSValue arr = JS_NewArray(ctx);
|
||||
int propIndex = 0;
|
||||
for (int j = 0; j < sh->prop_count; j++) {
|
||||
JSAtom atom = sh->prop[j].atom;
|
||||
if (atom != JS_ATOM_NULL) {
|
||||
// Convert to a JSValue (usually a string, or symbol, etc.)
|
||||
JSValue val = JS_AtomToValue(ctx, atom);
|
||||
// Append to array
|
||||
JS_SetPropertyUint32(ctx, arr, propIndex++, val);
|
||||
}
|
||||
}
|
||||
JS_SetPropertyStr(ctx, o, "props", arr);
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
JSValue js_dump_shapes(JSContext *ctx) {
|
||||
JSRuntime *rt = ctx->rt;
|
||||
|
||||
JSValue ret = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, ret, "shape_hash_size", JS_NewInt32(ctx, rt->shape_hash_size));
|
||||
|
||||
JSValue arr = JS_NewArray(ctx);
|
||||
uint32_t idx = 0;
|
||||
|
||||
for (int i = 0; i < rt->shape_hash_size; i++) {
|
||||
JSShape *sh = rt->shape_hash[i];
|
||||
while (sh) {
|
||||
JSValue shapeObj = shape_to_obj(ctx, sh);
|
||||
JS_SetPropertyUint32(ctx, arr, idx++, shapeObj);
|
||||
sh = sh->shape_hash_next;
|
||||
}
|
||||
}
|
||||
|
||||
struct list_head *el;
|
||||
list_for_each(el, &rt->gc_obj_list) {
|
||||
JSGCObjectHeader *gp = list_entry(el, JSGCObjectHeader, link);
|
||||
if (gp->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {
|
||||
JSObject *p = (JSObject *)gp;
|
||||
if (!p->shape->is_hashed) {
|
||||
JSValue shapeObj = shape_to_obj(ctx, p->shape);
|
||||
JS_SetPropertyUint32(ctx, arr, idx++, shapeObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JS_SetPropertyStr(ctx, ret, "shapes", arr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void js_debug_sethook(JSContext *ctx, js_hook hook, int type)
|
||||
{
|
||||
if (type == JS_HOOK_CALL)
|
||||
ctx->fn_start_hook = hook;
|
||||
else if (type == JS_HOOK_RET)
|
||||
ctx->fn_end_hook = hook;
|
||||
else if (type == JS_HOOK_CYCLE)
|
||||
ctx->fn_cycle_hook = hook;
|
||||
else if (type == JS_HOOK_GC)
|
||||
ctx->fn_gc_hook = hook;
|
||||
}
|
||||
|
||||
JSValue js_dump_atoms(JSContext *js)
|
||||
{
|
||||
JSRuntime *rt = js->rt;
|
||||
JSValue ret = JS_NewObject(js);
|
||||
JS_SetPropertyStr(js,ret,"count", JS_NewFloat64(js,rt->atom_count));
|
||||
JS_SetPropertyStr(js,ret,"atom_size", JS_NewFloat64(js,rt->atom_size));
|
||||
JS_SetPropertyStr(js,ret,"hash_size", JS_NewFloat64(js,rt->atom_hash_size));
|
||||
JSValue atoms = JS_NewArray(js);
|
||||
int ac = 0;
|
||||
|
||||
for (int i = 0; i < rt->atom_hash_size; i++) {
|
||||
int h = rt->atom_hash[i];
|
||||
if (h) {
|
||||
while(h) {
|
||||
JSAtomStruct *p = rt->atom_array[h];
|
||||
JSValue atobj = JS_NewObject(js);
|
||||
JS_SetPropertyStr(js,atobj,"index", JS_NewInt32(js,h));
|
||||
JS_SetPropertyStr(js,atobj,"hash", JS_NewUint32(js,p->hash));
|
||||
JS_SetPropertyStr(js, atobj, "atom_type", JS_NewInt32(js, p->atom_type));
|
||||
JS_SetPropertyStr(js,atobj,"value", JS_AtomToValue(js,h));
|
||||
h = p->hash_next;
|
||||
JS_SetPropertyUint32(js,atoms,ac++,atobj);
|
||||
}
|
||||
}
|
||||
}
|
||||
JS_SetPropertyStr(js,ret,"atoms", atoms);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Returns a JS object containing selected JSMemoryUsage stats */
|
||||
JSValue js_get_memory_usage(JSContext *ctx) {
|
||||
JSMemoryUsage s;
|
||||
JS_ComputeMemoryUsage(ctx->rt, &s);
|
||||
|
||||
JSValue obj = JS_NewObject(ctx);
|
||||
|
||||
/* For each field, store it as a property in the object */
|
||||
#define SETM(name) \
|
||||
JS_SetPropertyStr(ctx, obj, #name, JS_NewInt64(ctx, s.name));
|
||||
|
||||
SETM(malloc_size)
|
||||
SETM(malloc_limit)
|
||||
SETM(memory_used_size)
|
||||
SETM(memory_used_count)
|
||||
SETM(atom_count)
|
||||
SETM(atom_size)
|
||||
SETM(str_count)
|
||||
SETM(str_size)
|
||||
SETM(obj_count)
|
||||
SETM(prop_count)
|
||||
SETM(prop_size)
|
||||
SETM(shape_count)
|
||||
SETM(shape_size)
|
||||
SETM(js_func_count)
|
||||
SETM(js_func_size)
|
||||
SETM(js_func_code_size)
|
||||
SETM(js_func_pc2line_count)
|
||||
SETM(js_func_pc2line_size)
|
||||
SETM(c_func_count)
|
||||
SETM(array_count)
|
||||
SETM(fast_array_count)
|
||||
SETM(fast_array_elements)
|
||||
SETM(binary_object_count)
|
||||
SETM(binary_object_size)
|
||||
|
||||
#undef SETM
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
JSValue js_dump_objects(JSContext *ctx) {
|
||||
JSValue arr = JS_NewArray(ctx);
|
||||
return arr;
|
||||
/* uint32_t idx = 0;
|
||||
|
||||
JSRuntime *rt = ctx->rt;
|
||||
struct list_head *el;
|
||||
list_for_each(el, &rt->gc_obj_list) {
|
||||
JSGCObjectHeader *hdr = list_entry(el, JSGCObjectHeader, link);
|
||||
// Dump any GC object, not just JSObject
|
||||
JSValue desc = js_dump_object(ctx, hdr);
|
||||
JS_SetPropertyUint32(ctx, arr, idx++, desc);
|
||||
}
|
||||
return arr;*/
|
||||
}
|
||||
|
||||
JSValue js_get_object_type_overheads(JSContext *ctx) {
|
||||
JSRuntime *rt = ctx->rt;
|
||||
static const struct {
|
||||
const char *name;
|
||||
size_t size;
|
||||
} object_types[] = {
|
||||
{ "JSRuntime", sizeof(JSRuntime) },
|
||||
{ "JSContext", sizeof(JSContext) },
|
||||
{ "JSObject", sizeof(JSObject) },
|
||||
{ "JSString", sizeof(JSString) },
|
||||
{ "JSFunctionBytecode", sizeof(JSFunctionBytecode) },
|
||||
};
|
||||
int usage_size_ok = 0;
|
||||
|
||||
JSValue arr = JS_NewArray(ctx);
|
||||
uint32_t idx = 0;
|
||||
|
||||
for (int i = 0; i < (int)(sizeof(object_types)/sizeof(object_types[0])); i++) {
|
||||
unsigned int size = (unsigned int)object_types[i].size;
|
||||
void *p = js_malloc_rt(rt, size);
|
||||
if (!p) {
|
||||
// out of memory? skip
|
||||
continue;
|
||||
}
|
||||
unsigned int size1 = js_malloc_usable_size_rt(rt, p);
|
||||
js_free_rt(rt, p);
|
||||
|
||||
if (size1 >= size) {
|
||||
usage_size_ok = 1;
|
||||
|
||||
JSValue entry = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, entry, "name", JS_NewString(ctx, object_types[i].name));
|
||||
JS_SetPropertyStr(ctx, entry, "base_size", JS_NewUint32(ctx, size));
|
||||
JS_SetPropertyStr(ctx, entry, "usable_size", JS_NewUint32(ctx, size1));
|
||||
JS_SetPropertyUint32(ctx, arr, idx++, entry);
|
||||
}
|
||||
}
|
||||
|
||||
if (!usage_size_ok) {
|
||||
// Possibly return a single object or just a boolean
|
||||
JSValue notice = JS_NewString(ctx, "malloc_usable_size unavailable");
|
||||
JS_SetPropertyUint32(ctx, arr, idx++, notice);
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
JSValue js_get_object_class_distribution(JSContext *ctx) {
|
||||
JSRuntime *rt = ctx->rt;
|
||||
JSValue ret = JS_NewArray(ctx);
|
||||
// For counting how many objects exist for each class
|
||||
int obj_classes[JS_CLASS_INIT_COUNT + 1] = {0};
|
||||
|
||||
// 1) Gather stats
|
||||
struct list_head *el;
|
||||
list_for_each(el, &rt->gc_obj_list) {
|
||||
JSGCObjectHeader *gp = list_entry(el, JSGCObjectHeader, link);
|
||||
if (gp->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {
|
||||
JSObject *p = (JSObject *)gp;
|
||||
// Bound the index
|
||||
uint32_t idx = p->class_id < JS_CLASS_INIT_COUNT ? p->class_id : JS_CLASS_INIT_COUNT;
|
||||
obj_classes[idx]++;
|
||||
}
|
||||
}
|
||||
|
||||
// 2) Convert to array of objects
|
||||
for (int class_id = 0; class_id <= JS_CLASS_INIT_COUNT; class_id++) {
|
||||
int count = obj_classes[class_id];
|
||||
if (!count) continue;
|
||||
|
||||
// Create something like: { class_id: 1, count: 20, name: "Object" }
|
||||
JSValue entry = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, entry, "class_id", JS_NewInt32(ctx, class_id));
|
||||
JS_SetPropertyStr(ctx, entry, "count", JS_NewInt32(ctx, count));
|
||||
|
||||
if (class_id < rt->class_count) {
|
||||
// We can get the class name atom
|
||||
JSAtom cname_atom = rt->class_array[class_id].class_name;
|
||||
// Convert to string
|
||||
JSValue cname_val = JS_AtomToValue(ctx, cname_atom);
|
||||
JS_SetPropertyStr(ctx, entry, "name", cname_val);
|
||||
} else {
|
||||
// "other" or "unknown"
|
||||
JS_SetPropertyStr(ctx, entry, "name", JS_NewString(ctx, "other"));
|
||||
}
|
||||
|
||||
JS_SetPropertyUint32(ctx, ret, class_id, entry);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user