Page MenuHomePhorge

using.h
No OneTemporary

Size
12 KB
Referenced Files
None
Subscribers
None
#ifndef SV_USING_H
#define SV_USING_H
#include "silver/engine.h"
#include "errors.h"
#include "gc/roots.h"
#include "modules/symbol.h"
typedef enum {
SV_DISPOSAL_RECORD_DEFER = 0,
SV_DISPOSAL_RECORD_ADOPT = 1,
SV_DISPOSAL_RECORD_USE = 2
} sv_disposal_record_kind_t;
static inline void sv_using_array_clear(ant_value_t arr) {
ant_object_t *ptr = js_obj_ptr(js_as_obj(arr));
if (ptr && ptr->type_tag == T_ARR) ptr->u.array.len = 0;
}
static inline ant_value_t sv_make_suppressed_error_value(
ant_t *js, ant_value_t error, ant_value_t suppressed
) {
GC_ROOT_SAVE(root_mark, js);
GC_ROOT_PIN(js, error);
GC_ROOT_PIN(js, suppressed);
ant_value_t obj = js_mkobj(js);
GC_ROOT_PIN(js, obj);
if (is_err(obj)) {
GC_ROOT_RESTORE(js, root_mark);
return obj;
}
ant_value_t proto = js_get_ctor_proto(js, "SuppressedError", 15);
if (is_object_type(proto)) js_set_proto_init(obj, proto);
js_mkprop_fast(js, obj, "error", 5, error);
js_mkprop_fast(js, obj, "suppressed", 10, suppressed);
js_mkprop_fast(js, obj, "message", 7, ANT_STRING("An error was suppressed during disposal."));
js_mkprop_fast(js, obj, "name", 4, ANT_STRING("SuppressedError"));
js_set_slot(obj, SLOT_ERROR_BRAND, js_true);
js_capture_stack(js, obj);
GC_ROOT_RESTORE(js, root_mark);
return obj;
}
static inline ant_value_t sv_disposal_error_value(ant_t *js, ant_value_t result) {
if (js->thrown_exists) {
ant_value_t thrown = js->thrown_value;
js->thrown_value = js_mkundef();
js->thrown_stack = js_mkundef();
js->thrown_exists = false;
return thrown;
}
if (is_err(result)) {
if (vdata(result) != 0) return mkval(T_OBJ, vdata(result));
return js_make_error_silent(js, JS_ERR_INTERNAL, "unknown disposal error");
}
return result;
}
static inline ant_value_t sv_suppress_disposal_error(
ant_t *js, ant_value_t error, ant_value_t previous
) {
if (vtype(previous) == T_UNDEF) return error;
return sv_make_suppressed_error_value(js, error, previous);
}
static inline ant_value_t sv_disposal_record_call(ant_t *js, ant_value_t record) {
ant_value_t kind_v = js_arr_get(js, record, 0);
ant_value_t value = js_arr_get(js, record, 1);
ant_value_t method = js_arr_get(js, record, 2);
int kind = vtype(kind_v) == T_NUM
? (int)js_getnum(kind_v) : -1;
ant_value_t this_arg = kind == SV_DISPOSAL_RECORD_USE ? value : js_mkundef();
ant_value_t arg = value;
ant_value_t *args = kind == SV_DISPOSAL_RECORD_ADOPT ? &arg : NULL;
int nargs = kind == SV_DISPOSAL_RECORD_ADOPT ? 1 : 0;
if (!is_callable(method))
return js_mkerr_typed(js, JS_ERR_TYPE, "disposer is not callable");
if (vtype(method) == T_CFUNC) {
ant_value_t saved_this = js->this_val;
js->this_val = this_arg;
ant_value_t result = js_as_cfunc(method)(js, args, nargs);
js->this_val = saved_this;
return result;
}
return sv_vm_call(js->vm, js, method, this_arg, args, nargs, NULL, false);
}
static inline ant_value_t sv_dispose_resource(ant_t *js, ant_value_t resource, bool is_async) {
if (vtype(resource) == T_NULL || vtype(resource) == T_UNDEF) return js_mkundef();
ant_value_t method = js_get_sym(js, resource, is_async ? get_asyncDispose_sym() : get_dispose_sym());
if (is_async && (vtype(method) == T_UNDEF || vtype(method) == T_NULL)) method = js_get_sym(js, resource, get_dispose_sym());
if (!is_callable(method)) return js_mkerr_typed(
js, JS_ERR_TYPE, is_async
? "resource is not async disposable"
: "resource is not disposable"
);
if (vtype(method) == T_CFUNC) {
ant_value_t saved_this = js->this_val;
js->this_val = resource;
ant_value_t result = js_as_cfunc(method)(js, NULL, 0);
js->this_val = saved_this;
return result;
}
return sv_vm_call(js->vm, js, method, resource, NULL, 0, NULL, false);
}
static inline ant_value_t sv_using_push(
ant_t *js, ant_value_t entries, ant_value_t resource, bool is_async
) {
if (vtype(resource) == T_NULL || vtype(resource) == T_UNDEF)
return resource;
if (vtype(entries) != T_ARR)
return js_mkerr_typed(js, JS_ERR_TYPE, "invalid using disposal stack");
GC_ROOT_SAVE(root_mark, js);
GC_ROOT_PIN(js, entries);
GC_ROOT_PIN(js, resource);
ant_value_t method = js_get_sym(js, resource, is_async ? get_asyncDispose_sym() : get_dispose_sym());
if (is_async && (vtype(method) == T_UNDEF || vtype(method) == T_NULL)) method = js_get_sym(js, resource, get_dispose_sym());
GC_ROOT_PIN(js, method);
if (!is_callable(method)) {
GC_ROOT_RESTORE(js, root_mark);
return js_mkerr_typed(js, JS_ERR_TYPE, "resource is not disposable");
}
ant_value_t record = js_mkarr(js);
GC_ROOT_PIN(js, record);
if (is_err(record)) {
GC_ROOT_RESTORE(js, root_mark);
return record;
}
js_arr_push(js, record, js_mknum((double)SV_DISPOSAL_RECORD_USE));
js_arr_push(js, record, resource);
js_arr_push(js, record, method);
js_arr_push(js, entries, record);
GC_ROOT_RESTORE(js, root_mark);
return resource;
}
static inline ant_value_t sv_dispose_records_sync(
ant_t *js, ant_value_t records, ant_offset_t len,
ant_value_t *completion, bool throw_completion
) {
for (ant_offset_t i = len; i > 0; i--) {
GC_ROOT_SAVE(iter_mark, js);
ant_value_t record = js_arr_get(js, records, i - 1);
GC_ROOT_PIN(js, record);
ant_value_t result = sv_disposal_record_call(js, record);
if (is_err(result) || js->thrown_exists) {
ant_value_t error = sv_disposal_error_value(js, result);
GC_ROOT_PIN(js, error);
*completion = sv_suppress_disposal_error(js, error, *completion);
if (is_err(*completion)) {
GC_ROOT_RESTORE(js, iter_mark);
return *completion;
}}
GC_ROOT_RESTORE(js, iter_mark);
}
if (throw_completion && vtype(*completion) != T_UNDEF) {
ant_value_t thrown = js_throw(js, *completion);
return thrown;
}
return *completion;
}
static inline ant_value_t sv_using_dispose_sync(
ant_t *js, ant_value_t entries, ant_value_t completion, bool throw_completion
) {
if (vtype(entries) != T_ARR) return js_mkerr_typed(js, JS_ERR_TYPE, "invalid using disposal stack");
GC_ROOT_SAVE(root_mark, js);
GC_ROOT_PIN(js, entries);
GC_ROOT_PIN(js, completion);
ant_offset_t len = js_arr_len(js, entries);
ant_value_t work = js_mkarr(js);
GC_ROOT_PIN(js, work);
if (is_err(work)) {
GC_ROOT_RESTORE(js, root_mark);
return work;
}
for (ant_offset_t i = 0; i < len; i++) {
GC_ROOT_SAVE(copy_mark, js);
ant_value_t record = js_arr_get(js, entries, i);
GC_ROOT_PIN(js, record);
js_arr_push(js, work, record);
GC_ROOT_RESTORE(js, copy_mark);
}
sv_using_array_clear(entries);
ant_value_t result = sv_dispose_records_sync(
js, work, js_arr_len(js, work), &completion, throw_completion
);
GC_ROOT_RESTORE(js, root_mark);
return result;
}
static inline ant_value_t sv_async_dispose_continue(
ant_t *js,
ant_value_t state,
bool rejected,
ant_value_t reason
);
static inline ant_value_t sv_async_dispose_on_fulfilled(ant_t *js, ant_value_t *args, int nargs) {
ant_value_t state = js_get_slot(js_getcurrentfunc(js), SLOT_DATA);
return sv_async_dispose_continue(js, state, false, js_mkundef());
}
static inline ant_value_t sv_async_dispose_on_rejected(
ant_t *js,
ant_value_t *args,
int nargs
) {
ant_value_t state = js_get_slot(js_getcurrentfunc(js), SLOT_DATA);
return sv_async_dispose_continue(js, state, true, nargs > 0 ? args[0] : js_mkundef());
}
static inline ant_value_t sv_async_dispose_continue(
ant_t *js,
ant_value_t state,
bool rejected,
ant_value_t reason
) {
GC_ROOT_SAVE(root_mark, js);
GC_ROOT_PIN(js, state);
GC_ROOT_PIN(js, reason);
ant_value_t entries = js_get_slot(state, SLOT_ENTRIES);
ant_value_t result_promise = js_get_slot(state, SLOT_DATA);
ant_value_t completion = js_get_slot(state, SLOT_AUX);
GC_ROOT_PIN(js, entries);
GC_ROOT_PIN(js, result_promise);
GC_ROOT_PIN(js, completion);
if (rejected) {
completion = sv_suppress_disposal_error(js, reason, completion);
GC_ROOT_PIN(js, completion);
js_set_slot(state, SLOT_AUX, completion);
}
ant_value_t idx_v = js_get_slot(state, SLOT_ITER_STATE);
ant_offset_t idx = vtype(idx_v) == T_NUM ? (ant_offset_t)js_getnum(idx_v) : 0;
while (idx > 0) {
GC_ROOT_SAVE(iter_mark, js);
idx--;
js_set_slot(state, SLOT_ITER_STATE, js_mknum((double)idx));
ant_value_t record = js_arr_get(js, entries, idx);
GC_ROOT_PIN(js, record);
ant_value_t result = sv_disposal_record_call(js, record);
GC_ROOT_PIN(js, result);
if (is_err(result) || js->thrown_exists) {
ant_value_t error = sv_disposal_error_value(js, result);
GC_ROOT_PIN(js, error);
completion = sv_suppress_disposal_error(js, error, completion);
js_set_slot(state, SLOT_AUX, completion);
GC_ROOT_RESTORE(js, iter_mark);
continue;
}
if (vtype(result) == T_PROMISE) {
ant_value_t on_fulfilled = js_heavy_mkfun(js, sv_async_dispose_on_fulfilled, state);
GC_ROOT_PIN(js, on_fulfilled);
ant_value_t on_rejected = js_heavy_mkfun(js, sv_async_dispose_on_rejected, state);
GC_ROOT_PIN(js, on_rejected);
js_promise_then(js, result, on_fulfilled, on_rejected);
GC_ROOT_RESTORE(js, iter_mark);
GC_ROOT_RESTORE(js, root_mark);
return result_promise;
}
GC_ROOT_RESTORE(js, iter_mark);
}
if (vtype(completion) != T_UNDEF) js_reject_promise(js, result_promise, completion);
else js_resolve_promise(js, result_promise, js_mkundef());
GC_ROOT_RESTORE(js, root_mark);
return result_promise;
}
static inline ant_value_t sv_using_dispose_async(
ant_t *js,
ant_value_t entries,
ant_value_t completion
) {
ant_value_t result_promise = js_mkpromise(js);
if (is_err(result_promise)) return result_promise;
GC_ROOT_SAVE(root_mark, js);
GC_ROOT_PIN(js, result_promise);
GC_ROOT_PIN(js, completion);
if (vtype(entries) != T_ARR) {
ant_value_t error = js_make_error_silent(js, JS_ERR_TYPE, "invalid using disposal stack");
GC_ROOT_PIN(js, error);
js_reject_promise(js, result_promise, error);
GC_ROOT_RESTORE(js, root_mark);
return result_promise;
}
GC_ROOT_PIN(js, entries);
ant_offset_t len = js_arr_len(js, entries);
ant_value_t work = js_mkarr(js);
GC_ROOT_PIN(js, work);
if (is_err(work)) {
GC_ROOT_RESTORE(js, root_mark);
return work;
}
for (ant_offset_t i = 0; i < len; i++) {
GC_ROOT_SAVE(copy_mark, js);
ant_value_t record = js_arr_get(js, entries, i);
GC_ROOT_PIN(js, record);
js_arr_push(js, work, record);
GC_ROOT_RESTORE(js, copy_mark);
}
sv_using_array_clear(entries);
ant_value_t state = js_mkobj(js);
GC_ROOT_PIN(js, state);
if (is_err(state)) {
GC_ROOT_RESTORE(js, root_mark);
return state;
}
js_set_slot(state, SLOT_ENTRIES, work);
js_set_slot(state, SLOT_DATA, result_promise);
js_set_slot(state, SLOT_AUX, completion);
js_set_slot(state, SLOT_ITER_STATE, js_mknum((double)js_arr_len(js, work)));
ant_value_t result = sv_async_dispose_continue(js, state, false, js_mkundef());
GC_ROOT_RESTORE(js, root_mark);
return result;
}
static inline ant_value_t sv_using_dispose(
ant_t *js,
ant_value_t entries,
ant_value_t completion,
bool is_async,
bool suppressed
) {
ant_value_t actual_completion = suppressed ? completion : js_mkundef();
if (is_async) return sv_using_dispose_async(js, entries, actual_completion);
return sv_using_dispose_sync(js, entries, actual_completion, !suppressed);
}
static inline ant_value_t sv_op_using_push(sv_vm_t *vm, ant_t *js, bool is_async) {
ant_value_t resource = vm->stack[--vm->sp];
ant_value_t entries = vm->stack[--vm->sp];
ant_value_t result = sv_using_push(js, entries, resource, is_async);
if (!is_err(result)) vm->stack[vm->sp++] = result;
return result;
}
static inline ant_value_t sv_op_dispose_resource(sv_vm_t *vm, ant_t *js, bool is_async) {
ant_value_t resource = vm->stack[--vm->sp];
ant_value_t result = sv_dispose_resource(js, resource, is_async);
if (!is_err(result)) vm->stack[vm->sp++] = result;
return result;
}
static inline ant_value_t sv_op_using_dispose(
sv_vm_t *vm,
ant_t *js,
bool is_async,
bool suppressed
) {
ant_value_t completion = suppressed ? vm->stack[--vm->sp] : js_mkundef();
ant_value_t entries = vm->stack[--vm->sp];
ant_value_t result = sv_using_dispose(js, entries, completion, is_async, suppressed);
if (!is_err(result)) vm->stack[vm->sp++] = result;
return result;
}
#endif

File Metadata

Mime Type
text/x-c
Expires
Fri, May 1, 6:23 PM (2 d)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
542817
Default Alt Text
using.h (12 KB)

Event Timeline