added JS_PrintValue() and use it in console.log(), print() and the REPL (#256)

This commit is contained in:
Fabrice Bellard 2025-04-30 13:40:15 +02:00
parent 30fe3de91d
commit be06b3e92b
5 changed files with 752 additions and 440 deletions

View File

@ -1083,6 +1083,13 @@ static JSValue js_std_file_printf(JSContext *ctx, JSValueConst this_val,
return js_printf_internal(ctx, argc, argv, f);
}
static JSValue js_std_file_printObject(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
JS_PrintValue(ctx, stdout, argv[0], NULL);
return JS_UNDEFINED;
}
static JSValue js_std_file_flush(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
@ -1540,6 +1547,7 @@ static const JSCFunctionListEntry js_std_funcs[] = {
JS_PROP_INT32_DEF("SEEK_CUR", SEEK_CUR, JS_PROP_CONFIGURABLE ),
JS_PROP_INT32_DEF("SEEK_END", SEEK_END, JS_PROP_CONFIGURABLE ),
JS_OBJECT_DEF("Error", js_std_error_props, countof(js_std_error_props), JS_PROP_CONFIGURABLE),
JS_CFUNC_DEF("__printObject", 1, js_std_file_printObject ),
};
static const JSCFunctionListEntry js_std_file_proto_funcs[] = {
@ -3891,17 +3899,23 @@ static JSValue js_print(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
int i;
const char *str;
size_t len;
JSValueConst v;
for(i = 0; i < argc; i++) {
if (i != 0)
putchar(' ');
str = JS_ToCStringLen(ctx, &len, argv[i]);
if (!str)
return JS_EXCEPTION;
fwrite(str, 1, len, stdout);
JS_FreeCString(ctx, str);
v = argv[i];
if (JS_IsString(v)) {
const char *str;
size_t len;
str = JS_ToCStringLen(ctx, &len, v);
if (!str)
return JS_EXCEPTION;
fwrite(str, 1, len, stdout);
JS_FreeCString(ctx, str);
} else {
JS_PrintValue(ctx, stdout, v, NULL);
}
}
putchar('\n');
return JS_UNDEFINED;
@ -4012,33 +4026,10 @@ void js_std_free_handlers(JSRuntime *rt)
JS_SetRuntimeOpaque(rt, NULL); /* fail safe */
}
static void js_dump_obj(JSContext *ctx, FILE *f, JSValueConst val)
{
const char *str;
str = JS_ToCString(ctx, val);
if (str) {
fprintf(f, "%s\n", str);
JS_FreeCString(ctx, str);
} else {
fprintf(f, "[exception]\n");
}
}
static void js_std_dump_error1(JSContext *ctx, JSValueConst exception_val)
{
JSValue val;
BOOL is_error;
is_error = JS_IsError(ctx, exception_val);
js_dump_obj(ctx, stderr, exception_val);
if (is_error) {
val = JS_GetPropertyStr(ctx, exception_val, "stack");
if (!JS_IsUndefined(val)) {
js_dump_obj(ctx, stderr, val);
}
JS_FreeValue(ctx, val);
}
JS_PrintValue(ctx, stderr, exception_val, NULL);
fputc('\n', stderr);
}
void js_std_dump_error(JSContext *ctx)

940
quickjs.c

File diff suppressed because it is too large Load Diff

View File

@ -1113,6 +1113,23 @@ int JS_SetModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name,
int JS_SetModuleExportList(JSContext *ctx, JSModuleDef *m,
const JSCFunctionListEntry *tab, int len);
/* debug value output */
typedef struct {
JS_BOOL show_hidden : 8; /* only show enumerable properties */
JS_BOOL show_closure : 8; /* show closure variables */
JS_BOOL raw_dump : 8; /* avoid doing autoinit and avoid any malloc() call (for internal use) */
uint32_t max_depth; /* recurse up to this depth, 0 = no limit */
uint32_t max_string_length; /* print no more than this length for
strings, 0 = no limit */
uint32_t max_item_count; /* print no more than this count for
arrays or objects, 0 = no limit */
} JSPrintValueOptions;
void JS_PrintValueSetDefaultOptions(JSPrintValueOptions *options);
void JS_PrintValueRT(JSRuntime *rt, FILE *fo, JSValueConst val, const JSPrintValueOptions *options);
void JS_PrintValue(JSContext *ctx, FILE *fo, JSValueConst val, const JSPrintValueOptions *options);
#undef js_unlikely
#undef js_force_inline

149
repl.js
View File

@ -875,126 +875,19 @@ import * as os from "os";
}
var hex_mode = false;
var eval_mode = "std";
function number_to_string(a, radix) {
function number_to_string_hex(a) {
var s;
if (!isFinite(a)) {
/* NaN, Infinite */
return a.toString();
if (a < 0) {
a = -a;
s = "-";
} else {
if (a == 0) {
if (1 / a < 0)
s = "-0";
else
s = "0";
} else {
if (radix == 16 && a === Math.floor(a)) {
var s;
if (a < 0) {
a = -a;
s = "-";
} else {
s = "";
}
s += "0x" + a.toString(16);
} else {
s = a.toString();
}
}
return s;
s = "";
}
}
function bigint_to_string(a, radix) {
var s;
if (radix == 16) {
var s;
if (a < 0) {
a = -a;
s = "-";
} else {
s = "";
}
s += "0x" + a.toString(16);
} else {
s = a.toString();
}
if (eval_mode === "std")
s += "n";
s += "0x" + a.toString(16);
return s;
}
function print(a) {
var stack = [];
function print_rec(a) {
var n, i, keys, key, type, s;
type = typeof(a);
if (type === "object") {
if (a === null) {
std.puts(a);
} else if (stack.indexOf(a) >= 0) {
std.puts("[circular]");
} else if (a instanceof Date) {
std.puts("Date " + a.toGMTString().__quote());
} else {
stack.push(a);
if (Array.isArray(a)) {
n = a.length;
std.puts("[ ");
for(i = 0; i < n; i++) {
if (i !== 0)
std.puts(", ");
if (i in a) {
print_rec(a[i]);
} else {
std.puts("<empty>");
}
if (i > 20) {
std.puts("...");
break;
}
}
std.puts(" ]");
} else if (Object.__getClass(a) === "RegExp") {
std.puts(a.toString());
} else {
keys = Object.keys(a);
n = keys.length;
std.puts("{ ");
for(i = 0; i < n; i++) {
if (i !== 0)
std.puts(", ");
key = keys[i];
std.puts(key, ": ");
print_rec(a[key]);
}
std.puts(" }");
}
stack.pop(a);
}
} else if (type === "string") {
s = a.__quote();
if (s.length > 79)
s = s.substring(0, 75) + "...\"";
std.puts(s);
} else if (type === "number") {
std.puts(number_to_string(a, hex_mode ? 16 : 10));
} else if (type === "bigint") {
std.puts(bigint_to_string(a, hex_mode ? 16 : 10));
} else if (type === "symbol") {
std.puts(String(a));
} else if (type === "function") {
std.puts("function " + a.name + "()");
} else {
std.puts(a);
}
}
print_rec(a);
}
function extract_directive(a) {
var pos;
if (a[0] !== '\\')
@ -1116,10 +1009,25 @@ import * as os from "os";
}
function print_eval_result(result) {
var default_print = true;
result = result.value;
eval_time = os.now() - eval_start_time;
std.puts(colors[styles.result]);
print(result);
if (hex_mode) {
if (typeof result == "number" &&
result === Math.floor(result)) {
std.puts(number_to_string_hex(result));
default_print = false;
} else if (typeof result == "bigint") {
std.puts(number_to_string_hex(result));
std.puts("n");
default_print = false;
}
}
if (default_print) {
std.__printObject(result);
}
std.puts("\n");
std.puts(colors.none);
/* set the last result */
@ -1130,15 +1038,10 @@ import * as os from "os";
function print_eval_error(error) {
std.puts(colors[styles.error_msg]);
if (error instanceof Error) {
console.log(error);
if (error.stack) {
std.puts(error.stack);
}
} else {
if (!(error instanceof Error))
std.puts("Throw: ");
console.log(error);
}
std.__printObject(error);
std.puts("\n");
std.puts(colors.none);
handle_cmd_end();

View File

@ -376,22 +376,29 @@ static JSValue js_print(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
int i;
const char *str;
JSValueConst v;
if (outfile) {
for (i = 0; i < argc; i++) {
if (i != 0)
fputc(' ', outfile);
str = JS_ToCString(ctx, argv[i]);
if (!str)
return JS_EXCEPTION;
if (!strcmp(str, "Test262:AsyncTestComplete")) {
async_done++;
} else if (strstart(str, "Test262:AsyncTestFailure", NULL)) {
async_done = 2; /* force an error */
v = argv[i];
if (JS_IsString(v)) {
const char *str;
size_t len;
str = JS_ToCStringLen(ctx, &len, v);
if (!str)
return JS_EXCEPTION;
if (!strcmp(str, "Test262:AsyncTestComplete")) {
async_done++;
} else if (strstart(str, "Test262:AsyncTestFailure", NULL)) {
async_done = 2; /* force an error */
}
fwrite(str, 1, len, outfile);
JS_FreeCString(ctx, str);
} else {
JS_PrintValue(ctx, outfile, v, NULL);
}
fputs(str, outfile);
JS_FreeCString(ctx, str);
}
fputc('\n', outfile);
}