mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-05 08:13:06 +08:00
Merge main
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { lowerReadlineCursorCall } from "./builtins/readline.js";
|
||||
import { lowerMonitorEventLoopDelayCall } from "./builtins/performance.js";
|
||||
import {
|
||||
lowerEnvironmentData,
|
||||
lowerReceiveMessageOnPort,
|
||||
@@ -69,6 +70,9 @@ export function lowerBuiltinCall(
|
||||
return lowerAddAbortListener(lowerer, expr, loc);
|
||||
}
|
||||
if (bi.module === "events" && bi.member === "once") return lowerEventsOnce(lowerer, expr, loc);
|
||||
if (bi.module === "perf_hooks" && bi.member === "monitorEventLoopDelay") {
|
||||
return lowerMonitorEventLoopDelayCall(lowerer, expr);
|
||||
}
|
||||
if (bi.module === "events") {
|
||||
const helper = lowerEventsEmitterHelper(lowerer, expr, bi.member, loc);
|
||||
if (helper) return helper;
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
} from "../../../ir/ir.js";
|
||||
import { PERF_ENTRY_FIELDS, PERF_ENTRY_MARKER, perfEntryJsonShape } from "../../type-mapper.js";
|
||||
import { builtinImportOf } from "./module-bindings.js";
|
||||
import { lowerOptionalArgument } from "../optional-arguments.js";
|
||||
import { checkedIterableSpread } from "../checked-iterable-spread.js";
|
||||
|
||||
/** True for any spelling of node:perf_hooks' `performance` object — the
|
||||
@@ -663,21 +664,11 @@ export function lowerHistogramMethodCall(
|
||||
);
|
||||
}
|
||||
|
||||
/** `monitorEventLoopDelay(options?)` from node:perf_hooks (a named import
|
||||
* or a namespace member): a new sampling histogram handle. The resolution
|
||||
* must be a literal-free number expression or an object literal option. */
|
||||
export function lowerMonitorEventLoopDelayCall(
|
||||
lowerer: Lowerer,
|
||||
expr: ts.CallExpression,
|
||||
): IrExpr | null {
|
||||
const callee = expr.expression;
|
||||
const bi = ts.isIdentifier(callee)
|
||||
? builtinImportOf(lowerer, callee)
|
||||
: ts.isPropertyAccessExpression(callee) && !callee.questionDotToken
|
||||
? lowerer.builtinMemberOf(callee)
|
||||
: null;
|
||||
if (bi === null || bi.module !== "perf_hooks" || bi.member !== "monitorEventLoopDelay")
|
||||
return null;
|
||||
/** `monitorEventLoopDelay(options?)` from node:perf_hooks, in any import
|
||||
* spelling (`lowerBuiltinCall`): a new sampling histogram handle. The
|
||||
* options are an inline object literal; the runtime validates the
|
||||
* resolution as Node does (an integer of at least 1). */
|
||||
export function lowerMonitorEventLoopDelayCall(lowerer: Lowerer, expr: ts.CallExpression): IrExpr {
|
||||
const loc = locOf(expr);
|
||||
const args = expr.arguments.nodes;
|
||||
let resolution: IrExpr = numLit(10, loc);
|
||||
@@ -698,8 +689,9 @@ export function lowerMonitorEventLoopDelayCall(
|
||||
);
|
||||
}
|
||||
for (const property of options.properties.nodes) {
|
||||
const shorthand = ts.isShorthandPropertyAssignment(property);
|
||||
if (
|
||||
!ts.isPropertyAssignment(property) ||
|
||||
(!shorthand && !ts.isPropertyAssignment(property)) ||
|
||||
!ts.isIdentifier(property.name) ||
|
||||
property.name.text !== "resolution"
|
||||
) {
|
||||
@@ -709,7 +701,13 @@ export function lowerMonitorEventLoopDelayCall(
|
||||
"resolution is the one option",
|
||||
);
|
||||
}
|
||||
resolution = lowerer.lowerExprExpecting(property.initializer, F64);
|
||||
// An undefined resolution takes the default, as Node's destructuring.
|
||||
resolution = lowerOptionalArgument(
|
||||
lowerer,
|
||||
shorthand ? property.name : property.initializer,
|
||||
F64,
|
||||
numLit(10, loc),
|
||||
);
|
||||
}
|
||||
}
|
||||
return { kind: "libCall", fn: "perf.eldNew", args: [resolution], type: F64, loc };
|
||||
|
||||
@@ -322,7 +322,9 @@ export function funcCoerceAdapter(
|
||||
),
|
||||
];
|
||||
} else if (checkedRet) {
|
||||
body = [{ kind: "return", value: { kind: "dynCheck", value: call, type: toT.ret, loc }, loc }];
|
||||
body = [
|
||||
{ kind: "return", value: { kind: "dynCheck", value: call, type: toT.ret, loc }, loc },
|
||||
];
|
||||
} else {
|
||||
const result = lowerer.coerceToExpected(call, toT.ret);
|
||||
if (!typeEquals(result.type, toT.ret))
|
||||
|
||||
@@ -144,7 +144,6 @@ import {
|
||||
fencePerfEntryKeyWalk,
|
||||
isPerfEntryType,
|
||||
lowerHistogramMethodCall,
|
||||
lowerMonitorEventLoopDelayCall,
|
||||
lowerPerfHooksCall,
|
||||
perfEntryTag,
|
||||
} from "./builtins/performance.js";
|
||||
@@ -243,16 +242,6 @@ const ZLIB_STREAM_METHODS: ReadonlySet<string> = new Set(["flush", "params", "cl
|
||||
|
||||
export function lowerCall(lowerer: Lowerer, expr: ts.CallExpression): IrExpr {
|
||||
const loc = locOf(expr);
|
||||
// node:perf_hooks monitorEventLoopDelay (named import or namespace member).
|
||||
if (
|
||||
(ts.isIdentifier(expr.expression) && expr.expression.text === "monitorEventLoopDelay") ||
|
||||
(ts.isPropertyAccessExpression(expr.expression) &&
|
||||
expr.expression.name.text === "monitorEventLoopDelay")
|
||||
) {
|
||||
const histogram = lowerMonitorEventLoopDelayCall(lowerer, expr);
|
||||
if (histogram) return histogram;
|
||||
}
|
||||
|
||||
// Object's checker return type is `any`. Claim known object inputs
|
||||
// before generic checked-dynamic dispatch tries to load the constructor.
|
||||
if (
|
||||
|
||||
@@ -13835,6 +13835,24 @@ const OBJECT_PROTOTYPE_KEYS: ReadonlySet<string> = new Set([
|
||||
* idiom over multiple shapes), index-signature keys, class instances,
|
||||
* and dyn/unknown stay fenced. Keys are literal strings — a computed key
|
||||
* over a shape would need the runtime key table. */
|
||||
/** Keys built-in errors carry outside their class fields: Node's system
|
||||
* error properties, and AggregateError's, SuppressedError's and
|
||||
* AssertionError's own members. */
|
||||
const BUILTIN_ERROR_SLOT_KEYS: ReadonlySet<string> = new Set([
|
||||
"errno",
|
||||
"syscall",
|
||||
"path",
|
||||
"dest",
|
||||
"info",
|
||||
"errors",
|
||||
"error",
|
||||
"suppressed",
|
||||
"actual",
|
||||
"expected",
|
||||
"operator",
|
||||
"generatedMessage",
|
||||
]);
|
||||
|
||||
function lowerInExpression(lowerer: Lowerer, expr: ts.BinaryExpression, loc: SrcLoc): IrExpr {
|
||||
if (
|
||||
lowerer.isStdlibGlobal(expr.right, "process") &&
|
||||
@@ -14018,9 +14036,10 @@ function lowerInExpression(lowerer: Lowerer, expr: ts.BinaryExpression, loc: Src
|
||||
// predicate's `err instanceof Error && "code" in err` shape): `code`
|
||||
// answers from the runtime error's code slot (stamped by fs/system/
|
||||
// event/spawn errors, absent on plain constructions — exactly Node's
|
||||
// own-property answer), and the always-declared members fold true.
|
||||
// Everything else fences: the runtime object carries no other dynamic
|
||||
// properties to ask (`stack` would be a lie — Node has one, we don't).
|
||||
// own-property answer), and the always-present members (message, name,
|
||||
// stack, Object.prototype's keys, members the static class declares)
|
||||
// fold true. Other keys test the declaring subclasses and the error's
|
||||
// own runtime properties; keys of built-in errors' extra slots fence.
|
||||
if (recv.type.kind === "object") {
|
||||
// %DOMException first: `'cause' in e` answers the options form's
|
||||
// own-property record (the runtime slot), and code/name/message are
|
||||
@@ -14076,6 +14095,9 @@ function lowerInExpression(lowerer: Lowerer, expr: ts.BinaryExpression, loc: Src
|
||||
loc,
|
||||
};
|
||||
if (key === "message" || key === "name") return always();
|
||||
// Every Error has its own `stack`, and Error.prototype's `toString`
|
||||
// and `constructor` are among Object.prototype's keys.
|
||||
if (key === "stack" || OBJECT_PROTOTYPE_KEYS.has(key)) return always();
|
||||
// A member the static class declares is always present.
|
||||
const declares = (c: ClassInfo): boolean =>
|
||||
c.fields.has(key) ||
|
||||
@@ -14084,12 +14106,12 @@ function lowerInExpression(lowerer: Lowerer, expr: ts.BinaryExpression, loc: Src
|
||||
lowerer.findMethodOn(c, `set:${key}`) !== null;
|
||||
const staticInfo = lowerer.classes.get(recv.type.className);
|
||||
if (staticInfo && declares(staticInfo)) return always();
|
||||
// Any other key: a descendant class that declares it (instanceof),
|
||||
// Any other key: a descendant class that declares it (instanceof), or
|
||||
// an own property set at runtime (a throw site's extra fields,
|
||||
// Object.assign, writes through a dynamic view), or an
|
||||
// Object.prototype member. `stack` stays fenced: Node has one, a
|
||||
// compiled error has none.
|
||||
if (key !== "stack") {
|
||||
// Object.assign, writes through a dynamic view). Keys built-in errors
|
||||
// keep outside their class fields depend on the runtime error and
|
||||
// stay fenced.
|
||||
if (!BUILTIN_ERROR_SLOT_KEYS.has(key)) {
|
||||
const stable = recv.kind === "varRef" || recv.kind === "caughtNarrow";
|
||||
const slot = stable ? null : lowerer.declareHiddenLocal("%inRecv", recv.type);
|
||||
const ref = (): IrExpr => (slot ? varRef(slot.id, recv.type, loc) : recv);
|
||||
|
||||
@@ -1350,7 +1350,8 @@ function lowerLabeled(lowerer: Lowerer, stmt: ts.LabeledStatement): IrStmt | IrS
|
||||
}
|
||||
|
||||
export function lowerStmt(lowerer: Lowerer, stmt: ts.Statement): IrStmt | IrStmt[] | null {
|
||||
if (ownPrototypeResetStatement(lowerer, stmt)) return { kind: "block", body: [], loc: locOf(stmt) };
|
||||
if (ownPrototypeResetStatement(lowerer, stmt))
|
||||
return { kind: "block", body: [], loc: locOf(stmt) };
|
||||
if (ts.isClassDeclaration(stmt)) {
|
||||
if (!stmt.name) lowerer.unsupported("SC1090", stmt, "anonymous local class declarations");
|
||||
const info = lowerer.lowerClassExpressionInfo(stmt);
|
||||
@@ -10924,7 +10925,13 @@ function resetTarget(lowerer: Lowerer, expr: ts.Expression): ts.Expression | nul
|
||||
function enclosingConstructor(node: ts.Node): ts.ConstructorDeclaration | null {
|
||||
for (let cur = node.parent; cur !== undefined; cur = cur.parent) {
|
||||
if (ts.isConstructorDeclaration(cur)) return cur;
|
||||
if (ts.isFunctionLike(cur) || (ts.isClassDeclaration(cur) || ts.isClassExpression(cur)) || ts.isSourceFile(cur)) return null;
|
||||
if (
|
||||
ts.isFunctionLike(cur) ||
|
||||
ts.isClassDeclaration(cur) ||
|
||||
ts.isClassExpression(cur) ||
|
||||
ts.isSourceFile(cur)
|
||||
)
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -10947,7 +10954,8 @@ function ownPrototypeRef(
|
||||
)
|
||||
return true;
|
||||
const cls = ctor.parent;
|
||||
if (!ts.isIdentifier(base) || !(ts.isClassDeclaration(cls) || ts.isClassExpression(cls))) return false;
|
||||
if (!ts.isIdentifier(base) || !(ts.isClassDeclaration(cls) || ts.isClassExpression(cls)))
|
||||
return false;
|
||||
const symbol = lowerer.resolveValueSymbol(base);
|
||||
const info = symbol ? lowerer.classBySymbol.get(symbol) : undefined;
|
||||
if (!info || lowerer.checker.valueDeclarationOf(symbol!) !== cls) return false;
|
||||
|
||||
@@ -5146,11 +5146,15 @@ function mapGenericIndexedAccess(type: ts.Type, ctx: TypeMapperCtx): IrType | nu
|
||||
const obj = type.getObjectType();
|
||||
if (obj === undefined || !(obj.flags & ts.TypeFlags.TypeParameter)) return null;
|
||||
const bound = resolveTypeParam(obj);
|
||||
const idx = type.getIndexType();
|
||||
if (idx === undefined) return null;
|
||||
// `T[keyof T]` over a T bound to an unknown-valued record view
|
||||
// (`Record<string, unknown>` maps to the checked-dynamic value, see
|
||||
// mapRecordType): every member reads as unknown.
|
||||
if (bound?.kind === "dyn" && idx.isIndexType() && idx.getTarget() === obj) return DYN;
|
||||
if (!bound || bound.kind !== "record") return null;
|
||||
const shape = shapes.get(bound.shapeId);
|
||||
if (!shape || shape.tuple) return null;
|
||||
const idx = type.getIndexType();
|
||||
if (idx === undefined) return null;
|
||||
let covered: IrType[] | null = null;
|
||||
if (idx.isStringLiteralType()) {
|
||||
const f = shape.fields.find((x) => x.name === idx.value);
|
||||
@@ -5206,6 +5210,17 @@ function mapBoundKeyofNeverConditional(type: ts.Type, ctx: TypeMapperCtx): IrTyp
|
||||
const target = check.getTarget();
|
||||
if (!target || !(target.flags & ts.TypeFlags.TypeParameter)) return null;
|
||||
const bound = resolveTypeParam(target);
|
||||
// A T bound to the checked-dynamic value (`Record<string, unknown>`, an
|
||||
// unknown-valued record view) has no static key set to decide the
|
||||
// branch, but both branches of the lib's `keyof T extends never ?
|
||||
// unknown : T[keyof T]` read as unknown there: one answer either way.
|
||||
if (bound?.kind === "dyn") {
|
||||
const whenEmpty = type.getTrueType();
|
||||
const otherwise = type.getFalseType();
|
||||
const a = whenEmpty ? mapType(whenEmpty, ctx) : null;
|
||||
const b = otherwise ? mapType(otherwise, ctx) : null;
|
||||
return a && b && typeEquals(a, b) ? a : null;
|
||||
}
|
||||
if (bound?.kind !== "record") return null;
|
||||
const shape = shapes.get(bound.shapeId);
|
||||
if (!shape || shape.tuple) return null;
|
||||
|
||||
@@ -363,6 +363,8 @@ export const MAY_THROW_LIB_FNS: ReadonlySet<IrLibFn> = new Set([
|
||||
"perf.entries",
|
||||
"perf.clearMarks",
|
||||
"perf.clearMeasures",
|
||||
// monitorEventLoopDelay's resolution: Node's ERR_OUT_OF_RANGE.
|
||||
"perf.eldNew",
|
||||
// Non-TTY stdin: Node's stdin has no setRawMode member, so the call is
|
||||
// the exact catchable TypeError.
|
||||
"process.stdinSetRawMode",
|
||||
|
||||
@@ -364,7 +364,7 @@ export const LIB_FN_SIGS = defineLibFnSignatures({
|
||||
/** perf_hooks event-loop metrics (scr_perf_loop.c): loopStart is the
|
||||
* loop's start in performance.now() ms (0 before it), idleTime the idle
|
||||
* ms since. eldNew(resolution ms) creates a monitorEventLoopDelay
|
||||
* histogram handle; enable/disable answer whether the state changed;
|
||||
* histogram handle (MAY THROW Node's resolution RangeError); enable/disable answer whether the state changed;
|
||||
* eldPercentile expects Node's validated range; eldStat(which) reads
|
||||
* 0 min, 1 max, 2 mean, 3 stddev, 4 count, 5 exceeds. */
|
||||
"perf.loopStart": { argTypes: [], result: F64 },
|
||||
|
||||
@@ -506,11 +506,11 @@ function scanRuntimeFeatures(mod: IrModule, stopAt?: keyof RuntimeFeatures): Run
|
||||
}
|
||||
if (fn.startsWith("dgram.") || fn.startsWith("dns.")) features.dgram = true;
|
||||
if (fn.startsWith("tlsca.")) features.tlsCa = true;
|
||||
if (fn.startsWith("perf.loop") || fn === "perf.idleTime" || fn.startsWith("perf.eld"))
|
||||
features.perfLoop = true;
|
||||
// scr_cipher.c over the mbedTLS archive; DEP0182 reports through the
|
||||
// process-warning unit, and unknown output encodings render through
|
||||
// the inspect unit.
|
||||
if (fn.startsWith("perf.loop") || fn === "perf.idleTime" || fn.startsWith("perf.eld"))
|
||||
features.perfLoop = true;
|
||||
if (fn === "crypto.cipherNew") {
|
||||
features.cipher = true;
|
||||
features.dynAsync = true;
|
||||
|
||||
@@ -53,6 +53,11 @@ const LIB_MODE_REFUSED_PREFIXES: readonly [string, string][] = [
|
||||
["crypto.cipherNew", "crypto.createCipheriv and createDecipheriv"],
|
||||
["fs.watch", "fs.watch"],
|
||||
["watcher.", "fs.watch"],
|
||||
// The event-loop metrics sample the loop and its timers (scr_perf_loop.c),
|
||||
// which library links exclude.
|
||||
["perf.loopStart", "performance.eventLoopUtilization"],
|
||||
["perf.idleTime", "performance.eventLoopUtilization"],
|
||||
["perf.eld", "perf_hooks.monitorEventLoopDelay"],
|
||||
["test.", "the node:test surface"],
|
||||
["readable.", "the node:stream surface"],
|
||||
["writable.", "the node:stream surface"],
|
||||
@@ -157,6 +162,7 @@ export function moduleLibAsyncSurface(mod: IrModule): { surface: string; loc: Sr
|
||||
[features.nodeTest, "the node:test surface"],
|
||||
[features.dynAsync, "the checked-dynamic async surface"],
|
||||
[features.dc, "the diagnostics_channel surface"],
|
||||
[features.perfLoop, "the perf_hooks event-loop metrics"],
|
||||
];
|
||||
for (const [on, surface] of coarse) {
|
||||
if (on) return { surface, loc: entryLoc };
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/6410-wrapper-untyped-results.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/6411-prototype-reset-constructors/issues.js",
|
||||
"<repo>/tests/corpus/6411-prototype-reset-constructors/main.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/6468-generic-unknown-record-entries.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
@@ -21,7 +21,9 @@
|
||||
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
@@ -212,7 +214,27 @@ static void scr_eld_cleanup(void) {
|
||||
scr_elds_len = 0;
|
||||
}
|
||||
|
||||
/* Node's validateInteger(resolution, 'options.resolution', 1): a catchable
|
||||
* RangeError (ERR_OUT_OF_RANGE) for a non-integer or a value out of range,
|
||||
* with the value as util.inspect renders it ("-0" included). */
|
||||
static bool scr_eld_resolution_ok(double resolution) {
|
||||
bool integer = isfinite(resolution) && trunc(resolution) == resolution;
|
||||
if (integer && resolution >= 1 && resolution <= 9007199254740991.0) return true;
|
||||
char received[48];
|
||||
if (resolution == 0 && signbit(resolution))
|
||||
memcpy(received, "-0", 3);
|
||||
else
|
||||
scr_num_received(resolution, received);
|
||||
char msg[192];
|
||||
int len = snprintf(msg, sizeof msg,
|
||||
"The value of \"options.resolution\" is out of range. It must be %s. Received %s",
|
||||
integer ? ">= 1 && <= 9007199254740991" : "an integer", received);
|
||||
scr_throw_error_msg_code(SCR_ERR_RANGE, msg, (size_t)len, "ERR_OUT_OF_RANGE");
|
||||
return false;
|
||||
}
|
||||
|
||||
double scr_eld_new(double resolution) {
|
||||
if (!scr_eld_resolution_ok(resolution)) return 0;
|
||||
if (scr_elds_len == 0 && scr_elds == NULL) scr_atexit(scr_eld_cleanup);
|
||||
ScrEld *h = calloc(1, sizeof(ScrEld));
|
||||
ScrEld **grown = realloc(scr_elds, (scr_elds_len + 1) * sizeof(ScrEld *));
|
||||
@@ -223,7 +245,7 @@ double scr_eld_new(double resolution) {
|
||||
}
|
||||
scr_elds = grown;
|
||||
h->min_value = INT64_MAX;
|
||||
h->resolution = resolution >= 1 ? resolution : 10;
|
||||
h->resolution = resolution;
|
||||
h->tick = scr_closure_new((void *)scr_eld_tick, 0);
|
||||
scr_elds[scr_elds_len++] = h;
|
||||
return (double)scr_elds_len;
|
||||
|
||||
@@ -23,3 +23,9 @@ function retryable(err: Error): boolean {
|
||||
}
|
||||
console.log(retryable(new Error("x")), retryable(new HttpError("a", 500)), retryable(new Timeout("t", 504)));
|
||||
console.log(describe(new Timeout("t", 504)));
|
||||
// Keys every Error has: its own stack, and Error.prototype's and
|
||||
// Object.prototype's members.
|
||||
function inherent(err: Error): boolean[] {
|
||||
return ["stack" in err, "toString" in err, "constructor" in err, "hasOwnProperty" in err];
|
||||
}
|
||||
console.log(inherent(new Error("s")), inherent(new HttpError("h", 400)), inherent(new TypeError("t")));
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Event-loop monitoring under @types/node: monitorEventLoopDelay's histogram,
|
||||
// eventLoopUtilization deltas, and an unref'd interval typed NodeJS.Timeout.
|
||||
import { monitorEventLoopDelay, performance } from "node:perf_hooks";
|
||||
import * as perfHooks from "node:perf_hooks";
|
||||
import { monitorEventLoopDelay as eld } from "node:perf_hooks";
|
||||
|
||||
const histogram = monitorEventLoopDelay({ resolution: 10 });
|
||||
console.log("enable", histogram.enable(), histogram.enable());
|
||||
@@ -47,3 +49,22 @@ const interval = setInterval(() => {
|
||||
}, 50);
|
||||
interval.unref();
|
||||
console.log("hasRef", interval.hasRef());
|
||||
|
||||
// Other import spellings, and Node's resolution validation.
|
||||
const aliased = eld();
|
||||
const namespaced = perfHooks.monitorEventLoopDelay({ resolution: 20 });
|
||||
console.log("spellings", aliased.enable(), namespaced.enable(), aliased.disable(), namespaced.disable());
|
||||
for (const resolution of [0, -0, -1, 1.5, NaN, Infinity, 1e16, 1]) {
|
||||
try {
|
||||
eld({ resolution });
|
||||
console.log("created", resolution);
|
||||
} catch (e) {
|
||||
console.log((e as Error).name, (e as { code?: string }).code, (e as Error).message);
|
||||
}
|
||||
}
|
||||
// An undefined resolution takes Node's default.
|
||||
function withResolution(resolution?: number): boolean {
|
||||
const h = monitorEventLoopDelay({ resolution });
|
||||
return h.enable() && h.disable();
|
||||
}
|
||||
console.log("defaults", withResolution(), withResolution(20), eld({ resolution: undefined }).enable());
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
// Inside a generic body, `Object.entries(tags)` over `T extends
|
||||
// Record<string, unknown>` reads `[string, T[keyof T]]` elements. An
|
||||
// instance whose T is the unknown-valued record view reads every member
|
||||
// as unknown; a record instance reads the union of its member types.
|
||||
function upper(text: string): string {
|
||||
return text.toUpperCase();
|
||||
}
|
||||
|
||||
function scrub(text: string): string {
|
||||
return text.replace(/secret/g, "***");
|
||||
}
|
||||
|
||||
function describe<T extends Record<string, unknown>>(tags: T): string[] {
|
||||
return Object.entries(tags).map(
|
||||
([key, value]) => `${key}=${typeof value === "string" ? upper(value) : String(value)}`,
|
||||
);
|
||||
}
|
||||
|
||||
function redact<T extends Record<string, unknown>>(tags: T): T {
|
||||
return Object.fromEntries(
|
||||
Object.entries(tags).map(([key, value]) => [key, typeof value === "string" ? scrub(value) : value]),
|
||||
) as T;
|
||||
}
|
||||
|
||||
function flatten(value: unknown): Record<string, unknown> {
|
||||
return typeof value === "object" && value !== null ? (value as Record<string, unknown>) : { value };
|
||||
}
|
||||
|
||||
console.log(describe(flatten({ b: 1, a: "x", c: true })));
|
||||
console.log(describe(flatten(5)));
|
||||
console.log(describe({ name: "n", size: 3 }));
|
||||
const cleaned = redact(flatten({ token: "secret", count: 2 }));
|
||||
console.log(cleaned, Object.keys(cleaned));
|
||||
@@ -0,0 +1 @@
|
||||
{"plain":4713}
|
||||
@@ -0,0 +1 @@
|
||||
{"plain":3445}
|
||||
@@ -0,0 +1 @@
|
||||
{"plain":5309}
|
||||
@@ -0,0 +1 @@
|
||||
{"plain":3632}
|
||||
@@ -0,0 +1 @@
|
||||
{"plain":4787}
|
||||
@@ -0,0 +1 @@
|
||||
{"plain":4127}
|
||||
@@ -0,0 +1 @@
|
||||
{"plain":4693}
|
||||
@@ -0,0 +1 @@
|
||||
{"plain":3803}
|
||||
@@ -0,0 +1 @@
|
||||
{"plain":3820}
|
||||
@@ -0,0 +1 @@
|
||||
{"plain":3726}
|
||||
@@ -0,0 +1 @@
|
||||
{"plain":3837}
|
||||
@@ -0,0 +1 @@
|
||||
{"plain":4711}
|
||||
@@ -73,6 +73,21 @@ describe("async_free detection over the IR", () => {
|
||||
);
|
||||
});
|
||||
|
||||
test("the event-loop metrics refuse", async () => {
|
||||
expect(
|
||||
await surfaceOf(
|
||||
"eld",
|
||||
`import { monitorEventLoopDelay } from "node:perf_hooks";\nconst h = monitorEventLoopDelay();\nconsole.log(h.enable());\n`,
|
||||
),
|
||||
).toContain("monitorEventLoopDelay");
|
||||
expect(
|
||||
await surfaceOf(
|
||||
"elu",
|
||||
`import { performance } from "node:perf_hooks";\nconsole.log(performance.eventLoopUtilization().idle >= 0);\n`,
|
||||
),
|
||||
).toContain("eventLoopUtilization");
|
||||
});
|
||||
|
||||
test("child_process refuses even in its synchronous spelling", async () => {
|
||||
expect(
|
||||
await surfaceOf(
|
||||
|
||||
@@ -569,10 +569,13 @@ describe("library profile fences", () => {
|
||||
expect(processIds).toContain("node-builtin.process.isTTY");
|
||||
expect(r.profile.fences[1]!.surfaces.every((s) => s.detector !== undefined)).toBe(true);
|
||||
// The user-timing timeline reads the same clock (mark() and measure()
|
||||
// default to performance.now()), so it joins the perf_hooks family.
|
||||
// default to performance.now()), and so do the event-loop metrics, so
|
||||
// they join the perf_hooks family.
|
||||
expect(r.profile.fences[2]!.surfaces.map((s) => s.id)).toEqual([
|
||||
"node-builtin.perf_hooks.monitorEventLoopDelay",
|
||||
"node-builtin.perf_hooks.performance.clearMarks",
|
||||
"node-builtin.perf_hooks.performance.clearMeasures",
|
||||
"node-builtin.perf_hooks.performance.eventLoopUtilization",
|
||||
"node-builtin.perf_hooks.performance.getEntries",
|
||||
"node-builtin.perf_hooks.performance.mark",
|
||||
"node-builtin.perf_hooks.performance.measure",
|
||||
|
||||
Reference in New Issue
Block a user