mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-25 06:24:27 +08:00
Print iterators and iterator helper objects as Node does
console.log and util.inspect of a generator object, an iterator helper,
or an array or string iterator were refused, and printing a Map or Set
iterator threw "util.inspect of a dynamic Array Iterator is not
supported yet".
Compiled generators now carry the tag their object prints with:
"Generator" or "AsyncGenerator" by default, "Iterator Helper" for the
helpers, "Array Iterator" and "String Iterator" for the iterators the
compiler builds as generators. The spawn wrapper sets it through the new
scr_gen_set_tag; an adapter over another generator copies that
generator's tag (scr_gen_copy_tag), and generators that stand in for an
object of another class, such as a Set iterator adapted into an
iterator-typed slot, keep refusing to print. They print as Node's
Object [<tag>] {} at any depth, and Object.prototype.toString answers
[object <tag>].
The runtime's native iterators print as Node does: Object [Array
Iterator] {} and Object [String Iterator] {}, and for Map and Set
iterators the entries the next steps would produce, without advancing,
under [Set Iterator], [Set Entries], [Map Iterator] or [Map Entries],
as [Object [Set Iterator]] past the depth limit and { } when none are
left. Object.prototype.toString answers [object Set Iterator] and the
other tags.
This commit is contained in:
@@ -1 +1 @@
|
||||
- **Iterator helpers:** `map`, `filter`, `take`, `drop`, `flatMap`, `toArray`, `forEach`, `reduce`, `some`, `every` and `find` compile on generators, on array, typed array, string, Map and Set iterators, and on the objects the helpers return. Helper objects support `.next()`, `.return()`, `for...of`, spreads, `Array.from` and `yield*`, pull their source lazily, and close it (run its `return()`) at the same points as Node: when `take` reaches its count, when a short-circuiting method finds its answer, when a callback throws, and when the loop consuming the helper exits early. In TypeScript, `h.return!()` and `h.return?.()` compile when `h` is a helper call or a `const` initialized with one. `flatMap` callbacks return arrays, generators, Sets or Maps; a string result throws Node's TypeError. `Iterator.from` accepts arrays, strings, typed arrays, Sets, Maps and iterator objects such as generators. In JavaScript, untyped array, string, Map and Set iterators answer the same methods. `Iterator.from` over an object with its own `next` method or over an untyped value, the `Iterator` global as a value, and printing typed helper objects are unsupported.
|
||||
- **Iterator helpers:** `map`, `filter`, `take`, `drop`, `flatMap`, `toArray`, `forEach`, `reduce`, `some`, `every` and `find` compile on generators, on array, typed array, string, Map and Set iterators, and on the objects the helpers return. Helper objects support `.next()`, `.return()`, `for...of`, spreads, `Array.from` and `yield*`, pull their source lazily, and close it (run its `return()`) at the same points as Node: when `take` reaches its count, when a short-circuiting method finds its answer, when a callback throws, and when the loop consuming the helper exits early. In TypeScript, `h.return!()` and `h.return?.()` compile when `h` is a helper call or a `const` initialized with one. `flatMap` callbacks return arrays, generators, Sets or Maps; a string result throws Node's TypeError. `Iterator.from` accepts arrays, strings, typed arrays, Sets, Maps and iterator objects such as generators. In JavaScript, untyped array, string, Map and Set iterators answer the same methods. `console.log`, `util.inspect` and `Object.prototype.toString` print iterator objects as Node does: `Object [Iterator Helper] {}`, `Object [Generator] {}`, `Object [Array Iterator] {}`, and Map and Set iterators with the entries they have left. `Iterator.from` over an object with its own `next` method or over an untyped value, the `Iterator` global as a value, and `String()` and template literals of typed iterator objects are unsupported.
|
||||
|
||||
@@ -4644,6 +4644,9 @@ export class LlEmitter {
|
||||
// An iterator helper's return() before its first next() closes the
|
||||
// source generator in its first argument slot.
|
||||
const closesSource = fn.generator.closesSource === true;
|
||||
if (fn.generator.tag !== undefined) this.declare(`declare void @scr_gen_set_tag(ptr, ptr)`);
|
||||
if (fn.generator.inheritsTag === true)
|
||||
this.declare(`declare void @scr_gen_copy_tag(ptr, ptr)`);
|
||||
if (closesSource) {
|
||||
const field = lifted ? 1 : 0;
|
||||
this.declare(`declare void @scr_gen_close(ptr)`);
|
||||
@@ -4676,6 +4679,12 @@ export class LlEmitter {
|
||||
settleAsync === null
|
||||
? ` %gg = call ptr @${genNew}(ptr @${mangleTrampoline(fn.name)}, ptr %ap, ptr @${mangleGenDrop(fn.name)})`
|
||||
: ` %gg = call ptr @scr_async_gen_new(ptr @${mangleTrampoline(fn.name)}, ptr %ap, ptr @${mangleGenDrop(fn.name)}, ptr @${settleAsync})`,
|
||||
...(fn.generator.tag !== undefined
|
||||
? [` call void @scr_gen_set_tag(ptr %gg, ptr ${this.cstr(fn.generator.tag)})`]
|
||||
: []),
|
||||
...(fn.generator.inheritsTag === true
|
||||
? [` call void @scr_gen_copy_tag(ptr %gg, ptr %a${lifted ? 1 : 0})`]
|
||||
: []),
|
||||
...(closesSource
|
||||
? [
|
||||
` call void @scr_gen_set_unstarted_return(ptr %gg, ptr @${mangleGenSourceClose(fn.name)})`,
|
||||
|
||||
@@ -2259,6 +2259,7 @@ function buildHelperGenFn(
|
||||
yieldT: outT.yieldT,
|
||||
nextT: outT.nextT,
|
||||
resultType,
|
||||
tag: "Iterator Helper",
|
||||
// A native Map or Set iterator has no return() to call.
|
||||
...(source.kind === "generator" ? { closesSource: true as const } : {}),
|
||||
},
|
||||
|
||||
@@ -258,7 +258,7 @@ function dynIteratorAdapter(
|
||||
returnType: expected.retT,
|
||||
locals,
|
||||
body,
|
||||
generator: { yieldT: expected.yieldT, nextT: expected.nextT, resultType },
|
||||
generator: { yieldT: expected.yieldT, nextT: expected.nextT, resultType, tag: "!" },
|
||||
loc,
|
||||
});
|
||||
}
|
||||
@@ -374,7 +374,12 @@ export function lowerCollectionIteratorValue(
|
||||
loc,
|
||||
},
|
||||
],
|
||||
generator: { yieldT: type.yieldT, nextT: UNDEFINED_T, resultType },
|
||||
generator: {
|
||||
yieldT: type.yieldT,
|
||||
nextT: UNDEFINED_T,
|
||||
resultType,
|
||||
tag: "!",
|
||||
},
|
||||
loc,
|
||||
});
|
||||
}
|
||||
@@ -484,7 +489,7 @@ export function lowerBytesIteratorValue(
|
||||
},
|
||||
{ kind: "return", value: null, loc },
|
||||
],
|
||||
generator: { yieldT: type.yieldT, nextT: UNDEFINED_T, resultType },
|
||||
generator: { yieldT: type.yieldT, nextT: UNDEFINED_T, resultType, tag: "Array Iterator" },
|
||||
loc,
|
||||
});
|
||||
}
|
||||
@@ -590,7 +595,7 @@ export function lowerStringIteratorValue(
|
||||
},
|
||||
{ kind: "return", value: null, loc },
|
||||
],
|
||||
generator: { yieldT: type.yieldT, nextT: UNDEFINED_T, resultType },
|
||||
generator: { yieldT: type.yieldT, nextT: UNDEFINED_T, resultType, tag: "String Iterator" },
|
||||
loc,
|
||||
});
|
||||
}
|
||||
@@ -751,7 +756,12 @@ function generatorRetypeAdapter(
|
||||
loc,
|
||||
},
|
||||
],
|
||||
generator: { yieldT: expected.yieldT, nextT: expected.nextT, resultType: toResult },
|
||||
generator: {
|
||||
yieldT: expected.yieldT,
|
||||
nextT: expected.nextT,
|
||||
resultType: toResult,
|
||||
inheritsTag: true,
|
||||
},
|
||||
loc,
|
||||
});
|
||||
}
|
||||
@@ -1022,7 +1032,7 @@ export function lowerArrayIteratorValue(
|
||||
},
|
||||
{ kind: "return", value: null, loc },
|
||||
],
|
||||
generator: { yieldT: type.yieldT, nextT: UNDEFINED_T, resultType },
|
||||
generator: { yieldT: type.yieldT, nextT: UNDEFINED_T, resultType, tag: "Array Iterator" },
|
||||
loc,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2800,7 +2800,8 @@ function lowerObjectPrototypeCall(
|
||||
const immediate = immediateObjectTagReceiver(lowerer, receiverNode);
|
||||
const receiver = immediate?.effect ?? lowerer.lowerExpr(receiverNode);
|
||||
const tag = immediate?.tag ?? objectToStringTag(lowerer, receiver.type);
|
||||
if (tag === null && receiver.type.kind !== "dyn") {
|
||||
// A generator object answers its tag through its checked capsule.
|
||||
if (tag === null && receiver.type.kind !== "dyn" && receiver.type.kind !== "generator") {
|
||||
lowerer.noLowering(
|
||||
"Object.prototype.toString.call over a " + lowerer.fmt(receiver.type) + " receiver",
|
||||
receiverNode,
|
||||
@@ -2820,7 +2821,13 @@ function lowerObjectPrototypeCall(
|
||||
}
|
||||
const result: IrExpr =
|
||||
tag === null
|
||||
? { kind: "libCall", fn: "dyn.objectTag", args: [ref], type: STRING, loc }
|
||||
? {
|
||||
kind: "libCall",
|
||||
fn: "dyn.objectTag",
|
||||
args: [ref.type.kind === "generator" ? lowerer.coerceToExpected(ref, DYN) : ref],
|
||||
type: STRING,
|
||||
loc,
|
||||
}
|
||||
: { kind: "strLit", value: "[object " + tag + "]", type: STRING, loc };
|
||||
return { kind: "seqExpr", stmts, result, type: STRING, loc };
|
||||
}
|
||||
|
||||
@@ -253,6 +253,10 @@ function inspectSupport(
|
||||
visiting.delete(t.className);
|
||||
return null;
|
||||
}
|
||||
// Generator objects print through their checked capsule: Node's
|
||||
// `Object [Generator] {}`, or the tag of the iterator they implement.
|
||||
case "generator":
|
||||
return null;
|
||||
default:
|
||||
return `'${lowerer.fmt(t)}' values have no inspect lowering`;
|
||||
}
|
||||
@@ -343,6 +347,14 @@ export function inspectExpr(
|
||||
return { kind: "libCall", fn: "insp.buffer", args: [value], type: STRING, loc };
|
||||
case "dyn":
|
||||
return { kind: "libCall", fn: "insp.dyn", args: [value, recurse, depth], type: STRING, loc };
|
||||
case "generator":
|
||||
return {
|
||||
kind: "libCall",
|
||||
fn: "insp.dyn",
|
||||
args: [lowerer.coerceToExpected(value, DYN), recurse, depth],
|
||||
type: STRING,
|
||||
loc,
|
||||
};
|
||||
case "object":
|
||||
if (eventTargetRootOf(lowerer.classes.get(t.className)) !== null) {
|
||||
return {
|
||||
|
||||
@@ -143,6 +143,7 @@ const FUNCTION_FIELDS: Record<StringFields<Omit<IrFunction, "body">>, Role> = {
|
||||
"captures[].localId": "other",
|
||||
"classCaptures[].name": "other",
|
||||
"classCaptures[].localId": "other",
|
||||
"generator.tag": "other",
|
||||
};
|
||||
|
||||
const MODULE_FIELDS: Record<StringFields<Omit<IrModule, "functions">>, Role> = {
|
||||
|
||||
@@ -1712,6 +1712,15 @@ export interface IrFunction {
|
||||
* iterator helper's source): `.return()` before the first `.next()`
|
||||
* closes it. */
|
||||
closesSource?: true;
|
||||
/** The @@toStringTag the object prints with (util.inspect,
|
||||
* Object.prototype.toString) when it is not a generator object:
|
||||
* "Iterator Helper", "Array Iterator", "String Iterator". A tag that
|
||||
* starts with '!' marks a generator standing in for an object of
|
||||
* another class, which those refuse to print. */
|
||||
tag?: string;
|
||||
/** The first parameter is a generator this one adapts: it prints with
|
||||
* that generator's tag. */
|
||||
inheritsTag?: true;
|
||||
};
|
||||
/** Source function name for native error stack frames. */
|
||||
sourceName?: string;
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/6075-print-iterators.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/6076-print-iterators-js.js"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
@@ -4986,6 +4986,7 @@ struct ScrGen {
|
||||
void (*drop_args)(void *);
|
||||
/* scr_gen_set_unstarted_return's hook; NULL for most generators. */
|
||||
void (*unstarted_return)(void *);
|
||||
const char *tag; /* scr_gen_set_tag's; NULL is the default */
|
||||
bool is_async;
|
||||
void (*settle_async)(ScrGen *, ScrPromise *);
|
||||
ScrAsyncGenRequest *requests_head;
|
||||
@@ -5547,6 +5548,14 @@ void scr_gen_resume(ScrGen *g) {
|
||||
|
||||
void scr_gen_set_unstarted_return(ScrGen *g, void (*hook)(void *)) { g->unstarted_return = hook; }
|
||||
|
||||
void scr_gen_set_tag(ScrGen *g, const char *tag) { g->tag = tag; }
|
||||
|
||||
void scr_gen_copy_tag(ScrGen *g, const ScrGen *from) { g->tag = from->tag; }
|
||||
|
||||
const char *scr_gen_tag(const ScrGen *g) {
|
||||
return g->tag ? g->tag : g->is_async ? "AsyncGenerator" : "Generator";
|
||||
}
|
||||
|
||||
void scr_gen_close(ScrGen *g) {
|
||||
scr_gen_slot_reset(&g->ret);
|
||||
scr_gen_resume_return(g);
|
||||
|
||||
@@ -706,6 +706,12 @@ bool scr_dyn_iterator_step_done(const ScrDyn *iterator);
|
||||
* next live entry index of its collection, or -1 once the collection is
|
||||
* exhausted (the iterator then ends its iteration). */
|
||||
double scr_dyn_collection_iterator_index(const ScrDyn *iterator);
|
||||
/* For util.inspect: a native iterator's kind (0 array or typed array,
|
||||
* 1 string, 2 Map, 3 Set, 4 a handle's), its selection (0 values, 1 keys,
|
||||
* 2 entries), its collection (NULL once exhausted, and for kinds other
|
||||
* than 2 and 3) and the index its next step reads from. */
|
||||
void scr_dyn_native_iterator_state(const ScrDyn *iterator, unsigned *kind, unsigned *selection,
|
||||
ScrMap **collection, double *index);
|
||||
/* The ordinary object snapshot plus a retained typed source. source_access
|
||||
* releases that source when materialize=false and returns a fresh snapshot
|
||||
* when true. */
|
||||
|
||||
@@ -63,6 +63,16 @@ ScrGen *scr_async_gen_new(void (*entry)(ScrFiber *, void *), void *argpack,
|
||||
/* Runs when return() reaches the generator before its first resume, with
|
||||
* the packed arguments still alive: an iterator helper closes its source. */
|
||||
void scr_gen_set_unstarted_return(ScrGen *g, void (*hook)(void *));
|
||||
/* The @@toStringTag that util.inspect and Object.prototype.toString print
|
||||
* for the generator object: "Generator" (or "AsyncGenerator") unless set,
|
||||
* as the compiler does for iterator helpers ("Iterator Helper") and for
|
||||
* the array and string iterators it builds as generators. A tag that
|
||||
* starts with '!' marks a generator standing in for an object of another
|
||||
* class, which these refuse to print. */
|
||||
void scr_gen_set_tag(ScrGen *g, const char *tag);
|
||||
/* An adapter generator prints with the tag of the generator it adapts. */
|
||||
void scr_gen_copy_tag(ScrGen *g, const ScrGen *from);
|
||||
const char *scr_gen_tag(const ScrGen *g);
|
||||
ScrGen *scr_gen_retain(ScrGen *g);
|
||||
void scr_gen_release(ScrGen *g); /* NULL-tolerant */
|
||||
void *scr_gen_retain_v(void *g);
|
||||
|
||||
@@ -1058,6 +1058,99 @@ static ScrStr *scr_insp_weak_collection(const char *label, double recurse, doubl
|
||||
return ib_take(&out);
|
||||
}
|
||||
|
||||
/* A native iterator: the array and string iterators print Node's empty
|
||||
* `Object [Array Iterator] {}`; the Map and Set iterators print the
|
||||
* entries their next steps would produce, under Node's labels
|
||||
* (`[Set Iterator] { 1, 2 }`, `[Map Entries] { [ 'a', 1 ] }`), without
|
||||
* advancing. NULL for a handle's iterator (kind 4), which keeps the
|
||||
* unsupported answer. */
|
||||
static ScrStr *scr_insp_native_iterator(ScrDyn *d, double recurse, double depth) {
|
||||
unsigned kind, selection;
|
||||
ScrMap *map;
|
||||
double index;
|
||||
scr_dyn_native_iterator_state(d, &kind, &selection, &map, &index);
|
||||
if (kind == 0) return scr_str_new("Object [Array Iterator] {}", 26);
|
||||
if (kind == 1) return scr_str_new("Object [String Iterator] {}", 27);
|
||||
if (kind != 2 && kind != 3) return NULL;
|
||||
bool pairs = selection == 2;
|
||||
const char *label = kind == 3 ? (pairs ? "[Set Entries]" : "[Set Iterator]")
|
||||
: (pairs ? "[Map Entries]" : "[Map Iterator]");
|
||||
if (recurse > depth) {
|
||||
InspBuf out = {0};
|
||||
ib_cstr(&out, "[Object ");
|
||||
ib_cstr(&out, label);
|
||||
ib_char(&out, ']');
|
||||
return ib_take(&out);
|
||||
}
|
||||
size_t remaining = 0;
|
||||
if (map) {
|
||||
for (double i = scr_map_iter_skip(map, index); i < scr_map_iter_count(map); i++)
|
||||
if (scr_map_iter_live(map, i)) remaining++;
|
||||
}
|
||||
if (remaining == 0) {
|
||||
InspBuf out = {0};
|
||||
ib_cstr(&out, label);
|
||||
ib_cstr(&out, " { }");
|
||||
return ib_take(&out);
|
||||
}
|
||||
ScrStr *base = scr_str_new(label, strlen(label));
|
||||
scr_insp_begin(recurse + 1);
|
||||
scr_map_iter_enter(map);
|
||||
size_t shown = 0;
|
||||
for (double i = scr_map_iter_skip(map, index); i < scr_map_iter_count(map) && shown < 100; i++) {
|
||||
if (!scr_map_iter_live(map, i)) continue;
|
||||
shown++;
|
||||
ScrDyn *item;
|
||||
if (pairs) {
|
||||
item = scr_dyn_new_arr();
|
||||
scr_dyn_arr_push(item, scr_map_dyn_key(map, i));
|
||||
scr_dyn_arr_push(item, kind == 3 ? scr_map_dyn_key(map, i) : scr_map_dyn_value(map, i));
|
||||
} else {
|
||||
item = kind == 2 && selection == 0 ? scr_map_dyn_value(map, i) : scr_map_dyn_key(map, i);
|
||||
}
|
||||
ScrStr *text = scr_insp_dyn(item, recurse + 1, depth);
|
||||
scr_dyn_release(item);
|
||||
scr_insp_entry(text, false);
|
||||
scr_str_release(text);
|
||||
}
|
||||
scr_map_iter_exit(map);
|
||||
if (remaining > shown) {
|
||||
ScrStr *more = scr_insp_more_items((double)(remaining - shown));
|
||||
scr_insp_entry(more, false);
|
||||
scr_str_release(more);
|
||||
}
|
||||
ScrStr *b0 = scr_str_new("{", 1);
|
||||
ScrStr *b1 = scr_str_new("}", 1);
|
||||
ScrStr *out = scr_insp_end(base, b0, b1, recurse + 1, false, false);
|
||||
scr_str_release(base);
|
||||
scr_str_release(b0);
|
||||
scr_str_release(b1);
|
||||
return out;
|
||||
}
|
||||
|
||||
/* A compiled generator object: Node's empty `Object [<tag>] {}`, with the
|
||||
* tag the compiler set (scr_gen_set_tag). A generator standing in for an
|
||||
* object of another class (a tag starting with '!') throws the
|
||||
* "not supported yet" Error instead. */
|
||||
static bool scr_insp_is_generator(const ScrDyn *d) {
|
||||
const char *key = d->v.typed_ref.type_key;
|
||||
return !strncmp(key, "generator<", 10) || !strncmp(key, "async-generator<", 16);
|
||||
}
|
||||
|
||||
static ScrStr *scr_insp_generator(const ScrDyn *d) {
|
||||
const char *tag = scr_gen_tag(d->v.typed_ref.ptr);
|
||||
if (tag[0] == '!') {
|
||||
static const char message[] = "util.inspect of this iterator object is not supported yet";
|
||||
scr_throw_error_msg(SCR_ERR_ERROR, message, sizeof message - 1);
|
||||
return scr_str_new("", 0);
|
||||
}
|
||||
InspBuf out = {0};
|
||||
ib_cstr(&out, "Object [");
|
||||
ib_cstr(&out, tag);
|
||||
ib_cstr(&out, "] {}");
|
||||
return ib_take(&out);
|
||||
}
|
||||
|
||||
static ScrStr *scr_insp_dyn_collection(ScrDyn *d, double recurse, double depth, bool is_map) {
|
||||
ScrMap *map = d->v.handle.ptr;
|
||||
const char *label = is_map ? "Map" : "Set";
|
||||
@@ -1309,6 +1402,10 @@ ScrStr *scr_insp_dyn(ScrDyn *d, double recurse, double depth) {
|
||||
return scr_insp_weak_collection(d->v.handle.tag == SCR_DYNH_WEAK_MAP ? "WeakMap" : "WeakSet",
|
||||
recurse, depth);
|
||||
if (d->v.handle.tag == SCR_DYNH_DATE) return scr_insp_date(scr_dyn_native_date_value(d));
|
||||
if (d->v.handle.tag == SCR_DYNH_ITERATOR) {
|
||||
ScrStr *iterator = scr_insp_native_iterator(d, recurse, depth);
|
||||
if (iterator) return iterator;
|
||||
}
|
||||
const ScrDynHandleOps *ops = scr_dyn_handle_ops_of(d);
|
||||
if (ops->inspect_view) {
|
||||
/* Node's custom inspectors answer the bare class name beyond the
|
||||
@@ -1349,6 +1446,7 @@ ScrStr *scr_insp_dyn(ScrDyn *d, double recurse, double depth) {
|
||||
}
|
||||
|
||||
case SCR_DYN_TYPED_REF: {
|
||||
if (scr_insp_is_generator(d)) return scr_insp_generator(d);
|
||||
const char *cls = NULL;
|
||||
ScrDyn *view = scr_event_ref_hooks ? scr_event_ref_hooks->inspect_view(d, &cls) : NULL;
|
||||
if (view) {
|
||||
|
||||
@@ -4673,6 +4673,16 @@ bool scr_dyn_iterator_step_done(const ScrDyn *value) {
|
||||
return ((ScrNativeIterator *)value->v.handle.ptr)->step_done;
|
||||
}
|
||||
|
||||
void scr_dyn_native_iterator_state(const ScrDyn *value, unsigned *kind, unsigned *selection,
|
||||
ScrMap **collection, double *index) {
|
||||
const ScrNativeIterator *iterator = value->v.handle.ptr;
|
||||
*kind = iterator->kind;
|
||||
*selection = iterator->selection;
|
||||
*collection = (iterator->kind == 2 || iterator->kind == 3) && iterator->source
|
||||
? iterator->source->v.handle.ptr : NULL;
|
||||
*index = iterator->index;
|
||||
}
|
||||
|
||||
double scr_dyn_collection_iterator_index(const ScrDyn *value) {
|
||||
ScrNativeIterator *iterator = value->v.handle.ptr;
|
||||
ScrDyn *source = iterator->source;
|
||||
@@ -6217,6 +6227,20 @@ ScrStr *scr_dyn_object_tag(const ScrDyn *d) {
|
||||
}
|
||||
if (d->kind == SCR_DYN_TYPED_REF) {
|
||||
if (scr_dyn_ref_is_error(d)) return scr_str_new("[object Error]", 14);
|
||||
if (scr_dyn_generator(d)) {
|
||||
const char *gen_tag = scr_gen_tag(d->v.typed_ref.ptr);
|
||||
if (gen_tag[0] == '!') {
|
||||
static const char message[] = "Object.prototype.toString of this iterator is not supported yet";
|
||||
scr_throw_error_msg(SCR_ERR_ERROR, message, sizeof message - 1);
|
||||
return NULL;
|
||||
}
|
||||
ScrJsonBuf b;
|
||||
scr_jb_init(&b);
|
||||
scr_jb_puts(&b, "[object ");
|
||||
scr_jb_puts(&b, gen_tag);
|
||||
scr_jb_putc(&b, ']');
|
||||
return scr_jb_finish(&b);
|
||||
}
|
||||
ScrDyn *materialized = scr_dyn_typed_ref_materialize(d);
|
||||
ScrStr *out = scr_dyn_object_tag(materialized);
|
||||
scr_dyn_release(materialized);
|
||||
@@ -6249,6 +6273,13 @@ ScrStr *scr_dyn_object_tag(const ScrDyn *d) {
|
||||
scr_jb_putc(&b, ']');
|
||||
return scr_jb_finish(&b);
|
||||
}
|
||||
if (d->v.handle.tag == SCR_DYNH_ITERATOR && ((const ScrNativeIterator *)d->v.handle.ptr)->kind < 4) {
|
||||
static const char *const tags[] = {
|
||||
"[object Array Iterator]", "[object String Iterator]", "[object Map Iterator]", "[object Set Iterator]",
|
||||
};
|
||||
const char *it_tag = tags[((const ScrNativeIterator *)d->v.handle.ptr)->kind];
|
||||
return scr_str_new(it_tag, strlen(it_tag));
|
||||
}
|
||||
if (scr_dyn_handle_ops_of(d)->to_string_tag) {
|
||||
ScrJsonBuf b;
|
||||
scr_jb_init(&b);
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
// util.inspect and Object.prototype.toString of iterator objects: generators,
|
||||
// array, typed array and string iterators and iterator helpers print as
|
||||
// Object [<tag>] {}, and Map and Set iterators print the entries they have
|
||||
// left.
|
||||
import { inspect } from "node:util";
|
||||
function* g(): Generator<number> { yield 1; }
|
||||
const s = new Set([1, 2, 3]);
|
||||
const m = new Map<string, number>([["a", 1], ["b", 2]]);
|
||||
const si = s.values(); si.next();
|
||||
console.log(g(), [1, 2].values(), "ab"[Symbol.iterator](), new Uint8Array([1]).values());
|
||||
console.log(s.values(), s.entries(), m.keys(), m.values(), m.entries(), si);
|
||||
console.log([1].values().map((x) => x), Iterator.from([1]), g().filter(() => true));
|
||||
console.log(inspect({ a: { b: { c: s.values(), d: g() } } }));
|
||||
console.log(Object.prototype.toString.call(g()), Object.prototype.toString.call([1].values().map((x) => x)), Object.prototype.toString.call(m.keys()));
|
||||
const done = s.values(); Array.from(done);
|
||||
console.log(done, new Map<string, number>().entries(), g());
|
||||
const keyed = new Map<{ x: number }, number[]>([[{ x: 1 }, [1, 2]]]);
|
||||
console.log(keyed.entries(), keyed.keys());
|
||||
console.log(new Set(Array.from({ length: 120 }, (_, i) => i)).values());
|
||||
@@ -0,0 +1,17 @@
|
||||
// The JavaScript form: util.inspect and Object.prototype.toString of
|
||||
// iterator objects.
|
||||
import { inspect } from "node:util";
|
||||
function* g() { yield 1; }
|
||||
const s = new Set([1, 2, 3]);
|
||||
const m = new Map ([["a", 1], ["b", 2]]);
|
||||
const si = s.values(); si.next();
|
||||
console.log(g(), [1, 2].values(), "ab"[Symbol.iterator](), new Uint8Array([1]).values());
|
||||
console.log(s.values(), s.entries(), m.keys(), m.values(), m.entries(), si);
|
||||
console.log([1].values().map((x) => x), Iterator.from([1]), g().filter(() => true));
|
||||
console.log(inspect({ a: { b: { c: s.values(), d: g() } } }));
|
||||
console.log(Object.prototype.toString.call(g()), Object.prototype.toString.call([1].values().map((x) => x)), Object.prototype.toString.call(m.keys()));
|
||||
const done = s.values(); Array.from(done);
|
||||
console.log(done, new Map ().entries(), g());
|
||||
const keyed = new Map ([[{ x: 1 }, [1, 2]]]);
|
||||
console.log(keyed.entries(), keyed.keys());
|
||||
console.log(new Set(Array.from({ length: 120 }, (_, i) => i)).values());
|
||||
@@ -2242,6 +2242,8 @@
|
||||
"6072-iterator-sources-js.js": 5350,
|
||||
"6073-stored-collection-helpers.ts": 4000,
|
||||
"6074-stored-collection-helpers-js.js": 4100,
|
||||
"6075-print-iterators.ts": 3800,
|
||||
"6076-print-iterators-js.js": 3500,
|
||||
"6100-record-get-or-create.ts": 3771,
|
||||
"6101-record-get-or-create-unchecked/main.ts": 2107,
|
||||
"6103-typeof-builtin-bindings.cjs": 2202,
|
||||
@@ -5320,6 +5322,8 @@
|
||||
"6072-iterator-sources-js.js": 5350,
|
||||
"6073-stored-collection-helpers.ts": 4150,
|
||||
"6074-stored-collection-helpers-js.js": 3600,
|
||||
"6075-print-iterators.ts": 3450,
|
||||
"6076-print-iterators-js.js": 3300,
|
||||
"6100-record-get-or-create.ts": 3173,
|
||||
"6101-record-get-or-create-unchecked/main.ts": 3228,
|
||||
"6103-typeof-builtin-bindings.cjs": 1796,
|
||||
|
||||
Reference in New Issue
Block a user