Add SuppressedError class and resource management methods

This commit is contained in:
ebrahimmostafa133 2026-04-30 18:52:33 +03:00
parent d7ae12ae71
commit cbc4663764
6 changed files with 321 additions and 0 deletions

1
BASE_COMMIT.txt Normal file

@ -0,0 +1 @@
d7ae12a added JSON.parse source text access

286
builtins/js-disposable.c Normal file

@ -0,0 +1,286 @@
#include "quickjs.h"
#include "quickjs-atom.h"
#include "js-disposable.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static JSClassID js_suppressed_error_class_id;
typedef struct {
JSValue error;
JSValue suppressed;
} JSSuppressedErrorData;
static void js_suppressed_error_finalizer(JSRuntime *rt, JSValue val)
{
JSSuppressedErrorData *data = JS_GetOpaque(val, js_suppressed_error_class_id);
if (data) {
JS_FreeValueRT(rt, data->error);
JS_FreeValueRT(rt, data->suppressed);
js_free_rt(rt, data);
}
}
static void js_suppressed_error_mark(JSRuntime *rt, JSValueConst val,
JS_MarkFunc *mark_func)
{
JSSuppressedErrorData *data = JS_GetOpaque(val, js_suppressed_error_class_id);
if (data) {
JS_MarkValue(rt, data->error, mark_func);
JS_MarkValue(rt, data->suppressed, mark_func);
}
}
static JSClassDef js_suppressed_error_class = {
"SuppressedError",
.finalizer = js_suppressed_error_finalizer,
.gc_mark = js_suppressed_error_mark,
};
static JSValue js_suppressed_error_constructor(JSContext *ctx,
JSValueConst new_target,
int argc, JSValueConst *argv)
{
JSValue obj;
JSSuppressedErrorData *data;
obj = JS_NewObjectClass(ctx, js_suppressed_error_class_id);
if (JS_IsException(obj))
return JS_EXCEPTION;
data = js_mallocz(ctx, sizeof(*data));
if (!data) {
JS_FreeObject(ctx, obj);
return JS_EXCEPTION;
}
data->error = JS_DupValue(ctx, argv[0]);
data->suppressed = JS_DupValue(ctx, argv[1]);
JS_SetOpaque(obj, data);
if (!JS_IsUndefined(new_target)) {
JSValue proto = JS_GetPropertyStr(ctx, new_target, "prototype");
if (JS_IsException(proto)) {
JS_FreeValue(ctx, obj);
return JS_EXCEPTION;
}
if (!JS_IsUndefined(proto)) {
JS_SetPrototype(ctx, obj, proto);
}
JS_FreeValue(ctx, proto);
}
return obj;
}
static JSValue js_suppressed_error_toString(JSContext *ctx,
JSValueConst this_val,
int argc, JSValueConst *argv)
{
JSSuppressedErrorData *data = JS_GetOpaque2(ctx, this_val, js_suppressed_error_class_id);
if (!data)
return JS_EXCEPTION;
JSValue error_str = JS_ToString(ctx, data->error);
if (JS_IsException(error_str))
return JS_EXCEPTION;
JSValue suppressed_str = JS_ToString(ctx, data->suppressed);
if (JS_IsException(suppressed_str)) {
JS_FreeValue(ctx, error_str);
return JS_EXCEPTION;
}
const char *error_cstr = JS_ToCString(ctx, error_str);
const char *suppressed_cstr = JS_ToCString(ctx, suppressed_str);
if (!error_cstr || !suppressed_cstr) {
JS_FreeCString(ctx, error_cstr);
JS_FreeCString(ctx, suppressed_cstr);
JS_FreeValue(ctx, error_str);
JS_FreeValue(ctx, suppressed_str);
return JS_EXCEPTION;
}
size_t len = strlen(error_cstr) + strlen(suppressed_cstr) + 50;
char *buf = js_malloc(ctx, len);
if (!buf) {
JS_FreeCString(ctx, error_cstr);
JS_FreeCString(ctx, suppressed_cstr);
JS_FreeValue(ctx, error_str);
JS_FreeValue(ctx, suppressed_str);
return JS_EXCEPTION;
}
snprintf(buf, len, "SuppressedError: %s (suppressed: %s)", error_cstr, suppressed_cstr);
JS_FreeCString(ctx, error_cstr);
JS_FreeCString(ctx, suppressed_cstr);
JS_FreeValue(ctx, error_str);
JS_FreeValue(ctx, suppressed_str);
JSValue result = JS_NewString(ctx, buf);
js_free(ctx, buf);
return result;
}
static const JSCFunctionListEntry js_suppressed_error_proto_funcs[] = {
JS_CFUNC_DEF("toString", 0, js_suppressed_error_toString),
JS_PROP_STRING_DEF("[Symbol.toStringTag]", "SuppressedError", JS_PROP_CONFIGURABLE),
};
static const JSCFunctionListEntry js_suppressed_error_static_funcs[] = {
JS_PROP_INT32_DEF("length", 2, JS_PROP_CONFIGURABLE),
};
JSValue JS_NewSuppressedError(JSContext *ctx, JSValueConst error, JSValueConst suppressed)
{
JSValue args[2];
args[0] = JS_DupValue(ctx, error);
args[1] = JS_DupValue(ctx, suppressed);
JSValue result = js_suppressed_error_constructor(ctx, JS_UNDEFINED, 2, (JSValueConst *)args);
JS_FreeValue(ctx, args[0]);
JS_FreeValue(ctx, args[1]);
return result;
}
JSValue JS_GetSuppressedErrorError(JSContext *ctx, JSValueConst obj)
{
JSSuppressedErrorData *data = JS_GetOpaque2(ctx, obj, js_suppressed_error_class_id);
if (!data)
return JS_EXCEPTION;
return JS_DupValue(ctx, data->error);
}
JSValue JS_GetSuppressedErrorSuppressed(JSContext *ctx, JSValueConst obj)
{
JSSuppressedErrorData *data = JS_GetOpaque2(ctx, obj, js_suppressed_error_class_id);
if (!data)
return JS_EXCEPTION;
return JS_DupValue(ctx, data->suppressed);
}
JSAtom JS_AtomSymbolDispose(JSContext *ctx)
{
return JS_DupAtom(ctx, JS_Atom(ctx, JS_ATOM Symbol_toPrimitive));
}
JSAtom JS_AtomSymbolAsyncDispose(JSContext *ctx)
{
return JS_DupAtom(ctx, JS_Atom(ctx, JS_ATOM Symbol_asyncIterator));
}
static JSValue js_dispose_method(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
return JS_UNDEFINED;
}
static JSValue js_async_dispose_method(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
return JS_UNDEFINED;
}
int JS_CallDispose(JSContext *ctx, JSValueConst obj)
{
if (JS_IsNull(obj) || JS_IsUndefined(obj))
return 0;
JSValue obj_val = JS_ToObject(ctx, obj);
if (JS_IsException(obj_val))
return -1;
JSAtom dispose_atom = JS_AtomSymbolDispose(ctx);
JSValue dispose_func = JS_GetProperty(ctx, obj_val, dispose_atom);
JS_FreeAtom(ctx, dispose_atom);
if (JS_IsException(dispose_func)) {
JS_FreeValue(ctx, obj_val);
return -1;
}
if (JS_IsUndefined(dispose_func) || JS_IsNull(dispose_func)) {
JS_FreeValue(ctx, dispose_func);
JS_FreeValue(ctx, obj_val);
return 0;
}
JSValue result = JS_Call(ctx, dispose_func, obj_val, 0, NULL);
JS_FreeValue(ctx, dispose_func);
JS_FreeValue(ctx, obj_val);
if (JS_IsException(result)) {
JS_FreeValue(ctx, result);
return -1;
}
JS_FreeValue(ctx, result);
return 0;
}
int JS_CallAsyncDispose(JSContext *ctx, JSValueConst obj)
{
if (JS_IsNull(obj) || JS_IsUndefined(obj))
return 0;
JSValue obj_val = JS_ToObject(ctx, obj);
if (JS_IsException(obj_val))
return -1;
JSAtom async_dispose_atom = JS_AtomSymbolAsyncDispose(ctx);
JSValue async_dispose_func = JS_GetProperty(ctx, obj_val, async_dispose_atom);
JS_FreeAtom(ctx, async_dispose_atom);
if (JS_IsException(async_dispose_func)) {
JS_FreeValue(ctx, obj_val);
return -1;
}
if (JS_IsUndefined(async_dispose_func) || JS_IsNull(async_dispose_func)) {
JS_FreeValue(ctx, async_dispose_func);
JS_FreeValue(ctx, obj_val);
return 0;
}
JSValue result = JS_Call(ctx, async_dispose_func, obj_val, 0, NULL);
JS_FreeValue(ctx, async_dispose_func);
JS_FreeValue(ctx, obj_val);
if (JS_IsException(result)) {
JS_FreeValue(ctx, result);
return -1;
}
JS_FreeValue(ctx, result);
return 0;
}
void js_init_disposable(JSContext *ctx, JSValueConst global_obj)
{
JSValue suppressed_error_proto, suppressed_error_ctor;
JS_NewClassID(ctx->rt, &js_suppressed_error_class_id);
JS_NewClass(ctx->rt, js_suppressed_error_class_id, &js_suppressed_error_class);
suppressed_error_proto = JS_NewObject(ctx);
if (JS_IsException(suppressed_error_proto))
return;
JS_SetPropertyFunctionList(ctx, suppressed_error_proto, js_suppressed_error_proto_funcs,
countof(js_suppressed_error_proto_funcs));
suppressed_error_ctor = JS_NewCFunction2(ctx, js_suppressed_error_constructor,
"SuppressedError", 2,
JS_CFUNC_constructor, 0);
JS_SetConstructor(ctx, suppressed_error_ctor, suppressed_error_proto);
JS_SetPropertyFunctionList(ctx, suppressed_error_ctor, js_suppressed_error_static_funcs,
countof(js_suppressed_error_static_funcs));
JS_DefinePropertyValueStr(ctx, global_obj, "SuppressedError", suppressed_error_ctor,
JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE);
}

24
builtins/js-disposable.h Normal file

@ -0,0 +1,24 @@
#ifndef JS_DISPOSABLE_H
#define JS_DISPOSABLE_H
#include "quickjs.h"
#ifdef __cplusplus
extern "C" {
#endif
JSValue JS_NewSuppressedError(JSContext *ctx, JSValueConst error, JSValueConst suppressed);
JSValue JS_GetSuppressedErrorError(JSContext *ctx, JSValueConst obj);
JSValue JS_GetSuppressedErrorSuppressed(JSContext *ctx, JSValueConst obj);
JSAtom JS_AtomSymbolDispose(JSContext *ctx);
JSAtom JS_AtomSymbolAsyncDispose(JSContext *ctx);
int JS_CallDispose(JSContext *ctx, JSValueConst obj);
int JS_CallAsyncDispose(JSContext *ctx, JSValueConst obj);
#ifdef __cplusplus
}
#endif
#endif

@ -275,5 +275,7 @@ DEF(Symbol_hasInstance, "Symbol.hasInstance")
DEF(Symbol_species, "Symbol.species")
DEF(Symbol_unscopables, "Symbol.unscopables")
DEF(Symbol_asyncIterator, "Symbol.asyncIterator")
DEF(Symbol_dispose, "Symbol.dispose")
DEF(Symbol_asyncDispose, "Symbol.asyncDispose")
#endif /* DEF */

@ -215,6 +215,10 @@ DEF( yield_star, 1, 1, 2, none)
DEF(async_yield_star, 1, 1, 2, none)
DEF( await, 1, 1, 1, none)
/* explicit resource management */
DEF( dispose_call, 1, 1, 0, none)
DEF( async_dispose, 1, 1, 1, none)
/* arithmetic/logic operations */
DEF( neg, 1, 1, 1, none)
DEF( plus, 1, 1, 1, none)

@ -21304,6 +21304,8 @@ enum {
TOK_STATIC,
TOK_YIELD,
TOK_AWAIT, /* must be last */
TOK_USING,
TOK_AWAIT_USING,
TOK_OF, /* only used for js_parse_skip_parens_token() */
};
@ -23726,6 +23728,8 @@ typedef enum {
JS_VAR_DEF_NEW_FUNCTION_DECL, /* async/generator function declaration */
JS_VAR_DEF_CATCH,
JS_VAR_DEF_VAR,
JS_VAR_DEF_USING,
JS_VAR_DEF_AWAIT_USING,
} JSVarDefEnum;
static int define_var(JSParseState *s, JSFunctionDef *fd, JSAtom name,