mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-07 08:33:50 +08:00
Store tty.isatty and process.cwd as function values (#2494)
This commit is contained in:
@@ -381,6 +381,7 @@ export const LIB_FN_SYMS: Record<string, string> = {
|
||||
"process.stdio": "scr_process_stdio",
|
||||
"process.stderrWrite": "scr_process_stderr_write",
|
||||
"process.isTTY": "scr_process_is_tty",
|
||||
"process.isTTYValue": "scr_process_is_tty_value",
|
||||
"date.now": "scr_date_now",
|
||||
"date.newNow": "scr_date_now",
|
||||
"date.newMs": "scr_date_new_ms",
|
||||
|
||||
@@ -119,6 +119,7 @@ const INERT_LIB_CALLS = new Set([
|
||||
"process.platform",
|
||||
"process.execPath",
|
||||
"process.isTTY",
|
||||
"process.isTTYValue",
|
||||
"worker.data",
|
||||
"worker.isMainThread",
|
||||
"worker.parentPort",
|
||||
|
||||
@@ -740,6 +740,11 @@ export function lowerProcessProperty(
|
||||
"process.env as a value of this type (read one variable: process.env.NAME or process.env[name])",
|
||||
);
|
||||
}
|
||||
// process.cwd as a value: the process module's member (surfaces.ts).
|
||||
if (member === "cwd") {
|
||||
const value = lowerer.lowerBuiltinCallableValue({ module: "process", member }, loc);
|
||||
if (value) return value;
|
||||
}
|
||||
if (member === "exit" || member === "cwd" || member === "getuid" || member === "kill") {
|
||||
lowerer.unsupported("SC1090", expr, `process methods as values (call '${member}' directly)`);
|
||||
}
|
||||
|
||||
@@ -24,7 +24,14 @@ import {
|
||||
localPrivateBrandClass,
|
||||
privateNameClass,
|
||||
} from "../private-brands.js";
|
||||
import { type Lowerer, PoisonError, jsFuncNameOf, neverTaintedJsType, own } from "../lowerer.js";
|
||||
import {
|
||||
type Lowerer,
|
||||
PoisonError,
|
||||
builtinValueNameOf,
|
||||
jsFuncNameOf,
|
||||
neverTaintedJsType,
|
||||
own,
|
||||
} from "../lowerer.js";
|
||||
import {
|
||||
ARRAY_METHODS,
|
||||
MAP_METHODS,
|
||||
@@ -1203,13 +1210,15 @@ export function lowerPropertyAccessExpr(
|
||||
(id) => lowerer.unions.get(id),
|
||||
)
|
||||
) {
|
||||
const fnName = jsFuncNameOf(expr.expression);
|
||||
// A builtin function value whose length is not its adapter's
|
||||
// parameter count (`Math.max.length` is 2).
|
||||
const fnLength =
|
||||
probed.kind === "closure"
|
||||
? lowerer.builtinCallableValueLengths.get(probed.fnName)
|
||||
: undefined;
|
||||
// A function is named by its definition, through const aliases: a
|
||||
// builtin module member whose Node name differs from the member
|
||||
// (`process.cwd` is `wrappedCwd`) by the module table, anything else
|
||||
// by the property or function it reads. A builtin function value
|
||||
// keeps its length when that is not its adapter's parameter count
|
||||
// (`Math.max.length` is 2).
|
||||
const fnName =
|
||||
builtinValueNameOf(expr.expression, lowerer) ?? jsFuncNameOf(expr.expression, lowerer);
|
||||
const fnLength = lowerer.builtinFunctionIdentity(probed).fnLength;
|
||||
const boxed: IrExpr = {
|
||||
kind: "dynFrom",
|
||||
value: probed,
|
||||
|
||||
@@ -4771,6 +4771,16 @@ function stdlibGlobalTokenDestructure(
|
||||
) {
|
||||
// A data member reads its value once, exactly like the spelled
|
||||
// `process.<member>` read the destructuring performs.
|
||||
} else if (
|
||||
globalName === "process" &&
|
||||
propName === "cwd" &&
|
||||
(value = lowerer.lowerBuiltinCallableValue(
|
||||
{ module: "process", member: propName },
|
||||
locOf(el),
|
||||
)) !== null
|
||||
) {
|
||||
// process.cwd reads the directory when it is called, so the detached
|
||||
// function is the same one every other spelling reads.
|
||||
} else {
|
||||
lowerer.unsupported(
|
||||
"SC1031",
|
||||
|
||||
@@ -2105,6 +2105,67 @@ function anyPiecedFuncType(lowerer: Lowerer, node: ts.Node, t: ts.Type): IrType
|
||||
* their own name, anonymous function/arrow expressions their
|
||||
* NamedEvaluation home (a variable initializer or property assignment).
|
||||
* Null when nothing names the value (the box stays anonymous). */
|
||||
/** The name Node gives the builtin module function `node` reads, through
|
||||
* const aliases (`const get = process.cwd`), when the module table names its
|
||||
* value differently from its member (BuiltinModuleFn.valueName); else null. */
|
||||
export function builtinValueNameOf(node: ts.Node, lowerer: Lowerer): string | null {
|
||||
let n: ts.Node = node;
|
||||
const seen = new Set<ts.Symbol>();
|
||||
for (;;) {
|
||||
while (ts.isParenthesizedExpression(n)) n = n.expression;
|
||||
if (!ts.isIdentifier(n)) break;
|
||||
const imported = lowerer.builtinImportOf(n);
|
||||
if (imported)
|
||||
return builtinModuleFnOf(lowerer, imported.module, imported.member)?.valueName ?? null;
|
||||
const symbol = lowerer.resolveValueSymbol(n);
|
||||
const declaration = symbol?.valueDeclaration;
|
||||
if (!symbol || seen.has(symbol) || !declaration) return null;
|
||||
seen.add(symbol);
|
||||
// A member destructured from a module or the process global
|
||||
// (`const { cwd } = process`).
|
||||
if (ts.isBindingElement(declaration)) {
|
||||
const pattern = declaration.parent;
|
||||
const holder = pattern?.parent;
|
||||
const key = declaration.propertyName ?? declaration.name;
|
||||
if (
|
||||
declaration.dotDotDotToken ||
|
||||
!pattern ||
|
||||
!ts.isObjectBindingPattern(pattern) ||
|
||||
!holder ||
|
||||
!ts.isVariableDeclaration(holder) ||
|
||||
!holder.initializer ||
|
||||
(ts.getCombinedNodeFlags(holder) & ts.NodeFlags.Const) === 0 ||
|
||||
!ts.isIdentifier(key)
|
||||
)
|
||||
return null;
|
||||
return builtinMemberValueName(lowerer, holder.initializer, key.text);
|
||||
}
|
||||
if (
|
||||
!ts.isVariableDeclaration(declaration) ||
|
||||
!declaration.initializer ||
|
||||
(ts.getCombinedNodeFlags(declaration) & ts.NodeFlags.Const) === 0
|
||||
)
|
||||
return null;
|
||||
n = declaration.initializer;
|
||||
}
|
||||
if (!ts.isPropertyAccessExpression(n)) return null;
|
||||
return builtinMemberValueName(lowerer, n.expression, n.name.text);
|
||||
}
|
||||
|
||||
/** BuiltinModuleFn.valueName of `member` read from `receiver`, the process
|
||||
* global or a builtin module's namespace; null for anything else. */
|
||||
function builtinMemberValueName(
|
||||
lowerer: Lowerer,
|
||||
receiver: ts.Expression,
|
||||
member: string,
|
||||
): string | null {
|
||||
const module = lowerer.isStdlibGlobal(receiver, "process")
|
||||
? "process"
|
||||
: lowerer.builtinNamespaceModuleOf(receiver);
|
||||
if (module === null) return null;
|
||||
return builtinModuleFnOf(lowerer, module, member)?.valueName ?? null;
|
||||
}
|
||||
|
||||
export function jsFuncNameOf(node: ts.Node, lowerer?: Lowerer): string | null {
|
||||
let n: ts.Node = node;
|
||||
while (ts.isParenthesizedExpression(n)) n = n.expression;
|
||||
@@ -2132,14 +2193,18 @@ export function jsFuncNameOf(node: ts.Node, lowerer?: Lowerer): string | null {
|
||||
if (ts.isPropertyAccessExpression(n)) return n.name.text;
|
||||
if ((ts.isFunctionExpression(n) || ts.isFunctionDeclaration(n)) && n.name) return n.name.text;
|
||||
if (ts.isFunctionExpression(n) || ts.isArrowFunction(n)) {
|
||||
const p = n.parent;
|
||||
if (p && ts.isVariableDeclaration(p) && p.initializer === n && ts.isIdentifier(p.name)) {
|
||||
// An anonymous function takes the name of the binding or property it
|
||||
// initializes, through parentheses (`const f = (function () {})`).
|
||||
let outer: ts.Node = n;
|
||||
while (outer.parent && ts.isParenthesizedExpression(outer.parent)) outer = outer.parent;
|
||||
const p = outer.parent;
|
||||
if (p && ts.isVariableDeclaration(p) && p.initializer === outer && ts.isIdentifier(p.name)) {
|
||||
return p.name.text;
|
||||
}
|
||||
if (p && ts.isBindingElement(p) && p.initializer === n && ts.isIdentifier(p.name)) {
|
||||
if (p && ts.isBindingElement(p) && p.initializer === outer && ts.isIdentifier(p.name)) {
|
||||
return p.name.text;
|
||||
}
|
||||
if (p && ts.isPropertyAssignment(p) && p.initializer === n && ts.isIdentifier(p.name)) {
|
||||
if (p && ts.isPropertyAssignment(p) && p.initializer === outer && ts.isIdentifier(p.name)) {
|
||||
return p.name.text;
|
||||
}
|
||||
}
|
||||
@@ -12351,20 +12416,13 @@ export class Lowerer {
|
||||
liveReference(expr.type) ||
|
||||
(expr.type.kind === "union" &&
|
||||
(this.unions.get(expr.type.unionId)?.arms.some(liveReference) ?? false));
|
||||
// A builtin function's adapter boxes under the builtin's name, the
|
||||
// name Node's function carries (`[Function: randomUUID]`).
|
||||
const builtinName =
|
||||
expr.kind === "closure" ? this.builtinCallableValueNames.get(expr.fnName) : undefined;
|
||||
const builtinLength =
|
||||
expr.kind === "closure" ? this.builtinCallableValueLengths.get(expr.fnName) : undefined;
|
||||
return {
|
||||
kind: "dynFrom",
|
||||
value: expr,
|
||||
type: DYN,
|
||||
loc: expr.loc,
|
||||
...(liveRef ? { liveRef: true as const } : {}),
|
||||
...(builtinName !== undefined ? { fnName: builtinName } : {}),
|
||||
...(builtinLength !== undefined ? { fnLength: builtinLength } : {}),
|
||||
...this.builtinFunctionIdentity(expr),
|
||||
};
|
||||
}
|
||||
return expr;
|
||||
@@ -12512,7 +12570,13 @@ export class Lowerer {
|
||||
) {
|
||||
return {
|
||||
kind: "dynCheck",
|
||||
value: { kind: "dynFrom", value: expr, type: DYN, loc: expr.loc },
|
||||
value: {
|
||||
kind: "dynFrom",
|
||||
value: expr,
|
||||
type: DYN,
|
||||
loc: expr.loc,
|
||||
...this.builtinFunctionIdentity(expr),
|
||||
},
|
||||
type: expected,
|
||||
loc: expr.loc,
|
||||
};
|
||||
@@ -16959,7 +17023,10 @@ export class Lowerer {
|
||||
if (!fnName) {
|
||||
fnName = keyedHelperName(this, `%builtin.value.${fn.valueFn ?? fn.fn}`, key);
|
||||
this.builtinCallableValueFns.set(key, fnName);
|
||||
this.builtinCallableValueNames.set(fnName, display.slice(display.lastIndexOf(".") + 1));
|
||||
this.builtinCallableValueNames.set(
|
||||
fnName,
|
||||
fn.valueName ?? display.slice(display.lastIndexOf(".") + 1),
|
||||
);
|
||||
if (fn.valueLength !== undefined)
|
||||
this.builtinCallableValueLengths.set(fnName, fn.valueLength);
|
||||
const params = funcType.params.map((type, index) => ({
|
||||
@@ -17051,6 +17118,19 @@ export class Lowerer {
|
||||
return { kind: "closure", fnName, captures: [], type: funcType, loc };
|
||||
}
|
||||
|
||||
/** The name and length a builtin function's adapter boxes under: the ones
|
||||
* Node's function carries (`[Function: randomUUID]`). Empty for any other
|
||||
* value. */
|
||||
builtinFunctionIdentity(expr: IrExpr): { fnName?: string; fnLength?: number } {
|
||||
if (expr.kind !== "closure") return {};
|
||||
const name = this.builtinCallableValueNames.get(expr.fnName);
|
||||
const length = this.builtinCallableValueLengths.get(expr.fnName);
|
||||
return {
|
||||
...(name !== undefined ? { fnName: name } : {}),
|
||||
...(length !== undefined ? { fnLength: length } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
isBuiltinCallableValue(expr: IrExpr): boolean {
|
||||
return (
|
||||
expr.kind === "closure" &&
|
||||
|
||||
@@ -823,6 +823,9 @@ export interface BuiltinModuleFn {
|
||||
/** The function value's `length` when it is not the count of its
|
||||
* required value parameters (`Math.max`'s 2 over its rest pack). */
|
||||
valueLength?: number;
|
||||
/** The function value's `name` when it is not the member's
|
||||
* (`process.cwd` is Node's `wrappedCwd`). */
|
||||
valueName?: string;
|
||||
}
|
||||
|
||||
export type BuiltinValueParam =
|
||||
@@ -1894,6 +1897,7 @@ function buildBuiltinModuleFns(): BuiltinModuleFnTable {
|
||||
{ mode: "optional", type: DYN, defaultValue: undefined },
|
||||
],
|
||||
},
|
||||
// util.debug is util.debuglog, under its name.
|
||||
debug: {
|
||||
fn: "util.debuglog",
|
||||
params: [DYN, DYN],
|
||||
@@ -1902,6 +1906,7 @@ function buildBuiltinModuleFns(): BuiltinModuleFnTable {
|
||||
{ mode: "required", type: DYN },
|
||||
{ mode: "optional", type: DYN, defaultValue: undefined },
|
||||
],
|
||||
valueName: "debuglog",
|
||||
},
|
||||
},
|
||||
// The util/types spoke supplies the probe name to the shared brand check.
|
||||
@@ -1954,10 +1959,29 @@ function buildBuiltinModuleFns(): BuiltinModuleFnTable {
|
||||
transcode: { fn: "buffer.transcode", params: [DYN, DYN, DYN], result: BYTES_U8 },
|
||||
},
|
||||
tty: {
|
||||
isatty: { fn: "process.isTTY", params: [F64], result: BOOL },
|
||||
isatty: {
|
||||
fn: "process.isTTY",
|
||||
params: [F64],
|
||||
result: BOOL,
|
||||
// As a value (loggers store `tty.isatty`), the adapter takes any
|
||||
// argument: a value that is not an integral fd answers false, and is
|
||||
// never coerced.
|
||||
valueFn: "process.isTTYValue",
|
||||
valueParams: [{ mode: "optional", type: DYN, defaultValue: undefined }],
|
||||
valueLength: 1,
|
||||
},
|
||||
},
|
||||
process: {
|
||||
loadEnvFile: { fn: "process.loadEnvFile", params: [DYN], result: VOID },
|
||||
// Every spelling of process.cwd as a value is this one function, which
|
||||
// reads the directory when it is called.
|
||||
cwd: {
|
||||
fn: "process.cwd",
|
||||
params: [],
|
||||
result: STRING,
|
||||
valueParams: [],
|
||||
valueName: "wrappedCwd",
|
||||
},
|
||||
},
|
||||
// node:string_decoder's surface is the StringDecoder CLASS — new/write/
|
||||
// end are special-cased (lowerNew + lowerStringDecoderMethodCall); no
|
||||
@@ -2289,6 +2313,9 @@ export const BUILTIN_MODULE_FN_ALIASES: Record<
|
||||
randomInt: ["crypto.randomIntMax", "crypto.randomIntCb", "crypto.randomIntPromise"],
|
||||
randomUUID: ["crypto.randomUUIDOpts"],
|
||||
},
|
||||
tty: {
|
||||
isatty: ["process.isTTYValue"],
|
||||
},
|
||||
os: {
|
||||
// lowerOsUserInfoCall assembles the record from scalar libCalls.
|
||||
userInfo: ["os.userHomedir", "os.userShell"],
|
||||
@@ -2720,12 +2747,6 @@ function buildAmbientSurfaceFns(): readonly AmbientSurfaceRow[] {
|
||||
fns: ["process.release"],
|
||||
note: "the compatibility target's release metadata as one stable read-only object",
|
||||
},
|
||||
{
|
||||
id: "node-builtin.process.cwd",
|
||||
kind: "node-builtin",
|
||||
name: "process.cwd",
|
||||
fns: ["process.cwd"],
|
||||
},
|
||||
{
|
||||
id: "node-builtin.process.chdir",
|
||||
kind: "node-builtin",
|
||||
@@ -2860,7 +2881,7 @@ function buildAmbientSurfaceFns(): readonly AmbientSurfaceRow[] {
|
||||
id: "node-builtin.process.isTTY",
|
||||
kind: "node-builtin",
|
||||
name: "process.isTTY",
|
||||
fns: ["process.isTTY"],
|
||||
fns: ["process.isTTY", "process.isTTYValue"],
|
||||
note: "the isTTY read on process.stdin/stdout/stderr — one surface across the three streams",
|
||||
},
|
||||
{
|
||||
|
||||
@@ -5376,6 +5376,7 @@ function buildLibFnSignatures() {
|
||||
/** Validated isatty(3) probe. Stream property lowering maps false to
|
||||
* undefined; tty.isatty exposes the boolean result directly. */
|
||||
"process.isTTY": { argTypes: [F64], result: BOOL },
|
||||
"process.isTTYValue": { argTypes: [DYN], result: BOOL },
|
||||
// Like process.envGet: the result is the module's interned
|
||||
// `number | undefined` union — checked by arms in the libCall case.
|
||||
/** Terminal width over an fd literal (1/2 — process.stdout/stderr
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/function-name-const-alias.mjs"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/process-cwd-value.cjs"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/process-cwd-value.mjs"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/tty-isatty-value-typed.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/tty-isatty-value.mjs"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
@@ -5126,6 +5126,12 @@ bool scr_process_is_tty(double fd) {
|
||||
return isatty((int)fd) != 0;
|
||||
}
|
||||
|
||||
/* tty.isatty as a function value (borrowed argument): a value that is not a
|
||||
* number answers false and is never coerced, as Node's integer test does. */
|
||||
bool scr_process_is_tty_value(const ScrDyn *fd) {
|
||||
return fd != NULL && fd->kind == SCR_DYN_NUM && scr_process_is_tty(fd->v.num);
|
||||
}
|
||||
|
||||
/* Terminal geometry for process.stdout/stderr.columns/rows: ioctl(TIOCGWINSZ) on
|
||||
* the stream's fd, exactly Node's tty.WriteStream source of truth. A
|
||||
* non-TTY stream, or a terminal that refuses the ioctl, answers -1 and
|
||||
|
||||
@@ -386,6 +386,7 @@ double scr_atomics_notify_dyn(const ScrDyn *, double, double);
|
||||
* non-TTY streams expose undefined (SEMANTICS.md). stdin_destroy is a
|
||||
* documented no-op. */
|
||||
bool scr_process_is_tty(double fd);
|
||||
bool scr_process_is_tty_value(const ScrDyn *fd);
|
||||
void scr_process_stdin_destroy(void);
|
||||
void scr_process_stdin_set_raw_mode(bool raw);
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// The name of a function value read through a const binding: a builtin
|
||||
// keeps Node's name (including when destructured), an anonymous function or
|
||||
// arrow takes the binding's name through parentheses, and an alias of a
|
||||
// named function keeps the function's name.
|
||||
import * as path from "node:path";
|
||||
import * as processModule from "node:process";
|
||||
|
||||
const { cwd } = process;
|
||||
const { cwd: renamed } = processModule;
|
||||
const join = path.join;
|
||||
const parenthesized = (function () {});
|
||||
const arrow = (() => 0);
|
||||
const nested = ((function () {}));
|
||||
function named() {}
|
||||
const alias = named;
|
||||
const aliasOfAlias = alias;
|
||||
console.log(cwd.name, renamed.name, join.name);
|
||||
console.log(parenthesized.name, arrow.name, nested.name);
|
||||
console.log(alias.name, aliasOfAlias.name);
|
||||
console.log(cwd === process.cwd, typeof cwd());
|
||||
@@ -0,0 +1,7 @@
|
||||
// process.cwd stored as a value from CommonJS: the same function as the
|
||||
// process module's member, read when it is called.
|
||||
const processModule = require("node:process");
|
||||
const get = process.cwd;
|
||||
console.log(get === processModule.cwd, get.name, get() === process.cwd());
|
||||
const { cwd } = processModule;
|
||||
console.log(cwd === get, typeof cwd());
|
||||
@@ -0,0 +1,22 @@
|
||||
// process.cwd stored as a value: one identity across the global, named,
|
||||
// namespace and default forms, Node's name and length, and the directory
|
||||
// read when it is called.
|
||||
import { cwd } from "node:process";
|
||||
import * as processModule from "node:process";
|
||||
import processDefault from "node:process";
|
||||
import { mkdtempSync, realpathSync, rmSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
|
||||
const get = process.cwd;
|
||||
console.log(typeof get, get.name, get.length);
|
||||
console.log(get === cwd, get === processModule.cwd, get === processDefault.cwd);
|
||||
console.log(get() === process.cwd(), cwd() === process.cwd());
|
||||
|
||||
const start = get();
|
||||
const dir = realpathSync(mkdtempSync(join(tmpdir(), "scriptc-cwd-value-")));
|
||||
process.chdir(dir);
|
||||
console.log(get() === dir, cwd() === dir, [0].map(() => get())[0] === dir);
|
||||
process.chdir(start);
|
||||
console.log(get() === start);
|
||||
rmSync(dir, { recursive: true });
|
||||
@@ -0,0 +1,10 @@
|
||||
// A typed alias of tty.isatty works as a callback with its declared
|
||||
// signature.
|
||||
import * as tty from "node:tty";
|
||||
|
||||
const isTerminal: (fd: number) => boolean = tty.isatty;
|
||||
function describe(fds: number[], test: (fd: number) => boolean): string[] {
|
||||
return fds.map((fd) => `${fd}:${test(fd)}`);
|
||||
}
|
||||
console.log(describe([-1, 0.5, 2 ** 40], isTerminal).join(" "));
|
||||
console.log(isTerminal === tty.isatty, [-1].map(isTerminal));
|
||||
@@ -0,0 +1,21 @@
|
||||
// tty.isatty stored as a value keeps the direct predicate's answers, its
|
||||
// identity across import forms, and its name and length. Values that are
|
||||
// not integral file descriptors answer false without being coerced.
|
||||
import * as tty from "node:tty";
|
||||
import { isatty } from "node:tty";
|
||||
import ttyDefault from "node:tty";
|
||||
|
||||
const probe = tty.isatty;
|
||||
console.log(typeof probe, probe.name, probe.length);
|
||||
console.log(probe === isatty, probe === ttyDefault.isatty);
|
||||
console.log(probe(-1), probe(1.5), probe(2 ** 31), probe(Number.NaN));
|
||||
console.log([-1, 0.5].map(probe));
|
||||
// Called through an untyped callback, values that are not numbers answer
|
||||
// false without being coerced.
|
||||
function forward(predicate, value) {
|
||||
return predicate(value);
|
||||
}
|
||||
let reads = 0;
|
||||
const object = { valueOf() { reads++; return 1; } };
|
||||
console.log(forward(probe, undefined), forward(probe, "1"), forward(probe, 1n), forward(probe, null), forward(probe, object), reads);
|
||||
console.log(forward(isatty, -1), forward(tty.isatty, 2 ** 31));
|
||||
@@ -6,7 +6,7 @@
|
||||
import { mkdirSync, openSync, readFileSync, writeFileSync } from "node:fs";
|
||||
import { writeFile } from "node:fs/promises";
|
||||
const read = readFileSync; console.log(read);
|
||||
const cwd = process.cwd;
|
||||
const exit = process.exit;
|
||||
const p = process;
|
||||
const env = process.env;
|
||||
const flo = Math.floor; // fixed-arity Math functions have native value forms
|
||||
|
||||
@@ -3,13 +3,13 @@ stdlib.ts:8:40 - error SC1090: a builtin function alias as an escaping value (ca
|
||||
7 | import { writeFile } from "node:fs/promises";
|
||||
8 | const read = readFileSync; console.log(read);
|
||||
| ^~~~
|
||||
9 | const cwd = process.cwd;
|
||||
9 | const exit = process.exit;
|
||||
|
||||
stdlib.ts:9:13 - error SC1090: process methods as values (call 'cwd' directly) are not supported yet
|
||||
stdlib.ts:9:14 - error SC1090: process methods as values (call 'exit' directly) are not supported yet
|
||||
|
||||
8 | const read = readFileSync; console.log(read);
|
||||
9 | const cwd = process.cwd;
|
||||
| ^~~~~~~~~~~
|
||||
9 | const exit = process.exit;
|
||||
| ^~~~~~~~~~~~
|
||||
10 | const p = process;
|
||||
|
||||
stdlib.ts:13:15 - error SC1090: string methods as values (call 'toUpperCase' directly) are not supported yet
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{ "plain": 2000, "san": 3000 }
|
||||
@@ -0,0 +1 @@
|
||||
{ "plain": 2000, "san": 3000 }
|
||||
@@ -0,0 +1 @@
|
||||
{ "plain": 2000, "san": 3000 }
|
||||
@@ -0,0 +1 @@
|
||||
{ "plain": 2000, "san": 3000 }
|
||||
@@ -0,0 +1 @@
|
||||
{ "plain": 2000, "san": 3000 }
|
||||
Reference in New Issue
Block a user