mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-30 07:14:39 +08:00
Read an Error subclass's own code through NodeJS.ErrnoException (#1471)
This commit is contained in:
@@ -1,7 +1,16 @@
|
||||
import * as ts from "../../ts7/adapter.js";
|
||||
import { type Lowerer } from "../lowerer.js";
|
||||
import { locOf } from "../../program.js";
|
||||
import { BOOL, CAUGHT, DYN, F64, type IrExpr, type IrType, STRING } from "../../../ir/ir.js";
|
||||
import {
|
||||
BOOL,
|
||||
CAUGHT,
|
||||
DYN,
|
||||
DYN_CLASS_PROPERTIES,
|
||||
F64,
|
||||
type IrExpr,
|
||||
type IrType,
|
||||
STRING,
|
||||
} from "../../../ir/ir.js";
|
||||
|
||||
const ASSERTION_ERROR_PROPERTIES: ReadonlySet<string> = new Set([
|
||||
"actual",
|
||||
@@ -126,7 +135,13 @@ export function lowerErrorCodeProperty(
|
||||
declared,
|
||||
);
|
||||
}
|
||||
const systemField = ["errno", "syscall", "path", "dest"].includes(expr.name.text);
|
||||
// A program class under the receiver's class can carry its own `code`
|
||||
// (a declared field or a property added through an untyped reference),
|
||||
// which Node reads instead of the errno code: read through the error's
|
||||
// dynamic view, which answers both.
|
||||
const systemField =
|
||||
["errno", "syscall", "path", "dest"].includes(expr.name.text) ||
|
||||
(expr.name.text === "code" && programClassMayOwnCode(lowerer, recvT.className));
|
||||
if (
|
||||
!systemField &&
|
||||
expr.name.text !== "code" &&
|
||||
@@ -162,7 +177,9 @@ export function lowerErrorCodeProperty(
|
||||
type: DYN,
|
||||
loc,
|
||||
},
|
||||
lowerer.withUndefinedArm(expr.name.text === "errno" ? F64 : STRING),
|
||||
expr.name.text === "code"
|
||||
? lowerer.envValueType()
|
||||
: lowerer.withUndefinedArm(expr.name.text === "errno" ? F64 : STRING),
|
||||
);
|
||||
}
|
||||
return {
|
||||
@@ -184,6 +201,19 @@ export function lowerErrorCodeProperty(
|
||||
};
|
||||
}
|
||||
|
||||
/** Whether a class declared in the program, `className` or one of its
|
||||
* subclasses, has its own `code` field or a dynamic property bag. The
|
||||
* runtime error classes keep `code` in the errno slot. */
|
||||
function programClassMayOwnCode(lowerer: Lowerer, className: string): boolean {
|
||||
for (const [name, info] of lowerer.classes) {
|
||||
if (name.startsWith("%")) continue;
|
||||
if (name !== className && !lowerer.isSubclassOf(name, className)) continue;
|
||||
if (info.def.fields.some((f) => f.name === "code" || f.name === DYN_CLASS_PROPERTIES))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Whether values of `type` are errors: class instances whose class is
|
||||
* %Error or extends it. A subclass of a class that does not extend Error
|
||||
* cannot be an error either, so the answer depends on the static class
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/6348-errno-code-own-field.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// `(err as NodeJS.ErrnoException).code` reads an Error subclass's own
|
||||
// `code` field, as Node reads the own property, and still reads the errno
|
||||
// code of runtime errors and undefined for plain errors.
|
||||
import { readFileSync } from "node:fs";
|
||||
|
||||
class AppError extends Error {
|
||||
code: string;
|
||||
constructor(message: string, code = "E_APP") {
|
||||
super(message);
|
||||
this.name = "AppError";
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
class DbError extends AppError {
|
||||
constructor() {
|
||||
super("db", "E_DB");
|
||||
}
|
||||
}
|
||||
class Later extends Error {
|
||||
code?: string;
|
||||
}
|
||||
|
||||
function codeOf(err: unknown): string {
|
||||
if (err instanceof Error) return (err as NodeJS.ErrnoException).code ?? "(none)";
|
||||
return "(not an error)";
|
||||
}
|
||||
|
||||
console.log(codeOf(new AppError("direct")));
|
||||
console.log(codeOf(new DbError()));
|
||||
const wrapped = new TypeError("wrapped", { cause: new AppError("cause", "E_CAUSE") });
|
||||
console.log(codeOf(wrapped.cause), codeOf(wrapped));
|
||||
try {
|
||||
throw new DbError();
|
||||
} catch (err) {
|
||||
console.log(codeOf(err));
|
||||
}
|
||||
const later = new Later("later");
|
||||
console.log(codeOf(later));
|
||||
later.code = "E_LATER";
|
||||
console.log(codeOf(later));
|
||||
try {
|
||||
readFileSync("/nonexistent/scriptc-corpus-6348");
|
||||
} catch (err) {
|
||||
console.log(codeOf(err));
|
||||
}
|
||||
console.log(codeOf(new Error("plain")), codeOf(new RangeError("range")), codeOf("text"));
|
||||
@@ -0,0 +1 @@
|
||||
{ "plain": 1200, "san": 1500 }
|
||||
Reference in New Issue
Block a user