Simplify iterator helpers code, remove duplication

This commit is contained in:
Saúl Ibarra Corretgé 2024-11-18 23:43:39 +01:00 committed by GitHub
parent 55b67a6591
commit 0b9b6c1915
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

432
quickjs.c
View file

@ -712,9 +712,13 @@ typedef enum JSIteratorKindEnum {
typedef enum JSIteratorHelperKindEnum {
JS_ITERATOR_HELPER_KIND_DROP,
JS_ITERATOR_HELPER_KIND_EVERY,
JS_ITERATOR_HELPER_KIND_FILTER,
JS_ITERATOR_HELPER_KIND_FIND,
JS_ITERATOR_HELPER_KIND_FLAT_MAP,
JS_ITERATOR_HELPER_KIND_FOR_EACH,
JS_ITERATOR_HELPER_KIND_MAP,
JS_ITERATOR_HELPER_KIND_SOME,
JS_ITERATOR_HELPER_KIND_TAKE,
} JSIteratorHelperKindEnum;
@ -40185,17 +40189,44 @@ fail:
return JS_EXCEPTION;
}
static JSValue js_create_iterator_helper(JSContext *ctx, JSValue iterator,
JSIteratorHelperKindEnum kind, JSValue func, int64_t count);
static JSValue js_iterator_proto_drop(JSContext *ctx, JSValue this_val,
int argc, JSValue *argv)
static int check_iterator(JSContext *ctx, JSValue obj)
{
if (!JS_IsObject(obj)) {
JS_ThrowTypeErrorNotAnObject(ctx);
return -1;
}
return 0;
}
typedef struct JSIteratorHelperData {
JSValue obj;
JSValue next;
JSValue func; // predicate (filter) or mapper (flatMap, map)
JSValue inner; // innerValue (flatMap)
int64_t count; // limit (drop, take) or counter (filter, map, flatMap)
JSIteratorHelperKindEnum kind : 8;
uint8_t executing : 1;
uint8_t done : 1;
} JSIteratorHelperData;
static JSValue js_create_iterator_helper(JSContext *ctx, JSValue this_val,
int argc, JSValue *argv, int magic)
{
JSValue func, obj, method;
int64_t count;
JSIteratorHelperData *it;
if (check_iterator(ctx, this_val) < 0)
return JS_EXCEPTION;
func = JS_UNDEFINED;
count = 0;
switch(magic) {
case JS_ITERATOR_HELPER_KIND_DROP:
case JS_ITERATOR_HELPER_KIND_TAKE:
{
JSValue v;
double dlimit;
int64_t limit;
if (!JS_IsObject(this_val))
return JS_ThrowTypeError(ctx, "Iterator.prototype.drop called on non-object");
v = JS_ToNumber(ctx, argv[0]);
if (JS_IsException(v))
return JS_EXCEPTION;
@ -40213,42 +40244,87 @@ static JSValue js_iterator_proto_drop(JSContext *ctx, JSValue this_val,
if (dlimit < 0)
goto fail;
else
limit = MAX_SAFE_INTEGER;
count = MAX_SAFE_INTEGER;
} else {
v = JS_ToIntegerFree(ctx, v);
if (JS_IsException(v))
return JS_EXCEPTION;
if (JS_ToInt64Free(ctx, &limit, v))
if (JS_ToInt64Free(ctx, &count, v))
return JS_EXCEPTION;
}
if (limit < 0) {
if (count < 0) {
fail:
return JS_ThrowRangeError(ctx, "must be positive");
}
return js_create_iterator_helper(ctx, this_val, JS_ITERATOR_HELPER_KIND_DROP, JS_UNDEFINED, limit);
}
break;
case JS_ITERATOR_HELPER_KIND_FILTER:
case JS_ITERATOR_HELPER_KIND_FLAT_MAP:
case JS_ITERATOR_HELPER_KIND_MAP:
{
func = argv[0];
if (check_function(ctx, func))
return JS_EXCEPTION;
}
break;
default:
abort();
break;
}
method = JS_GetProperty(ctx, this_val, JS_ATOM_next);
if (JS_IsException(method))
return JS_EXCEPTION;
obj = JS_NewObjectClass(ctx, JS_CLASS_ITERATOR_HELPER);
if (JS_IsException(obj)) {
JS_FreeValue(ctx, method);
return JS_EXCEPTION;
}
it = js_malloc(ctx, sizeof(*it));
if (!it) {
JS_FreeValue(ctx, obj);
JS_FreeValue(ctx, method);
return JS_EXCEPTION;
}
it->kind = magic;
it->obj = js_dup(this_val);
it->func = js_dup(func);
it->next = method;
it->inner = JS_UNDEFINED;
it->count = count;
it->executing = 0;
it->done = 0;
JS_SetOpaqueInternal(obj, it);
return obj;
}
static JSValue js_iterator_proto_every(JSContext *ctx, JSValue this_val,
int argc, JSValue *argv)
static JSValue js_iterator_proto_func(JSContext *ctx, JSValue this_val,
int argc, JSValue *argv, int magic)
{
JSValue item, method, ret, func, index_val, r;
JSValue args[2];
int64_t idx;
BOOL done;
if (!JS_IsObject(this_val))
return JS_ThrowTypeError(ctx, "Iterator.prototype.every called on non-object");
if (check_iterator(ctx, this_val) < 0)
return JS_EXCEPTION;
if (check_function(ctx, argv[0]))
return JS_EXCEPTION;
func = js_dup(argv[0]);
method = JS_GetProperty(ctx, this_val, JS_ATOM_next);
if (JS_IsException(method))
goto exception;
goto fail;
r = JS_UNDEFINED;
switch(magic) {
case JS_ITERATOR_HELPER_KIND_EVERY:
{
r = JS_TRUE;
for (idx = 0; /*empty*/; idx++) {
item = JS_IteratorNext(ctx, this_val, method, 0, NULL, &done);
if (JS_IsException(item))
goto exception;
goto fail;
if (done)
break;
index_val = js_int64(idx);
@ -40258,7 +40334,7 @@ static JSValue js_iterator_proto_every(JSContext *ctx, JSValue this_val,
JS_FreeValue(ctx, item);
JS_FreeValue(ctx, index_val);
if (JS_IsException(ret))
goto exception;
goto fail;
if (!JS_ToBoolFree(ctx, ret)) {
if (JS_IteratorClose(ctx, this_val, FALSE) < 0)
r = JS_EXCEPTION;
@ -40270,49 +40346,14 @@ static JSValue js_iterator_proto_every(JSContext *ctx, JSValue this_val,
ret = JS_UNDEFINED;
item = JS_UNDEFINED;
}
JS_FreeValue(ctx, func);
JS_FreeValue(ctx, method);
return r;
exception:
JS_IteratorClose(ctx, this_val, TRUE);
JS_FreeValue(ctx, func);
JS_FreeValue(ctx, method);
return JS_EXCEPTION;
}
static JSValue js_iterator_proto_filter(JSContext *ctx, JSValue this_val,
int argc, JSValue *argv)
{
JSValue func;
if (!JS_IsObject(this_val))
return JS_ThrowTypeError(ctx, "Iterator.prototype.filter called on non-object");
func = argv[0];
if (check_function(ctx, func))
return JS_EXCEPTION;
return js_create_iterator_helper(ctx, this_val, JS_ITERATOR_HELPER_KIND_FILTER, func, 0);
}
static JSValue js_iterator_proto_find(JSContext *ctx, JSValue this_val,
int argc, JSValue *argv)
{
JSValue item, method, ret, func, index_val, r;
JSValue args[2];
int64_t idx;
BOOL done;
if (!JS_IsObject(this_val))
return JS_ThrowTypeError(ctx, "Iterator.prototype.filter called on non-object");
if (check_function(ctx, argv[0]))
return JS_EXCEPTION;
func = js_dup(argv[0]);
method = JS_GetProperty(ctx, this_val, JS_ATOM_next);
if (JS_IsException(method))
goto exception;
r = JS_UNDEFINED;
}
break;
case JS_ITERATOR_HELPER_KIND_FIND:
{
for (idx = 0; /*empty*/; idx++) {
item = JS_IteratorNext(ctx, this_val, method, 0, NULL, &done);
if (JS_IsException(item))
goto exception;
goto fail;
if (done)
break;
index_val = js_int64(idx);
@ -40322,7 +40363,7 @@ static JSValue js_iterator_proto_find(JSContext *ctx, JSValue this_val,
JS_FreeValue(ctx, index_val);
if (JS_IsException(ret)) {
JS_FreeValue(ctx, item);
goto exception;
goto fail;
}
if (JS_ToBoolFree(ctx, ret)) {
if (JS_IteratorClose(ctx, this_val, FALSE) < 0) {
@ -40337,48 +40378,14 @@ static JSValue js_iterator_proto_find(JSContext *ctx, JSValue this_val,
ret = JS_UNDEFINED;
item = JS_UNDEFINED;
}
JS_FreeValue(ctx, func);
JS_FreeValue(ctx, method);
return r;
exception:
JS_IteratorClose(ctx, this_val, TRUE);
JS_FreeValue(ctx, func);
JS_FreeValue(ctx, method);
return JS_EXCEPTION;
}
static JSValue js_iterator_proto_flatMap(JSContext *ctx, JSValue this_val,
int argc, JSValue *argv)
{
JSValue func;
if (!JS_IsObject(this_val))
return JS_ThrowTypeError(ctx, "Iterator.prototype.flatMap called on non-object");
func = argv[0];
if (check_function(ctx, func))
return JS_EXCEPTION;
return js_create_iterator_helper(ctx, this_val, JS_ITERATOR_HELPER_KIND_FLAT_MAP, func, 0);
}
static JSValue js_iterator_proto_forEach(JSContext *ctx, JSValue this_val,
int argc, JSValue *argv)
{
JSValue item, method, ret, func, index_val;
JSValue args[2];
int64_t idx;
BOOL done;
if (!JS_IsObject(this_val))
return JS_ThrowTypeError(ctx, "Iterator.prototype.forEach called on non-object");
if (check_function(ctx, argv[0]))
return JS_EXCEPTION;
func = js_dup(argv[0]);
method = JS_GetProperty(ctx, this_val, JS_ATOM_next);
if (JS_IsException(method))
goto exception;
}
break;
case JS_ITERATOR_HELPER_KIND_FOR_EACH:
{
for (idx = 0; /*empty*/; idx++) {
item = JS_IteratorNext(ctx, this_val, method, 0, NULL, &done);
if (JS_IsException(item))
goto exception;
goto fail;
if (done)
break;
index_val = js_int64(idx);
@ -40388,34 +40395,59 @@ static JSValue js_iterator_proto_forEach(JSContext *ctx, JSValue this_val,
JS_FreeValue(ctx, item);
JS_FreeValue(ctx, index_val);
if (JS_IsException(ret))
goto exception;
goto fail;
JS_FreeValue(ctx, ret);
index_val = JS_UNDEFINED;
ret = JS_UNDEFINED;
item = JS_UNDEFINED;
}
}
break;
case JS_ITERATOR_HELPER_KIND_SOME:
{
r = JS_FALSE;
for (idx = 0; /*empty*/; idx++) {
item = JS_IteratorNext(ctx, this_val, method, 0, NULL, &done);
if (JS_IsException(item))
goto fail;
if (done)
break;
index_val = js_int64(idx);
args[0] = item;
args[1] = index_val;
ret = JS_Call(ctx, func, JS_UNDEFINED, countof(args), args);
JS_FreeValue(ctx, item);
JS_FreeValue(ctx, index_val);
if (JS_IsException(ret))
goto fail;
if (JS_ToBoolFree(ctx, ret)) {
if (JS_IteratorClose(ctx, this_val, FALSE) < 0)
r = JS_EXCEPTION;
else
r = JS_TRUE;
break;
}
index_val = JS_UNDEFINED;
ret = JS_UNDEFINED;
item = JS_UNDEFINED;
}
}
break;
default:
abort();
break;
}
JS_FreeValue(ctx, func);
JS_FreeValue(ctx, method);
return JS_UNDEFINED;
exception:
return r;
fail:
JS_IteratorClose(ctx, this_val, TRUE);
JS_FreeValue(ctx, func);
JS_FreeValue(ctx, method);
return JS_EXCEPTION;
}
static JSValue js_iterator_proto_map(JSContext *ctx, JSValue this_val,
int argc, JSValue *argv)
{
JSValue func;
if (!JS_IsObject(this_val))
return JS_ThrowTypeError(ctx, "Iterator.prototype.map called on non-object");
func = argv[0];
if (check_function(ctx, func))
return JS_EXCEPTION;
return js_create_iterator_helper(ctx, this_val, JS_ITERATOR_HELPER_KIND_MAP, func, 0);
}
static JSValue js_iterator_proto_reduce(JSContext *ctx, JSValue this_val,
int argc, JSValue *argv)
{
@ -40424,8 +40456,8 @@ static JSValue js_iterator_proto_reduce(JSContext *ctx, JSValue this_val,
int64_t idx;
BOOL done;
if (!JS_IsObject(this_val))
return JS_ThrowTypeError(ctx, "Iterator.prototype.reduce called on non-object");
if (check_iterator(ctx, this_val) < 0)
return JS_EXCEPTION;
if (check_function(ctx, argv[0]))
return JS_EXCEPTION;
acc = JS_UNDEFINED;
@ -40478,98 +40510,6 @@ exception:
return JS_EXCEPTION;
}
static JSValue js_iterator_proto_some(JSContext *ctx, JSValue this_val,
int argc, JSValue *argv)
{
JSValue item, method, ret, func, index_val, r;
JSValue args[2];
int64_t idx;
BOOL done;
if (!JS_IsObject(this_val))
return JS_ThrowTypeError(ctx, "Iterator.prototype.some called on non-object");
if (check_function(ctx, argv[0]))
return JS_EXCEPTION;
func = js_dup(argv[0]);
method = JS_GetProperty(ctx, this_val, JS_ATOM_next);
if (JS_IsException(method))
goto exception;
r = JS_FALSE;
for (idx = 0; /*empty*/; idx++) {
item = JS_IteratorNext(ctx, this_val, method, 0, NULL, &done);
if (JS_IsException(item))
goto exception;
if (done)
break;
index_val = js_int64(idx);
args[0] = item;
args[1] = index_val;
ret = JS_Call(ctx, func, JS_UNDEFINED, countof(args), args);
JS_FreeValue(ctx, item);
JS_FreeValue(ctx, index_val);
if (JS_IsException(ret))
goto exception;
if (JS_ToBoolFree(ctx, ret)) {
if (JS_IteratorClose(ctx, this_val, FALSE) < 0)
r = JS_EXCEPTION;
else
r = JS_TRUE;
break;
}
index_val = JS_UNDEFINED;
ret = JS_UNDEFINED;
item = JS_UNDEFINED;
}
JS_FreeValue(ctx, func);
JS_FreeValue(ctx, method);
return r;
exception:
JS_IteratorClose(ctx, this_val, TRUE);
JS_FreeValue(ctx, func);
JS_FreeValue(ctx, method);
return JS_EXCEPTION;
}
static JSValue js_iterator_proto_take(JSContext *ctx, JSValue this_val,
int argc, JSValue *argv)
{
JSValue v;
double dlimit;
int64_t limit;
if (!JS_IsObject(this_val))
return JS_ThrowTypeError(ctx, "Iterator.prototype.take called on non-object");
v = JS_ToNumber(ctx, argv[0]);
if (JS_IsException(v))
return JS_EXCEPTION;
// Check for Infinity.
if (JS_ToFloat64(ctx, &dlimit, v)) {
JS_FreeValue(ctx, v);
return JS_EXCEPTION;
}
if (isnan(dlimit)) {
JS_FreeValue(ctx, v);
goto fail;
}
if (!isfinite(dlimit)) {
JS_FreeValue(ctx, v);
if (dlimit < 0)
goto fail;
else
limit = MAX_SAFE_INTEGER;
} else {
v = JS_ToIntegerFree(ctx, v);
if (JS_IsException(v))
return JS_EXCEPTION;
if (JS_ToInt64Free(ctx, &limit, v))
return JS_EXCEPTION;
}
if (limit < 0) {
fail:
return JS_ThrowRangeError(ctx, "must be positive");
}
return js_create_iterator_helper(ctx, this_val, JS_ITERATOR_HELPER_KIND_TAKE, JS_UNDEFINED, limit);
}
static JSValue js_iterator_proto_toArray(JSContext *ctx, JSValue this_val,
int argc, JSValue *argv)
{
@ -40578,8 +40518,8 @@ static JSValue js_iterator_proto_toArray(JSContext *ctx, JSValue this_val,
BOOL done;
result = JS_UNDEFINED;
if (!JS_IsObject(this_val))
return JS_ThrowTypeError(ctx, "Iterator.prototype.toArray called on non-object");
if (check_iterator(ctx, this_val) < 0)
return JS_EXCEPTION;
method = JS_GetProperty(ctx, this_val, JS_ATOM_next);
if (JS_IsException(method))
return JS_EXCEPTION;
@ -40621,9 +40561,8 @@ static JSValue js_iterator_proto_set_toStringTag(JSContext *ctx, JSValue this_va
{
int res;
if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)
return JS_ThrowTypeError(ctx,
"set Iterator.prototype[Symbol.toStringTag] called on non-object");
if (check_iterator(ctx, this_val) < 0)
return JS_EXCEPTION;
if (js_same_value(ctx, this_val, ctx->class_proto[JS_CLASS_ITERATOR]))
return JS_ThrowTypeError(ctx, "Cannot assign to read only property");
res = JS_GetOwnProperty(ctx, NULL, this_val, JS_ATOM_Symbol_toStringTag);
@ -40639,17 +40578,6 @@ static JSValue js_iterator_proto_set_toStringTag(JSContext *ctx, JSValue this_va
return JS_UNDEFINED;
}
typedef struct JSIteratorHelperData {
JSIteratorHelperKindEnum kind;
JSValue obj;
JSValue next;
JSValue func; // predicate (filter) or mapper (flatMap, map)
JSValue inner; // innerValue (flatMap)
int64_t count; // limit (drop, take) or counter (filter, map, flatMap)
BOOL executing;
BOOL done;
} JSIteratorHelperData;
static void js_iterator_helper_finalizer(JSRuntime *rt, JSValue val)
{
JSObject *p = JS_VALUE_GET_OBJ(val);
@ -40695,7 +40623,7 @@ static JSValue js_iterator_helper_next(JSContext *ctx, JSValue this_val,
return JS_UNDEFINED;
}
it->executing = TRUE;
it->executing = 1;
switch (it->kind) {
case JS_ITERATOR_HELPER_KIND_DROP:
@ -40915,8 +40843,8 @@ static JSValue js_iterator_helper_next(JSContext *ctx, JSValue this_val,
}
done:
it->done = magic == GEN_MAGIC_NEXT ? *pdone : TRUE;
it->executing = FALSE;
it->done = magic == GEN_MAGIC_NEXT ? *pdone : 1;
it->executing = 0;
return ret;
fail:
if (it) {
@ -40927,53 +40855,21 @@ fail:
goto done;
}
static JSValue js_create_iterator_helper(JSContext *ctx, JSValue iterator,
JSIteratorHelperKindEnum kind, JSValue func, int64_t count)
{
JSValue obj, method;
JSIteratorHelperData *it;
method = JS_GetProperty(ctx, iterator, JS_ATOM_next);
if (JS_IsException(method))
return JS_EXCEPTION;
obj = JS_NewObjectClass(ctx, JS_CLASS_ITERATOR_HELPER);
if (JS_IsException(obj)) {
JS_FreeValue(ctx, method);
return JS_EXCEPTION;
}
it = js_malloc(ctx, sizeof(*it));
if (!it) {
JS_FreeValue(ctx, obj);
JS_FreeValue(ctx, method);
return JS_EXCEPTION;
}
it->kind = kind;
it->obj = js_dup(iterator);
it->func = js_dup(func);
it->next = method;
it->inner = JS_UNDEFINED;
it->count = count;
it->executing = FALSE;
it->done = FALSE;
JS_SetOpaqueInternal(obj, it);
return obj;
}
static const JSCFunctionListEntry js_iterator_funcs[] = {
JS_CFUNC_DEF("from", 1, js_iterator_from ),
};
static const JSCFunctionListEntry js_iterator_proto_funcs[] = {
JS_CFUNC_DEF("drop", 1, js_iterator_proto_drop ),
JS_CFUNC_DEF("every", 1, js_iterator_proto_every ),
JS_CFUNC_DEF("filter", 1, js_iterator_proto_filter ),
JS_CFUNC_DEF("find", 1, js_iterator_proto_find ),
JS_CFUNC_DEF("flatMap", 1, js_iterator_proto_flatMap ),
JS_CFUNC_DEF("forEach", 1, js_iterator_proto_forEach ),
JS_CFUNC_DEF("map", 1, js_iterator_proto_map ),
JS_CFUNC_MAGIC_DEF("drop", 1, js_create_iterator_helper, JS_ITERATOR_HELPER_KIND_DROP ),
JS_CFUNC_MAGIC_DEF("filter", 1, js_create_iterator_helper, JS_ITERATOR_HELPER_KIND_FILTER ),
JS_CFUNC_MAGIC_DEF("flatMap", 1, js_create_iterator_helper, JS_ITERATOR_HELPER_KIND_FLAT_MAP ),
JS_CFUNC_MAGIC_DEF("map", 1, js_create_iterator_helper, JS_ITERATOR_HELPER_KIND_MAP ),
JS_CFUNC_MAGIC_DEF("take", 1, js_create_iterator_helper, JS_ITERATOR_HELPER_KIND_TAKE ),
JS_CFUNC_MAGIC_DEF("every", 1, js_iterator_proto_func, JS_ITERATOR_HELPER_KIND_EVERY ),
JS_CFUNC_MAGIC_DEF("find", 1, js_iterator_proto_func, JS_ITERATOR_HELPER_KIND_FIND),
JS_CFUNC_MAGIC_DEF("forEach", 1, js_iterator_proto_func, JS_ITERATOR_HELPER_KIND_FOR_EACH ),
JS_CFUNC_MAGIC_DEF("some", 1, js_iterator_proto_func, JS_ITERATOR_HELPER_KIND_SOME ),
JS_CFUNC_DEF("reduce", 1, js_iterator_proto_reduce ),
JS_CFUNC_DEF("some", 1, js_iterator_proto_some ),
JS_CFUNC_DEF("take", 1, js_iterator_proto_take ),
JS_CFUNC_DEF("toArray", 0, js_iterator_proto_toArray ),
JS_CFUNC_DEF("[Symbol.iterator]", 0, js_iterator_proto_iterator ),
JS_CGETSET_DEF("[Symbol.toStringTag]", js_iterator_proto_get_toStringTag, js_iterator_proto_set_toStringTag),