mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-21 05:50:55 +08:00
DataView setters, fresh-ArrayBuffer erasure, Object.is, and the en-US Intl slice
- The DataView setter family lands end-to-end; fresh ArrayBuffers erase into zero-filled typed-array views; resizable/shared surfaces fence by name - Object.is compiles with the spec's SameValue across every kind, including union operands - Composed en-US Intl.NumberFormat and toLocaleString print byte-exact through the Ryu digit core's shortest-round-trip rounding, 30k-double fuzz-verified; the rest of Intl fences by name with reasons - DurationFormat deliberately fences: Node throws V8-internal Temporal errors with embedded source paths on plain inputs # Conflicts: # packages/compiler/test/ts7/baselines/order-parity.json
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
* emitter's frames (see the discipline comment in emitter core). */
|
||||
import type { CEmitter, Temp } from "./emitter.js";
|
||||
import { arrayOf, BOOL, BYTES_U8, bytesOf, canMarshalFuncIntoIsland, CHILDSTREAM_T, DYN, F64, IrExpr, IrRecordShape, IrType, isRefCounted, isUnitType, MAY_THROW_LIB_FNS, RUNTIME_ERROR_CLASSES, STRING, typeEquals } from "../../ir/nodes.js";
|
||||
import { boxAccess, BYTES_NUM_KIND_C, BYTES_NUM_VAR_C, bytesElemKindC, cDecl, cFnPtrCast, cNumberLiteral, cStringLiteral, cType, DV_GET_KIND_C, elemAccess, mapKeyAccess, mapKeyKindC, mapValKindC, retainCallC, vAdapters } from "./emit-types.js";
|
||||
import { boxAccess, BYTES_NUM_KIND_C, BYTES_NUM_VAR_C, bytesElemKindC, cDecl, cFnPtrCast, cNumberLiteral, cStringLiteral, cType, DV_GET_KIND_C, DV_SET_KIND_C, elemAccess, mapKeyAccess, mapKeyKindC, mapValKindC, retainCallC, vAdapters } from "./emit-types.js";
|
||||
import { mangleClassNew, mangleClassRetain, mangleClassStruct, mangleField, mangleFnClosure, mangleFunction, mangleGlobal, mangleLocal, mangleRecordNew, mangleRecordStruct, mangleVtStruct } from "../mangle.js";
|
||||
import { OVERFLOW_MEMBER } from "./emit-shapes.js";
|
||||
import { dynDestrCheckHelper, dynIterNHelper, dynKeyGetHelper } from "./emit-walkers.js";
|
||||
@@ -1137,6 +1137,24 @@ export function emitExpr(E: CEmitter, e: IrExpr): Temp {
|
||||
E.emitPendingCheck();
|
||||
return t;
|
||||
}
|
||||
case "dvSetUint8":
|
||||
case "dvSetInt8":
|
||||
case "dvSetUint16":
|
||||
case "dvSetInt16":
|
||||
case "dvSetUint32":
|
||||
case "dvSetInt32":
|
||||
case "dvSetFloat32":
|
||||
case "dvSetFloat64": {
|
||||
// DataView setters: [offset, value, littleEndian?] — void;
|
||||
// throw the getters' constant RangeError on a bad offset.
|
||||
const kind = DV_SET_KIND_C[method];
|
||||
E.line(
|
||||
`scr_dataview_set(${r.name}, ${args[0]!.name}, ${args[1]!.name}, ${kind}, ` +
|
||||
`${args[2]?.name ?? "false"});${E.srcComment(e.loc)}`,
|
||||
);
|
||||
E.emitPendingCheck();
|
||||
return { name: "", type: e.type };
|
||||
}
|
||||
default: {
|
||||
const _exhaustive: never = method;
|
||||
void _exhaustive;
|
||||
@@ -1927,11 +1945,13 @@ export function emitExpr(E: CEmitter, e: IrExpr): Temp {
|
||||
return E.newTemp(e.type, e.negated ? `!(${test})` : test);
|
||||
}
|
||||
case "unionEq": {
|
||||
// Strict equality of the ARM values via the per-union helper (tag
|
||||
// compare + per-arm payload compare). Both boxes are borrowed.
|
||||
// Strict equality (or Object.is's SameValue — the f64 arm's
|
||||
// compare is the one difference) of the ARM values via the
|
||||
// per-union helper (tag compare + per-arm payload compare). Both
|
||||
// boxes are borrowed.
|
||||
const l = E.emitExpr(e.left);
|
||||
const r = E.emitExpr(e.right);
|
||||
const call = `${E.unionEqHelper(e.unionId)}(${l.name}, ${r.name})`;
|
||||
const call = `${E.unionEqHelper(e.unionId, e.sameValue)}(${l.name}, ${r.name})`;
|
||||
return E.newTemp(e.type, e.negated ? `!${call}` : call);
|
||||
}
|
||||
case "caughtTest": {
|
||||
@@ -2480,6 +2500,13 @@ export function emitExpr(E: CEmitter, e: IrExpr): Temp {
|
||||
return finish(`scr_num_to_exponential(${arg(0)})`);
|
||||
case "num.toFixed0":
|
||||
return finish(`scr_num_to_fixed0(${arg(0)})`);
|
||||
// Object.is over two numbers — SameValue on doubles. No throw.
|
||||
case "num.sameValue":
|
||||
return finish(`scr_num_same_value(${arg(0)}, ${arg(1)})`);
|
||||
// Intl.NumberFormat("en-US").format / toLocaleString("en-US")
|
||||
// with default options (scr_lib.c). +1 string; no throw.
|
||||
case "intl.numFormatEnUs":
|
||||
return finish(`scr_intl_num_format_en_us(${arg(0)})`);
|
||||
// The URL surface (scr_url.c): construction and the two
|
||||
// fileURLToPath receiver forms throw catchable TypeErrors
|
||||
// (may-throw seed set); the getters are pure reads. Receivers
|
||||
|
||||
@@ -585,6 +585,19 @@ export const DV_GET_KIND_C: Record<string, string> = {
|
||||
dvGetBigInt64Number: "SCR_DV_BIGI64",
|
||||
};
|
||||
|
||||
/** The runtime's ScrDataViewGet tag per dvSet* bytesIntrinsic method (the
|
||||
* setters reuse the getter kinds; no BIG setters exist). */
|
||||
export const DV_SET_KIND_C: Record<string, string> = {
|
||||
dvSetUint8: "SCR_DV_U8",
|
||||
dvSetInt8: "SCR_DV_I8",
|
||||
dvSetUint16: "SCR_DV_U16",
|
||||
dvSetInt16: "SCR_DV_I16",
|
||||
dvSetUint32: "SCR_DV_U32",
|
||||
dvSetInt32: "SCR_DV_I32",
|
||||
dvSetFloat32: "SCR_DV_F32",
|
||||
dvSetFloat64: "SCR_DV_F64",
|
||||
};
|
||||
|
||||
/** Runtime accessor suffix for an element type: arrays store f64 and bool
|
||||
* unboxed and everything refcounted as a pointer (`_ref`). */
|
||||
export function elemAccess(elem: IrType): "f64" | "bool" | "ref" {
|
||||
|
||||
@@ -120,22 +120,26 @@ import { OVERFLOW_MEMBER } from "./emit-shapes.js";
|
||||
return name;
|
||||
}
|
||||
|
||||
/** The per-union strict-equality helper (interned per unionId):
|
||||
/** The per-union strict-equality helper (interned per unionId, with a
|
||||
* separate SameValue variant per unionId when Object.is reaches it):
|
||||
* different tags are never equal; equal tags compare the arm values —
|
||||
* unit arms equal, f64 with C == (NaN !== NaN, +0 === -0), strings by
|
||||
* bytes, bools by value, ref arms by POINTER identity (JS object
|
||||
* equality). Borrows both operands. */
|
||||
export function unionEqHelper(E: CEmitter, unionId: string): string {
|
||||
const existing = E.unionEqFns.get(unionId);
|
||||
* unit arms equal, f64 with C == (NaN !== NaN, +0 === -0) or SameValue
|
||||
* (NaN equals NaN, +0 differs from -0) under the sameValue flag,
|
||||
* strings by bytes, bools by value, ref arms by POINTER identity (JS
|
||||
* object equality). Borrows both operands. */
|
||||
export function unionEqHelper(E: CEmitter, unionId: string, sameValue: boolean): string {
|
||||
const key = sameValue ? `sv:${unionId}` : unionId;
|
||||
const existing = E.unionEqFns.get(key);
|
||||
if (existing) return existing;
|
||||
const def = E.unionsById.get(unionId);
|
||||
if (!def) throw new Error(`emitter bug: equality of unknown union ${unionId}`);
|
||||
const name = `sc_ue_${E.unionEqFns.size}`;
|
||||
E.unionEqFns.set(unionId, name);
|
||||
E.unionEqFns.set(key, name);
|
||||
const what = sameValue ? "SameValue" : "strict equality";
|
||||
const sig = `static bool ${name}(ScrUnion *a, ScrUnion *b)`;
|
||||
E.walkerProtos.push(`${sig}; /* strict equality ${unionId} */`);
|
||||
E.walkerProtos.push(`${sig}; /* ${what} ${unionId} */`);
|
||||
const d: string[] = [
|
||||
`${sig} { /* strict equality ${unionId} */`,
|
||||
`${sig} { /* ${what} ${unionId} */`,
|
||||
` if (a->tag != b->tag) return false;`,
|
||||
` switch (a->tag) {`,
|
||||
];
|
||||
@@ -146,7 +150,11 @@ import { OVERFLOW_MEMBER } from "./emit-shapes.js";
|
||||
d.push(` case ${i}: return true;`);
|
||||
break;
|
||||
case "f64":
|
||||
d.push(` case ${i}: return scr_union_get_f64(a) == scr_union_get_f64(b);`);
|
||||
d.push(
|
||||
sameValue
|
||||
? ` case ${i}: return scr_num_same_value(scr_union_get_f64(a), scr_union_get_f64(b));`
|
||||
: ` case ${i}: return scr_union_get_f64(a) == scr_union_get_f64(b);`,
|
||||
);
|
||||
break;
|
||||
case "bool":
|
||||
d.push(` case ${i}: return scr_union_get_bool(a) == scr_union_get_bool(b);`);
|
||||
|
||||
@@ -1076,8 +1076,8 @@ export class CEmitter {
|
||||
return unionTruthyHelper(this, unionId);
|
||||
}
|
||||
|
||||
unionEqHelper(unionId: string): string {
|
||||
return unionEqHelper(this, unionId);
|
||||
unionEqHelper(unionId: string, sameValue: boolean): string {
|
||||
return unionEqHelper(this, unionId, sameValue);
|
||||
}
|
||||
|
||||
unionToStrHelper(unionId: string): string {
|
||||
|
||||
@@ -185,6 +185,8 @@ const LIB_FN_SYMS: Record<string, string> = {
|
||||
"dyn.errInstanceof": "scr_dyn_err_instanceof",
|
||||
"num.toExponential": "scr_num_to_exponential",
|
||||
"num.toFixed0": "scr_num_to_fixed0",
|
||||
"num.sameValue": "scr_num_same_value",
|
||||
"intl.numFormatEnUs": "scr_intl_num_format_en_us",
|
||||
"number.isFinite": "scr_num_is_finite",
|
||||
"number.isNaN": "scr_num_is_nan",
|
||||
"number.isInteger": "scr_num_is_integer",
|
||||
@@ -772,6 +774,19 @@ const DV_GET_KIND: Record<string, number> = {
|
||||
dvGetBigInt64Number: 9,
|
||||
};
|
||||
|
||||
/** ScrDataViewGet per dvSet* method (the setters reuse the getter kinds;
|
||||
* no BIG setters exist). */
|
||||
const DV_SET_KIND: Record<string, number> = {
|
||||
dvSetUint8: 0,
|
||||
dvSetInt8: 1,
|
||||
dvSetUint16: 2,
|
||||
dvSetInt16: 3,
|
||||
dvSetUint32: 4,
|
||||
dvSetInt32: 5,
|
||||
dvSetFloat32: 6,
|
||||
dvSetFloat64: 7,
|
||||
};
|
||||
|
||||
class LlEmitter {
|
||||
/** Interned string literals: UTF-8 text → { symbol, byte length } —
|
||||
* first-use order, the C emitter's determinism discipline. */
|
||||
@@ -4363,7 +4378,14 @@ class LlEmitter {
|
||||
const t = B.tmp();
|
||||
B.line(`${a} = call double @scr_union_get_f64(ptr ${l.name})`);
|
||||
B.line(`${b} = call double @scr_union_get_f64(ptr ${r.name})`);
|
||||
B.line(`${t} = fcmp oeq double ${a}, ${b}`);
|
||||
if (e.sameValue) {
|
||||
// Object.is's f64 compare: NaN equals NaN, +0 differs
|
||||
// from -0 — the runtime SameValue.
|
||||
this.declare(`declare zeroext i1 @scr_num_same_value(double, double)`);
|
||||
B.line(`${t} = call zeroext i1 @scr_num_same_value(double ${a}, double ${b})`);
|
||||
} else {
|
||||
B.line(`${t} = fcmp oeq double ${a}, ${b}`);
|
||||
}
|
||||
B.line(`store i1 ${t}, ptr ${slot}`);
|
||||
break;
|
||||
}
|
||||
@@ -8889,6 +8911,24 @@ class LlEmitter {
|
||||
false,
|
||||
true,
|
||||
);
|
||||
case "dvSetUint8":
|
||||
case "dvSetInt8":
|
||||
case "dvSetUint16":
|
||||
case "dvSetInt16":
|
||||
case "dvSetUint32":
|
||||
case "dvSetInt32":
|
||||
case "dvSetFloat32":
|
||||
case "dvSetFloat64":
|
||||
// DataView setters: [offset, value, littleEndian?] — void; throw
|
||||
// the getters' constant RangeError on a bad offset.
|
||||
return call(
|
||||
"scr_dataview_set",
|
||||
"void (ptr, double, double, i32, i1 zeroext)",
|
||||
`ptr ${r.name}, double ${args[0]!.name}, double ${args[1]!.name}, ` +
|
||||
`i32 ${DV_SET_KIND[method]}, i1 ${args[2]?.name ?? "false"}`,
|
||||
false,
|
||||
true,
|
||||
);
|
||||
default: {
|
||||
const _exhaustive: never = method;
|
||||
void _exhaustive;
|
||||
|
||||
@@ -10,12 +10,12 @@ import { isJsSourceFile, locOf } from "../program.js";
|
||||
import { isGenericCallableMemberType, typeKey } from "../types.js";
|
||||
import { PoisonError, dynFallbackType, dynUndefinedExpr, importCallHandleType, jsFuncNameOf, newFnCtx } from "./lowerer.js";
|
||||
import { enforceLibBoundary } from "./lib-boundary.js";
|
||||
import { builtinFenceHintOf, builtinModuleFnOf } from "./surfaces.js";
|
||||
import { NARROW_FIRST, builtinFenceHintOf, builtinModuleFnOf } from "./surfaces.js";
|
||||
import { requiresDynamicDiag } from "../../diagnostics/diagnostic.js";
|
||||
import { mixinFnShapeOf } from "./lower-mixins.js";
|
||||
import { bufEncoding, dynStringReceiver, lowerArrayFromCall, lowerDynArrayFilterCall, lowerDynArrayFlatMapCall, lowerGroupByStaticCall, lowerIteratorHelperCall, lowerObjectAssignIndexShape, lowerObjectFromEntriesCall, lowerObjectIterOverIndexShape, lowerRegexMethodCall, lowerStringMethodCall, lowerTupleReadMethodCall } from "./lower-containers.js";
|
||||
import { lowerChildStreamMethodCall, lowerDirentMethodCall, lowerPerfHooksCall, lowerProcStreamMethodCall, lowerReflectApplyCall, lowerWatcherMethodCall } from "./lower-builtins.js";
|
||||
import { lowerPromiseAllTupleCall, lowerPromiseRejectCall, probeLower, templateRawTextOf } from "./lower-exprs.js";
|
||||
import { droppableStatic, lowerPromiseAllTupleCall, lowerPromiseRejectCall, probeLower, templateRawTextOf } from "./lower-exprs.js";
|
||||
import { httpClientFnBindingOf, isStreamUndefCallExpr, lowerHttpClientFnCall } from "./lower-server.js";
|
||||
import { EMITTER_API_MEMBERS, exactInstanceClassOf, findGenericMethodOn, lowerClassGenericMethodCall, lowerStaticMethodCall, type ClassInfo } from "./lower-classes.js";
|
||||
import { emitterRooted, lowerEmitterMethodCall } from "./lower-emitter.js";
|
||||
@@ -3589,6 +3589,9 @@ export function lowerCall(L: Lowerer, expr: ts.CallExpression): IrExpr {
|
||||
lowerSymbolStaticCall(L, expr, expr.expression) ??
|
||||
lowerSymbolMethodCall(L, expr, expr.expression) ??
|
||||
lowerRegExpStaticCall(L, expr, expr.expression) ??
|
||||
// The composed en-US Intl.NumberFormat form — before the member
|
||||
// fences (the receiver's Intl.NumberFormat type has no mapping).
|
||||
lowerIntlNumberFormatCall(L, expr, expr.expression) ??
|
||||
lowerGroupByStaticCall(L, expr, expr.expression) ??
|
||||
// Iterator-helper chains rooted at arr.values() — before the
|
||||
// array method paths (the terminal names collide with array
|
||||
@@ -4286,6 +4289,43 @@ const inliningPredicates = new Set<ts.Symbol>();
|
||||
const fn = name === "toExponential" ? "num.toExponential" : "num.toFixed0";
|
||||
return { kind: "libCall", fn, args: [operand], type: STRING, loc };
|
||||
}
|
||||
// Number.prototype.toLocaleString("en-US") — the spec makes it
|
||||
// NumberFormat(locale).format(this), so the en-US embedded formatter
|
||||
// answers exactly. The unlowered forms fence by NAME: no locale (the
|
||||
// host environment's default, which a compiled binary cannot carry),
|
||||
// other locales (ICU data the binary does not embed), options bags.
|
||||
if (name === "toLocaleString" && recvKind === "f64") {
|
||||
if (call.arguments.length === 0) {
|
||||
L.noLowering(
|
||||
"Number.prototype.toLocaleString without a locale",
|
||||
call,
|
||||
"the default locale is the host environment's, which a compiled binary cannot carry — " +
|
||||
'pass it explicitly: x.toLocaleString("en-US")',
|
||||
);
|
||||
}
|
||||
if (call.arguments.length > 1) {
|
||||
L.noLowering(
|
||||
"Number.prototype.toLocaleString with an options bag",
|
||||
call,
|
||||
"the embedded data covers DEFAULT options only (decimal notation, up to 3 fraction " +
|
||||
'digits, grouping) — x.toLocaleString("en-US")',
|
||||
);
|
||||
}
|
||||
const locNode = call.arguments[0]!;
|
||||
if (ts.isSpreadElement(locNode) || !ts.isStringLiteralLike(locNode) || locNode.text !== "en-US") {
|
||||
L.noLowering(
|
||||
!ts.isSpreadElement(locNode) && ts.isStringLiteralLike(locNode)
|
||||
? `Number.prototype.toLocaleString at locale "${locNode.text}"`
|
||||
: "Number.prototype.toLocaleString with a non-literal locale",
|
||||
locNode,
|
||||
'"en-US" (Node\'s default-build locale) is the one locale whose data the runtime embeds — ' +
|
||||
"everything else is ICU data the binary does not carry",
|
||||
);
|
||||
}
|
||||
const operand = L.lowerExpr(recv);
|
||||
if (operand.type.kind !== "f64") return null;
|
||||
return { kind: "libCall", fn: "intl.numFormatEnUs", args: [operand], type: STRING, loc };
|
||||
}
|
||||
if (name === "charAt" && recvKind === "string" && call.arguments.length === 1 &&
|
||||
!ts.isSpreadElement(call.arguments[0]!)) {
|
||||
const receiver = L.lowerExpr(recv);
|
||||
@@ -6147,11 +6187,217 @@ export function lowerPromiseMethodCall(L: Lowerer, call: ts.CallExpression,
|
||||
return { kind: "libCall", fn: "regexp.escape", args: [arg], type: STRING, loc: locOf(call) };
|
||||
}
|
||||
|
||||
/** The composed en-US Intl.NumberFormat form: `new Intl.NumberFormat(
|
||||
* "en-US").format(x)` (and the callable spelling without `new` — the
|
||||
* spec makes them the same formatter). Only the COMPOSED form lowers —
|
||||
* formatter values have no representation — and only the one locale
|
||||
* whose data the runtime embeds, with default options: decimal
|
||||
* notation, 0–3 fraction digits rounded half-up on the shortest
|
||||
* round-tripping decimal (ICU's rounding input — format(1.0005) is
|
||||
* "1.001" though toFixed(3) answers "1.000"), "," grouping. The
|
||||
* unlowered forms fence by NAME (no locale — the host environment's
|
||||
* default, which a compiled binary cannot carry; other locales — ICU
|
||||
* data the binary does not embed; options bags; non-number arguments).
|
||||
* Null when the callee isn't a NumberFormat-construction .format. */
|
||||
function lowerIntlNumberFormatCall(L: Lowerer, call: ts.CallExpression,
|
||||
access: ts.PropertyAccessExpression,): IrExpr | null {
|
||||
if (call.questionDotToken || access.questionDotToken) return null;
|
||||
if (access.name.text !== "format") return null;
|
||||
let recv: ts.Expression = access.expression;
|
||||
while (ts.isParenthesizedExpression(recv)) recv = recv.expression;
|
||||
let ctorArgs: readonly ts.Expression[];
|
||||
if (ts.isNewExpression(recv) || (ts.isCallExpression(recv) && !recv.questionDotToken)) {
|
||||
const ctor = recv.expression;
|
||||
if (
|
||||
!ts.isPropertyAccessExpression(ctor) || ctor.questionDotToken ||
|
||||
ctor.name.text !== "NumberFormat" || !L.isStdlibGlobal(ctor.expression, "Intl")
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
ctorArgs = recv.arguments ?? [];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
const loc = locOf(call);
|
||||
if (ctorArgs.length === 0) {
|
||||
L.noLowering(
|
||||
"Intl.NumberFormat without a locale",
|
||||
recv,
|
||||
"the default locale is the host environment's, which a compiled binary cannot carry — " +
|
||||
'pass it explicitly: new Intl.NumberFormat("en-US").format(x)',
|
||||
);
|
||||
}
|
||||
if (ctorArgs.length > 1) {
|
||||
L.noLowering(
|
||||
"Intl.NumberFormat with an options bag",
|
||||
ctorArgs[1]!,
|
||||
"the embedded data covers DEFAULT options only (decimal notation, up to 3 fraction digits, " +
|
||||
'grouping) — new Intl.NumberFormat("en-US").format(x)',
|
||||
);
|
||||
}
|
||||
const locArg = ctorArgs[0]!;
|
||||
if (ts.isSpreadElement(locArg) || !ts.isStringLiteralLike(locArg) || locArg.text !== "en-US") {
|
||||
L.noLowering(
|
||||
!ts.isSpreadElement(locArg) && ts.isStringLiteralLike(locArg)
|
||||
? `Intl.NumberFormat at locale "${locArg.text}"`
|
||||
: "Intl.NumberFormat with a non-literal locale",
|
||||
locArg,
|
||||
'"en-US" (Node\'s default-build locale) is the one locale whose data the runtime embeds — ' +
|
||||
"everything else is ICU data the binary does not carry",
|
||||
);
|
||||
}
|
||||
if (call.arguments.length !== 1 || ts.isSpreadElement(call.arguments[0]!)) {
|
||||
L.noLowering(`Intl.NumberFormat("en-US").format with ${call.arguments.length} arguments`, call);
|
||||
}
|
||||
const argNode = call.arguments[0]!;
|
||||
if (L.mapTypeOf(L.typeOf(argNode))?.kind !== "f64") {
|
||||
L.noLowering(
|
||||
`Intl.NumberFormat("en-US").format over a '${L.checker.typeToString(L.typeOf(argNode))}'`,
|
||||
argNode,
|
||||
"a number argument is the lowered form (bigint and numeric-string inputs have no representation)",
|
||||
);
|
||||
}
|
||||
const arg = L.lowerExprExpecting(argNode, F64);
|
||||
if (arg.type.kind !== "f64") L.badType(argNode, L.typeOf(argNode));
|
||||
return { kind: "libCall", fn: "intl.numFormatEnUs", args: [arg], type: STRING, loc };
|
||||
}
|
||||
|
||||
/** Object.is over statically disjoint kinds: the constant false, with
|
||||
* both operands still evaluated for their effects (droppable statics
|
||||
* fold away — JS evaluates arguments, but nothing observes a pure one). */
|
||||
function objectIsDisjointFalse(left: IrExpr, right: IrExpr, loc: SrcLoc): IrExpr {
|
||||
const stmts: IrStmt[] = [];
|
||||
for (const e of [left, right]) {
|
||||
if (!droppableStatic(e)) stmts.push({ kind: "exprStmt", expr: e, loc });
|
||||
}
|
||||
const answer: IrExpr = { kind: "boolLit", value: false, type: BOOL, loc };
|
||||
if (stmts.length === 0) return answer;
|
||||
return { kind: "seqExpr", stmts, result: answer, type: BOOL, loc };
|
||||
}
|
||||
|
||||
function lowerObjectStaticCall(L: Lowerer, call: ts.CallExpression,
|
||||
access: ts.PropertyAccessExpression,): IrExpr | null {
|
||||
if (call.questionDotToken || access.questionDotToken) return null;
|
||||
if (!L.isStdlibGlobal(access.expression, "Object")) return null;
|
||||
const member = access.name.text;
|
||||
// Object.is — the spec's SameValue over the static kinds. Number
|
||||
// pairs take the runtime SameValue (NaN equals NaN, +0 differs from
|
||||
// -0 — the two divergences from ===); every other supported pair
|
||||
// rides exactly the strict-equality machinery, whose answers
|
||||
// SameValue shares: strings by bytes, bools by value, unit literals
|
||||
// by tag, unions per arm (a number arm's payload compare upgrades to
|
||||
// SameValue via unionEq's flag), and the reference kinds by pointer
|
||||
// identity. Statically DISJOINT kind pairs answer the constant false
|
||||
// with the operands still evaluated (tsc admits any pair — Object.is
|
||||
// is (any, any) — and JS evaluates the arguments either way).
|
||||
// dyn/jsval operands keep strict equality's stance: validate first.
|
||||
if (member === "is") {
|
||||
if (call.arguments.length !== 2 || call.arguments.some((a) => ts.isSpreadElement(a))) {
|
||||
L.noLowering(
|
||||
`Object.is with ${call.arguments.length} arguments`,
|
||||
call,
|
||||
"exactly two arguments are the lowered form (JS treats a missing one as undefined — pass it explicitly)",
|
||||
);
|
||||
}
|
||||
const loc = locOf(call);
|
||||
const leftNode = call.arguments[0]!;
|
||||
const rightNode = call.arguments[1]!;
|
||||
const left = L.lowerExpr(leftNode);
|
||||
const right = L.lowerExpr(rightNode);
|
||||
const lk = left.type.kind;
|
||||
const rk = right.type.kind;
|
||||
if (lk === "f64" && rk === "f64") {
|
||||
return { kind: "libCall", fn: "num.sameValue", args: [left, right], type: BOOL, loc };
|
||||
}
|
||||
if (left.type.kind === "string" && right.type.kind === "string") {
|
||||
return { kind: "strEq", negated: false, left, right, type: BOOL, loc };
|
||||
}
|
||||
if (lk === "bool" && rk === "bool") {
|
||||
return { kind: "bin", op: "===", left, right, type: BOOL, loc };
|
||||
}
|
||||
const unitTest = L.lowerUnitComparison(left, right, false, loc);
|
||||
if (unitTest) return unitTest;
|
||||
if (lk === "dyn" || rk === "dyn" || lk === "jsval" || rk === "jsval") {
|
||||
L.noLowering(
|
||||
"Object.is over a dynamic operand",
|
||||
call,
|
||||
"validate/narrow the value first (strict equality's rule) — SameValue only differs from === on numbers (NaN, ±0)",
|
||||
);
|
||||
}
|
||||
if (left.type.kind === "union" || right.type.kind === "union") {
|
||||
const ut = left.type.kind === "union" ? left.type : (right.type as IrType & { kind: "union" });
|
||||
const bothUnion = left.type.kind === "union" && right.type.kind === "union";
|
||||
const sameUnion = bothUnion && typeEquals(left.type, right.type);
|
||||
if ((sameUnion || !bothUnion) && L.eqComparableUnion(ut.unionId)) {
|
||||
const plain = left.type.kind === "union" ? right : left;
|
||||
const arms = L.unions.get(ut.unionId)?.arms ?? [];
|
||||
// The plain side wraps into the union exactly like === when the
|
||||
// union holds its type; a plain PRIMITIVE the union has no arm
|
||||
// for is the disjoint constant false (coercing it would strand).
|
||||
if (bothUnion || arms.some((a) => typeEquals(a, plain.type))) {
|
||||
const sameValue = arms.some((a) => a.kind === "f64");
|
||||
return {
|
||||
kind: "unionEq",
|
||||
unionId: ut.unionId,
|
||||
negated: false,
|
||||
sameValue,
|
||||
left: L.coerceInto(leftNode, left, ut),
|
||||
right: L.coerceInto(rightNode, right, ut),
|
||||
type: BOOL,
|
||||
loc,
|
||||
};
|
||||
}
|
||||
if (
|
||||
plain.type.kind === "f64" || plain.type.kind === "string" ||
|
||||
plain.type.kind === "bool" || isUnitType(plain.type)
|
||||
) {
|
||||
return objectIsDisjointFalse(left, right, loc);
|
||||
}
|
||||
}
|
||||
L.noLowering(
|
||||
"Object.is over these union operands",
|
||||
call,
|
||||
`union-typed comparisons need one comparable shape (${NARROW_FIRST})`,
|
||||
);
|
||||
}
|
||||
// Reference kinds: pointer identity — exactly strict equality
|
||||
// (hierarchy-related classes widen the derived side first).
|
||||
let idLeft = left;
|
||||
let idRight = right;
|
||||
if (left.type.kind === "object" && right.type.kind === "object") {
|
||||
if (L.isSubclassOf(left.type.className, right.type.className)) {
|
||||
idLeft = L.upcastTo(left, right.type.className);
|
||||
} else if (L.isSubclassOf(right.type.className, left.type.className)) {
|
||||
idRight = L.upcastTo(right, left.type.className);
|
||||
}
|
||||
}
|
||||
if (
|
||||
(idLeft.type.kind === "func" && idRight.type.kind === "func") ||
|
||||
(idLeft.type.kind === "classval" && idRight.type.kind === "classval")
|
||||
) {
|
||||
return { kind: "bin", op: "===", left: idLeft, right: idRight, type: BOOL, loc };
|
||||
}
|
||||
if (
|
||||
(idLeft.type.kind === "array" || idLeft.type.kind === "map" ||
|
||||
idLeft.type.kind === "set" || idLeft.type.kind === "object" ||
|
||||
idLeft.type.kind === "record" || idLeft.type.kind === "symbol" ||
|
||||
idLeft.type.kind === "bytes" || idLeft.type.kind === "promise") &&
|
||||
typeEquals(idLeft.type, idRight.type)
|
||||
) {
|
||||
return { kind: "bin", op: "===", left: idLeft, right: idRight, type: BOOL, loc };
|
||||
}
|
||||
// Statically disjoint pairs with a primitive/unit side: SameValue
|
||||
// never crosses kinds, so the answer is the constant false.
|
||||
const disjoint = new Set(["f64", "string", "bool", "undefinedT", "nullT"]);
|
||||
if (lk !== rk && (disjoint.has(lk) || disjoint.has(rk))) {
|
||||
return objectIsDisjointFalse(left, right, loc);
|
||||
}
|
||||
L.noLowering(
|
||||
`Object.is over '${L.fmt(left.type)}' and '${L.fmt(right.type)}' operands`,
|
||||
call,
|
||||
"the operands must share one comparable kind (numbers, strings, booleans, units, one union shape, or one reference type)",
|
||||
);
|
||||
}
|
||||
// `Object.assign(fn, { props })` whose RESULT type maps to the hybrid
|
||||
// (function-with-properties) record: the chalk-shape CONSTRUCTOR.
|
||||
if (member === "assign") {
|
||||
|
||||
@@ -5330,6 +5330,7 @@ export function lowerNew(L: Lowerer, expr: ts.NewExpression): IrExpr {
|
||||
WeakRef: "deref()-after-collect exposes GC timing — genuinely dynamic; hold a strong reference instead",
|
||||
FinalizationRegistry: "finalization callbacks expose GC timing — genuinely dynamic; release resources explicitly instead",
|
||||
SharedArrayBuffer: "no shared-memory threads exist in a compiled program — Uint8Array is the byte storage",
|
||||
ArrayBuffer: "no free-standing ArrayBuffer value exists — typed arrays own their storage: allocate the view directly (new Uint8Array(n)), or erase a fresh buffer into one (new Uint8Array(new ArrayBuffer(n)), new DataView(new ArrayBuffer(n), ...))",
|
||||
Proxy: "property-access metaprogramming has no static lowering (every property read must resolve at compile time)",
|
||||
Function: "runtime code generation cannot be compiled ahead of time (the eval stance) — write the function",
|
||||
};
|
||||
|
||||
@@ -4400,30 +4400,44 @@ const BYTES_CTORS: Record<string, IrBytesElem | undefined> = {
|
||||
const seed: IrExpr = { kind: "arrayLit", elems, type: arrayOf(F64), loc };
|
||||
return { kind: "bytesNew", source: seed, type, loc };
|
||||
}
|
||||
// The SYNTACTIC `new T(new SharedArrayBuffer(n))` form — the
|
||||
// Atomics.wait sleep idiom's construction. The buffer never exists
|
||||
// as a value: n must be a byte-length LITERAL divisible by the
|
||||
// element size (tsc would admit any number; a bad one is Node's
|
||||
// RangeError — rejected at compile time instead of half-lowering),
|
||||
// and the whole expression is a zero-filled typed array of
|
||||
// n/elemSize elements. Sharing is unobservable without threads
|
||||
// (scriptc has none), so erasing the SharedArrayBuffer is exact —
|
||||
// SEMANTICS.md documents the stance.
|
||||
// The SYNTACTIC `new T(new ArrayBuffer(n))` and `new T(new
|
||||
// SharedArrayBuffer(n))` forms — fresh-buffer construction (the
|
||||
// shared spelling is the Atomics.wait sleep idiom's). The buffer
|
||||
// never exists as a value: n must be a byte-length LITERAL divisible
|
||||
// by the element size (tsc would admit any number; a bad one is
|
||||
// Node's RangeError — rejected at compile time instead of
|
||||
// half-lowering), and the whole expression is a zero-filled typed
|
||||
// array of n/elemSize elements. Erasing the buffer is exact: nothing
|
||||
// else can ever reference it, so neither sharing (scriptc has no
|
||||
// threads) nor aliasing is observable — SEMANTICS.md documents the
|
||||
// stance. The RESIZABLE form (a maxByteLength options bag) fences by
|
||||
// name: resize needs the buffer to exist as a value, and none does.
|
||||
if (
|
||||
ts.isNewExpression(argNode) &&
|
||||
ts.isIdentifier(argNode.expression) &&
|
||||
argNode.expression.text === "SharedArrayBuffer" &&
|
||||
(argNode.expression.text === "ArrayBuffer" ||
|
||||
argNode.expression.text === "SharedArrayBuffer") &&
|
||||
L.isStdlibSymbol(L.resolveValueSymbol(argNode.expression) ?? undefined)
|
||||
) {
|
||||
const bufCtor = argNode.expression.text;
|
||||
if ((argNode.arguments?.length ?? 0) > 1) {
|
||||
L.noLowering(
|
||||
`new ${name} over a resizable ${bufCtor}`,
|
||||
argNode,
|
||||
"a maxByteLength options bag makes the buffer resizable, and resizing needs the buffer " +
|
||||
"to exist as a runtime value — no free-standing ArrayBuffer value exists (the buffer here " +
|
||||
"erases into the view): drop the options bag",
|
||||
);
|
||||
}
|
||||
const elemSize = elem === "u8" ? 1 : 4;
|
||||
const lenArg = argNode.arguments?.length === 1 ? argNode.arguments[0] : undefined;
|
||||
const lenT = lenArg ? L.typeOf(lenArg) : null;
|
||||
const byteLen = lenT?.isNumberLiteralType() ? lenT.value : null;
|
||||
if (byteLen === null || byteLen % elemSize !== 0 || byteLen < 0) {
|
||||
L.noLowering(
|
||||
`new ${name} over this SharedArrayBuffer`,
|
||||
`new ${name} over this ${bufCtor}`,
|
||||
argNode,
|
||||
`the byte length must be a number literal divisible by ${elemSize} — new ${name}(new SharedArrayBuffer(${elemSize}))`,
|
||||
`the byte length must be a number literal divisible by ${elemSize} — new ${name}(new ${bufCtor}(${elemSize}))`,
|
||||
);
|
||||
}
|
||||
const count: IrExpr = { kind: "numLit", value: byteLen / elemSize, type: F64, loc };
|
||||
@@ -4477,9 +4491,48 @@ const BYTES_CTORS: Record<string, IrBytesElem | undefined> = {
|
||||
"supported: new DataView(x.buffer), (x.buffer, byteOffset), or (x.buffer, byteOffset, byteLength)",
|
||||
);
|
||||
}
|
||||
// The FRESH-BUFFER form: `new DataView(new ArrayBuffer(n), ...)` — the
|
||||
// buffer erases into the view (nothing else can ever reference it, so
|
||||
// the aliasing a shared buffer would exhibit is unobservable): the
|
||||
// view's storage is a fresh zero-filled n-byte allocation. n must be a
|
||||
// non-negative integer LITERAL (tsc admits any number; a fractional
|
||||
// one is ToIndex truncation nothing here implements). byteOffset and
|
||||
// byteLength keep their runtime Node-RangeError story. The RESIZABLE
|
||||
// form (a maxByteLength options bag) fences by name.
|
||||
if (
|
||||
ts.isNewExpression(bufNode) &&
|
||||
ts.isIdentifier(bufNode.expression) &&
|
||||
bufNode.expression.text === "ArrayBuffer" &&
|
||||
L.isStdlibSymbol(L.resolveValueSymbol(bufNode.expression) ?? undefined)
|
||||
) {
|
||||
if ((bufNode.arguments?.length ?? 0) > 1) {
|
||||
L.noLowering(
|
||||
"new DataView over a resizable ArrayBuffer",
|
||||
bufNode,
|
||||
"a maxByteLength options bag makes the buffer resizable, and resizing needs the buffer " +
|
||||
"to exist as a runtime value — no free-standing ArrayBuffer value exists (the buffer here " +
|
||||
"erases into the view): drop the options bag",
|
||||
);
|
||||
}
|
||||
const lenArg = bufNode.arguments?.length === 1 ? bufNode.arguments[0] : undefined;
|
||||
const lenT = lenArg ? L.typeOf(lenArg) : null;
|
||||
const byteLen = lenT?.isNumberLiteralType() ? lenT.value : null;
|
||||
if (byteLen === null || !Number.isInteger(byteLen) || byteLen < 0) {
|
||||
L.noLowering(
|
||||
"new DataView over this ArrayBuffer",
|
||||
bufNode,
|
||||
"the byte length must be a non-negative integer literal — new DataView(new ArrayBuffer(8))",
|
||||
);
|
||||
}
|
||||
const count: IrExpr = { kind: "numLit", value: byteLen, type: F64, loc };
|
||||
const receiver: IrExpr = { kind: "bytesNew", source: count, type: BYTES_U8, loc };
|
||||
const idxArgs = args.slice(1).map((a) => L.lowerExprExpecting(a, F64));
|
||||
return { kind: "bytesIntrinsic", method: "dataViewNew", receiver, args: idxArgs, type: BYTES_U8, loc };
|
||||
}
|
||||
const hint =
|
||||
"views compile over a typed array's own storage — new DataView(x.buffer, byteOffset?, byteLength?) " +
|
||||
"where x is a Uint8Array/Uint32Array/Float32Array/Buffer value; free-standing ArrayBuffers have no representation";
|
||||
"where x is a Uint8Array/Uint32Array/Float32Array/Buffer value — or a fresh buffer erased into " +
|
||||
"the view: new DataView(new ArrayBuffer(n), ...); free-standing ArrayBuffers have no representation";
|
||||
if (!ts.isPropertyAccessExpression(bufNode) || bufNode.name.text !== "buffer" || bufNode.questionDotToken) {
|
||||
L.noLowering("new DataView over this buffer expression", bufNode, hint);
|
||||
}
|
||||
@@ -4667,6 +4720,26 @@ const BYTES_CTORS: Record<string, IrBytesElem | undefined> = {
|
||||
if (nArgs === 2) args.push(L.lowerExprExpecting(call.arguments[1]!, BOOL));
|
||||
return { kind: "bytesIntrinsic", method: dvGetter.method, receiver, args, type: F64, loc };
|
||||
}
|
||||
// DataView setters — the getters' mirror: (byteOffset, value) plus the
|
||||
// optional littleEndian bool on the multi-byte kinds. Void results;
|
||||
// the same constant Node RangeError on a bad offset (may-throw seeds).
|
||||
// setBigUint64/setBigInt64 never lower (bigint ARGUMENTS have no
|
||||
// representation and no composed form exists) — they keep the member
|
||||
// fence, as does setFloat16.
|
||||
const dvSetter = own(DV_SETTERS, name);
|
||||
if (dvSetter !== undefined && receiverIr.elem === "u8") {
|
||||
const maxArgs = dvSetter.le ? 3 : 2;
|
||||
if (nArgs < 2 || nArgs > maxArgs) {
|
||||
L.noLowering(`.${name} with ${nArgs} arguments`, call);
|
||||
}
|
||||
const receiver = L.lowerExpr(access.expression);
|
||||
const args = [
|
||||
L.lowerExprExpecting(call.arguments[0]!, F64),
|
||||
L.lowerExprExpecting(call.arguments[1]!, F64),
|
||||
];
|
||||
if (nArgs === 3) args.push(L.lowerExprExpecting(call.arguments[2]!, BOOL));
|
||||
return { kind: "bytesIntrinsic", method: dvSetter.method, receiver, args, type: VOID, loc };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -4909,6 +4982,17 @@ const DV_GETTERS: Record<string, { method: IrBytesIntrinsicMethod; le: boolean }
|
||||
getBigInt64: { method: "dvGetBigInt64Number", le: true },
|
||||
};
|
||||
|
||||
const DV_SETTERS: Record<string, { method: IrBytesIntrinsicMethod; le: boolean } | undefined> = {
|
||||
setUint8: { method: "dvSetUint8", le: false },
|
||||
setInt8: { method: "dvSetInt8", le: false },
|
||||
setUint16: { method: "dvSetUint16", le: true },
|
||||
setInt16: { method: "dvSetInt16", le: true },
|
||||
setUint32: { method: "dvSetUint32", le: true },
|
||||
setInt32: { method: "dvSetInt32", le: true },
|
||||
setFloat32: { method: "dvSetFloat32", le: true },
|
||||
setFloat64: { method: "dvSetFloat64", le: true },
|
||||
};
|
||||
|
||||
/** The Buffer statics — `Buffer.from(...)`, `Buffer.alloc(n)`,
|
||||
* `Buffer.concat(list)`, `Buffer.isBuffer(x)` — on THE stdlib Buffer
|
||||
* global (name + provenance; fallback and @types/node alike). Null when
|
||||
|
||||
@@ -847,8 +847,9 @@ function lowerExprInner(L: Lowerer, expr: ts.Expression): IrExpr {
|
||||
BigInt: "arbitrary-precision integers have no static representation — f64 is the one number type (Number.MAX_SAFE_INTEGER bounds exact integers)",
|
||||
Proxy: "property-access metaprogramming has no static lowering (every property read must resolve at compile time)",
|
||||
Reflect: "reflective property access has no static lowering — read and call members directly",
|
||||
Intl: "locale- and ICU-backed behavior lives outside the static runtime (the localeCompare stance: code-unit order, no collation/locale data)",
|
||||
Intl: "locale- and ICU-backed behavior lives outside the static runtime (the localeCompare stance: code-unit order, no collation/locale data) — what lowers: the composed new Intl.NumberFormat(\"en-US\").format(x) and x.toLocaleString(\"en-US\") with default options",
|
||||
SharedArrayBuffer: "no shared-memory threads exist in a compiled program — Uint8Array is the byte storage",
|
||||
ArrayBuffer: "no free-standing ArrayBuffer value exists — typed arrays own their storage (new Uint8Array(n) allocates; new Uint8Array(new ArrayBuffer(n)) erases the buffer into the view)",
|
||||
WeakRef: "deref()-after-collect exposes GC timing — genuinely dynamic; hold a strong reference instead",
|
||||
FinalizationRegistry: "finalization callbacks expose GC timing — genuinely dynamic; release resources explicitly instead",
|
||||
eval: "runtime code evaluation cannot be compiled ahead of time",
|
||||
@@ -7133,6 +7134,7 @@ export function lowerBinary(L: Lowerer, expr: ts.BinaryExpression): IrExpr {
|
||||
kind: "unionEq",
|
||||
unionId: ut.unionId,
|
||||
negated,
|
||||
sameValue: false,
|
||||
left: L.coerceInto(expr.left, left, ut),
|
||||
right: L.coerceInto(expr.right, right, ut),
|
||||
type: BOOL,
|
||||
|
||||
@@ -2822,6 +2822,7 @@ export function lowerVarDecl(L: Lowerer, decl: ts.VariableDeclaration, isLet: bo
|
||||
kind: "unionEq",
|
||||
unionId: unionType.unionId,
|
||||
negated: false,
|
||||
sameValue: false,
|
||||
left: disc,
|
||||
right: L.coerceInto(clause.expression, test, unionType),
|
||||
type: BOOL,
|
||||
|
||||
@@ -2224,9 +2224,32 @@ export class Lowerer {
|
||||
}
|
||||
}
|
||||
// STANDARD-LIBRARY nominal types with no lowering at all: the SC2020
|
||||
// story, naming the type.
|
||||
// story, naming the type — and for the families with a WHY, the same
|
||||
// pointed reason the identifier/constructor chokepoints teach.
|
||||
if (stdlibNominal) {
|
||||
this.pushDiag(noLoweringDiag(this.checker.typeToString(type), locOf(node)));
|
||||
const intlHint =
|
||||
"Intl formatter values have no representation — the COMPOSED en-US forms lower: " +
|
||||
'new Intl.NumberFormat("en-US").format(x) and x.toLocaleString("en-US") with default options; ' +
|
||||
"the rest is ICU locale data the binary does not carry";
|
||||
const typeHints: Record<string, string | undefined> = {
|
||||
ArrayBuffer:
|
||||
"no free-standing ArrayBuffer value exists — typed arrays own their storage " +
|
||||
"(new Uint8Array(n) allocates; new Uint8Array(new ArrayBuffer(n)) erases the buffer into the view)",
|
||||
SharedArrayBuffer:
|
||||
"no shared-memory threads exist in a compiled program — Uint8Array is the byte storage",
|
||||
NumberFormat: intlHint,
|
||||
DateTimeFormat: intlHint,
|
||||
DurationFormat: intlHint,
|
||||
PluralRules: intlHint,
|
||||
Collator: intlHint,
|
||||
ListFormat: intlHint,
|
||||
RelativeTimeFormat: intlHint,
|
||||
Segmenter: intlHint,
|
||||
DisplayNames: intlHint,
|
||||
};
|
||||
this.pushDiag(
|
||||
noLoweringDiag(this.checker.typeToString(type), locOf(node), typeHints[stdlibOwnSym?.name ?? ""]),
|
||||
);
|
||||
throw new PoisonError();
|
||||
}
|
||||
// USER record shapes blocked by ONE member (SC2009's record arm):
|
||||
|
||||
@@ -1038,9 +1038,18 @@ export const BUILTIN_MODULE_FENCE_HINTS: Record<string, Record<string, string |
|
||||
hint =
|
||||
"the regex-LITERAL argument forms lower (s.match(/re/)); a string pattern constructs " +
|
||||
"a RegExp at runtime, which has no lowering";
|
||||
} else if (container === "ArrayBuffer" || container.startsWith("ArrayBuffer<")) {
|
||||
hint =
|
||||
"resize/transfer/maxByteLength need the buffer to exist as a runtime value, and no " +
|
||||
"free-standing ArrayBuffer value does — typed arrays own fixed-length storage " +
|
||||
"(new Uint8Array(n)); allocate a new view and copy instead";
|
||||
} else if (container === "SharedArrayBuffer" || container.startsWith("SharedArrayBuffer<")) {
|
||||
hint =
|
||||
"no shared-memory threads exist in a compiled program — Uint8Array is the byte storage " +
|
||||
"(a fixed-length allocation: grow has nothing to share it with)";
|
||||
} else if (
|
||||
container === "Intl" || container.startsWith("Intl.") ||
|
||||
["NumberFormat", "DateTimeFormat", "PluralRules", "Collator", "Locale",
|
||||
["NumberFormat", "DateTimeFormat", "DurationFormat", "PluralRules", "Collator", "Locale",
|
||||
"RelativeTimeFormat", "ListFormat", "Segmenter", "DisplayNames"].includes(container) ||
|
||||
member === "toLocaleString" || member === "toLocaleDateString" ||
|
||||
member === "toLocaleTimeString" || member === "toLocaleLowerCase" ||
|
||||
@@ -1048,8 +1057,9 @@ export const BUILTIN_MODULE_FENCE_HINTS: Record<string, Record<string, string |
|
||||
) {
|
||||
hint =
|
||||
"locale- and ICU-backed behavior lives outside the static runtime (the localeCompare " +
|
||||
"stance: code-unit order, no collation/locale data) — format with template literals, " +
|
||||
"toFixed, and toString instead";
|
||||
"stance: code-unit order, no collation/locale data) — what lowers: the composed " +
|
||||
'new Intl.NumberFormat("en-US").format(x) and x.toLocaleString("en-US") with default ' +
|
||||
"options; format with template literals, toFixed, and toString otherwise";
|
||||
} else if (container === "Object" && member === "assign") {
|
||||
hint =
|
||||
"spread instead: { ...a, ...b } builds the merged record; what lowers: the empty-target " +
|
||||
|
||||
@@ -1366,7 +1366,12 @@ export type IrStrIntrinsicMethod =
|
||||
* DataView" RangeError on any bad offset. `dvGetBigUint64Number`/
|
||||
* `dvGetBigInt64Number` are the COMPOSED `Number(view.getBigUint64(...))`
|
||||
* lowerings — the bare bigint-returning calls are fenced, the wrapped
|
||||
* form converts the 8-byte integer to double exactly as Number(bigint). */
|
||||
* form converts the 8-byte integer to double exactly as Number(bigint).
|
||||
* The `dvSet*` setters mirror the getters: [offset, value] plus the
|
||||
* optional bool littleEndian on the multi-byte kinds → void, the same
|
||||
* constant RangeError on any bad offset; values coerce JS-exactly
|
||||
* (integer kinds by modular truncation, Float32 by double→float
|
||||
* rounding). No BIG setters exist — bigint arguments never lower. */
|
||||
export type IrBytesIntrinsicMethod =
|
||||
| "length"
|
||||
| "byteLength"
|
||||
@@ -1419,7 +1424,15 @@ export type IrBytesIntrinsicMethod =
|
||||
| "dvGetFloat32"
|
||||
| "dvGetFloat64"
|
||||
| "dvGetBigUint64Number"
|
||||
| "dvGetBigInt64Number";
|
||||
| "dvGetBigInt64Number"
|
||||
| "dvSetUint8"
|
||||
| "dvSetInt8"
|
||||
| "dvSetUint16"
|
||||
| "dvSetInt16"
|
||||
| "dvSetUint32"
|
||||
| "dvSetInt32"
|
||||
| "dvSetFloat32"
|
||||
| "dvSetFloat64";
|
||||
|
||||
/** The bytesIntrinsic methods that can raise a catchable error — backends'
|
||||
* may-throw analyses seed on these exactly like MAY_THROW_LIB_FNS. */
|
||||
@@ -1449,6 +1462,14 @@ export const MAY_THROW_BYTES_METHODS: ReadonlySet<IrBytesIntrinsicMethod> = new
|
||||
"dvGetFloat64",
|
||||
"dvGetBigUint64Number",
|
||||
"dvGetBigInt64Number",
|
||||
"dvSetUint8",
|
||||
"dvSetInt8",
|
||||
"dvSetUint16",
|
||||
"dvSetInt16",
|
||||
"dvSetUint32",
|
||||
"dvSetInt32",
|
||||
"dvSetFloat32",
|
||||
"dvSetFloat64",
|
||||
]);
|
||||
|
||||
/** The regex operation surface. Receiver/arg conventions (validated):
|
||||
@@ -1749,6 +1770,20 @@ export type IrLibFn =
|
||||
* forms keep their island lowering. Results +1; never throw. */
|
||||
| "num.toExponential"
|
||||
| "num.toFixed0"
|
||||
/** Object.is over two numbers — the spec's SameValue on doubles: NaN
|
||||
* equals NaN, +0 differs from -0, everything else is `===`. Plain bool
|
||||
* result; never throws. (Union-armed operands take unionEq's sameValue
|
||||
* flag instead — this is the both-f64 fast path.) */
|
||||
| "num.sameValue"
|
||||
/** `new Intl.NumberFormat("en-US").format(x)` and
|
||||
* `x.toLocaleString("en-US")` with DEFAULT options — the one locale
|
||||
* whose data the runtime embeds (Node's default-build locale): decimal
|
||||
* notation, 0–3 fraction digits rounded half-up on the SHORTEST
|
||||
* round-tripping decimal (ICU's rounding input, probed vs Node —
|
||||
* format(1.0005) is "1.001" though toFixed(3) answers "1.000"), ","
|
||||
* grouping every three integer digits, "∞"/"NaN" texts, and "-0" for
|
||||
* negative inputs rounding to zero. Result +1; never throws. */
|
||||
| "intl.numFormatEnUs"
|
||||
/** `delete process.env[NAME]` — unsetenv(3): the mutation is visible to
|
||||
* every later read (process.envGet asks getenv fresh) and inherited by
|
||||
* spawned children, exactly Node. Statement position only (JS's boolean
|
||||
@@ -4306,11 +4341,15 @@ export type IrExpr =
|
||||
* comparison (`u === "text"`) arrives here after the frontend wraps the
|
||||
* plain side (payload identity is preserved by the wrap, so ref-arm
|
||||
* semantics stay JS-exact). Operands are borrowed; result is a plain
|
||||
* bool. `negated` is the `!==` spelling. */
|
||||
* bool. `negated` is the `!==` spelling. `sameValue` upgrades the f64
|
||||
* arm's compare from `===` to SameValue (NaN equals NaN, +0 differs
|
||||
* from -0) — the Object.is lowering; every other arm's compare is
|
||||
* shared between the two semantics. */
|
||||
| {
|
||||
kind: "unionEq";
|
||||
unionId: string;
|
||||
negated: boolean;
|
||||
sameValue: boolean;
|
||||
left: IrExpr;
|
||||
right: IrExpr;
|
||||
type: IrType;
|
||||
|
||||
@@ -209,6 +209,8 @@ export const LIB_FN_SIGS: Record<IrLibFn, { argTypes: (IrType | null)[]; result:
|
||||
"regexp.escape": { argTypes: [STRING], result: STRING },
|
||||
"num.toExponential": { argTypes: [F64], result: STRING },
|
||||
"num.toFixed0": { argTypes: [F64], result: STRING },
|
||||
"num.sameValue": { argTypes: [F64, F64], result: BOOL },
|
||||
"intl.numFormatEnUs": { argTypes: [F64], result: STRING },
|
||||
"sym.new": { argTypes: [STRING], result: SYMBOL_T },
|
||||
"sym.newAnon": { argTypes: [], result: SYMBOL_T },
|
||||
"sym.for": { argTypes: [STRING], result: SYMBOL_T },
|
||||
@@ -2082,13 +2084,23 @@ function validateFunction(
|
||||
? { argTypes: [STRING, F64, F64, F64], minArgs: 4, result: F64 }
|
||||
: e.method === "dataViewNew"
|
||||
? { argTypes: [F64, F64], minArgs: 0, result: BYTES_U8 }
|
||||
: {
|
||||
// dvGet*: the 8-bit getters take no littleEndian.
|
||||
argTypes:
|
||||
e.method === "dvGetUint8" || e.method === "dvGetInt8" ? [F64] : [F64, BOOL],
|
||||
minArgs: 1,
|
||||
result: F64,
|
||||
});
|
||||
: e.method.startsWith("dvSet")
|
||||
? {
|
||||
// dvSet*: [offset, value], the 8-bit setters take no littleEndian.
|
||||
argTypes:
|
||||
e.method === "dvSetUint8" || e.method === "dvSetInt8"
|
||||
? [F64, F64]
|
||||
: [F64, F64, BOOL],
|
||||
minArgs: 2,
|
||||
result: VOID,
|
||||
}
|
||||
: {
|
||||
// dvGet*: the 8-bit getters take no littleEndian.
|
||||
argTypes:
|
||||
e.method === "dvGetUint8" || e.method === "dvGetInt8" ? [F64] : [F64, BOOL],
|
||||
minArgs: 1,
|
||||
result: F64,
|
||||
});
|
||||
if (e.args.length < sig.minArgs || e.args.length > sig.argTypes.length) {
|
||||
err(`bytesIntrinsic ${e.method}: ${e.args.length} args`, e.loc);
|
||||
}
|
||||
|
||||
@@ -4754,6 +4754,24 @@
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/corpus/2560-arraybuffer-dataview-set.ts": {
|
||||
"order": [
|
||||
"<repo>/tests/corpus/2560-arraybuffer-dataview-set.ts"
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/corpus/2561-object-is.ts": {
|
||||
"order": [
|
||||
"<repo>/tests/corpus/2561-object-is.ts"
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/corpus/2562-intl-numberformat-en-us.ts": {
|
||||
"order": [
|
||||
"<repo>/tests/corpus/2562-intl-numberformat-en-us.ts"
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/corpus/300-if-else.ts": {
|
||||
"order": [
|
||||
"<repo>/tests/corpus/300-if-else.ts"
|
||||
@@ -6282,6 +6300,12 @@
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/diagnostics/intl.ts": {
|
||||
"order": [
|
||||
"<repo>/tests/diagnostics/intl.ts"
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/diagnostics/island-boundary-slots.ts": {
|
||||
"order": [
|
||||
"<repo>/tests/diagnostics/island-boundary-slots.ts"
|
||||
@@ -6632,6 +6656,48 @@
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/fixtures/node-types/argv-env.ts": {
|
||||
"order": [
|
||||
"<repo>/tests/fixtures/node-types/argv-env.ts"
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/fixtures/node-types/fenced.ts": {
|
||||
"order": [
|
||||
"<repo>/tests/fixtures/node-types/fenced.ts"
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/fixtures/node-types/import-fences.ts": {
|
||||
"order": [
|
||||
"<repo>/tests/fixtures/node-types/import-fences.ts"
|
||||
],
|
||||
"diags": [
|
||||
{
|
||||
"code": "SC1010",
|
||||
"message": "the 'v8' module is not supported yet",
|
||||
"loc": {
|
||||
"file": "<repo>/tests/fixtures/node-types/import-fences.ts",
|
||||
"start": 422,
|
||||
"end": 461
|
||||
},
|
||||
"milestone": "M4",
|
||||
"hint": "relative imports (./file, ../dir/file), package.json-mediated project imports (#alias via the imports field, self-name references via exports), installed npm packages (their code runs under --dynamic), and the built-in fs, fs/promises, path, os, url, crypto, zlib, child_process, net, http, tls, https, http2, dgram, dns, util, util/types, string_decoder, readline, events, stream, buffer, assert, assert/strict, worker_threads, cluster, tty, async_hooks, timers, timers/promises, diagnostics_channel, perf_hooks, and node:test and node:module modules (bare or node:-prefixed) are supported"
|
||||
}
|
||||
]
|
||||
},
|
||||
"<repo>/tests/fixtures/node-types/path-os.ts": {
|
||||
"order": [
|
||||
"<repo>/tests/fixtures/node-types/path-os.ts"
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/fixtures/node-types/stream-capture.ts": {
|
||||
"order": [
|
||||
"<repo>/tests/fixtures/node-types/stream-capture.ts"
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/fixtures/npm/cases/2465-getter-table/main.ts": {
|
||||
"order": [
|
||||
"<repo>/tests/fixtures/npm/cases/2465-getter-table/main.ts"
|
||||
@@ -7041,48 +7107,6 @@
|
||||
"hint": "set \"strictNullChecks\": true (or \"strict\": true) in the project tsconfig"
|
||||
}
|
||||
]
|
||||
},
|
||||
"<repo>/tests/fixtures/node-types/argv-env.ts": {
|
||||
"order": [
|
||||
"<repo>/tests/fixtures/node-types/argv-env.ts"
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/fixtures/node-types/fenced.ts": {
|
||||
"order": [
|
||||
"<repo>/tests/fixtures/node-types/fenced.ts"
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/fixtures/node-types/import-fences.ts": {
|
||||
"order": [
|
||||
"<repo>/tests/fixtures/node-types/import-fences.ts"
|
||||
],
|
||||
"diags": [
|
||||
{
|
||||
"code": "SC1010",
|
||||
"message": "the 'v8' module is not supported yet",
|
||||
"loc": {
|
||||
"file": "<repo>/tests/fixtures/node-types/import-fences.ts",
|
||||
"start": 422,
|
||||
"end": 461
|
||||
},
|
||||
"milestone": "M4",
|
||||
"hint": "relative imports (./file, ../dir/file), package.json-mediated project imports (#alias via the imports field, self-name references via exports), installed npm packages (their code runs under --dynamic), and the built-in fs, fs/promises, path, os, url, crypto, zlib, child_process, net, http, tls, https, http2, dgram, dns, util, util/types, string_decoder, readline, events, stream, buffer, assert, assert/strict, worker_threads, cluster, tty, async_hooks, timers, timers/promises, diagnostics_channel, perf_hooks, and node:test and node:module modules (bare or node:-prefixed) are supported"
|
||||
}
|
||||
]
|
||||
},
|
||||
"<repo>/tests/fixtures/node-types/path-os.ts": {
|
||||
"order": [
|
||||
"<repo>/tests/fixtures/node-types/path-os.ts"
|
||||
],
|
||||
"diags": []
|
||||
},
|
||||
"<repo>/tests/fixtures/node-types/stream-capture.ts": {
|
||||
"order": [
|
||||
"<repo>/tests/fixtures/node-types/stream-capture.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,6 +312,49 @@ double scr_dataview_get(const ScrBytes *b, double byte_off, ScrDataViewGet kind,
|
||||
return 0; /* unreachable */
|
||||
}
|
||||
|
||||
void scr_dataview_set(ScrBytes *b, double byte_off, double value, ScrDataViewGet kind, bool le) {
|
||||
size_t width = scr_dataview_get_size(kind);
|
||||
/* ToIndex + the view-relative bounds check — the getters' ONE message. */
|
||||
double off = (byte_off != byte_off) ? 0 : trunc(byte_off);
|
||||
if (!(off >= 0) || off > 9007199254740991.0 || off + (double)width > (double)b->len) {
|
||||
static const char msg[] = "Offset is outside the bounds of the DataView";
|
||||
scr_throw_error_msg(SCR_ERR_RANGE, msg, sizeof msg - 1);
|
||||
return;
|
||||
}
|
||||
/* Coerce to the stored bit pattern. The integer kinds share ToUint32's
|
||||
* 2^32 residue (signed/unsigned store the same bits; narrower widths
|
||||
* take the low bytes — 2^width divides 2^32, so the residues agree);
|
||||
* F32 rounds double→float to nearest even, exactly the spec. */
|
||||
uint64_t u = 0;
|
||||
switch (kind) {
|
||||
case SCR_DV_U8:
|
||||
case SCR_DV_I8:
|
||||
case SCR_DV_U16:
|
||||
case SCR_DV_I16:
|
||||
case SCR_DV_U32:
|
||||
case SCR_DV_I32:
|
||||
u = (uint64_t)scr_bytes_to_u32(value);
|
||||
break;
|
||||
case SCR_DV_F32: {
|
||||
float f = (float)value;
|
||||
uint32_t bits;
|
||||
memcpy(&bits, &f, 4);
|
||||
u = bits;
|
||||
break;
|
||||
}
|
||||
case SCR_DV_F64:
|
||||
memcpy(&u, &value, 8);
|
||||
break;
|
||||
default:
|
||||
return; /* BIG kinds never lower (bigint arguments) */
|
||||
}
|
||||
/* Scatter host-independently; le false is the JS big-endian default. */
|
||||
uint8_t *p = b->data + (size_t)off;
|
||||
for (size_t i = 0; i < width; i++) {
|
||||
p[le ? i : width - 1 - i] = (uint8_t)(u >> (8 * i));
|
||||
}
|
||||
}
|
||||
|
||||
/* ── encodings (u8 only — the compiler routes only u8 receivers here) ──── */
|
||||
|
||||
static const char scr_hex_digits[] = "0123456789abcdef";
|
||||
|
||||
@@ -3486,6 +3486,104 @@ ScrStr *scr_num_to_fixed0(double x) {
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Increment a decimal digit string in place. Returns true on overflow —
|
||||
* the value becomes 1 followed by len zeros (the caller folds the zeros
|
||||
* into its scale); an EMPTY string increments to "1" the same way (the
|
||||
* round-up-from-nothing case: 0.0005 at 3 fraction digits). */
|
||||
static bool scr_dec_inc(char *d, int len) {
|
||||
for (int i = len - 1; i >= 0; i--) {
|
||||
if (d[i] != '9') {
|
||||
d[i]++;
|
||||
return false;
|
||||
}
|
||||
d[i] = '0';
|
||||
}
|
||||
d[0] = '1';
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Intl.NumberFormat("en-US").format(x) / x.toLocaleString("en-US") with
|
||||
* DEFAULT options: decimal notation, minimum 0 / maximum 3 fraction
|
||||
* digits, "," grouping every three integer digits, "∞"/"NaN" texts, and
|
||||
* "-0" whenever the input is negative or negative zero even after
|
||||
* rounding to zero. Rounding is half-up ON THE SHORTEST ROUND-TRIPPING
|
||||
* DECIMAL — ICU's rounding input, probed against Node: format(1.0005) is
|
||||
* "1.001" although the double is 1.000499... and toFixed(3) answers
|
||||
* "1.000"; format(1e23) prints the shortest form's trailing zeros, not
|
||||
* the double's exact expansion. The en-US/latn symbols (",", ".", "∞",
|
||||
* "NaN", group size 3) are the whole embedded locale surface. Verified
|
||||
* differentially against Node. Result +1; never throws. */
|
||||
ScrStr *scr_intl_num_format_en_us(double x) {
|
||||
if (isnan(x)) return scr_str_new("NaN", 3);
|
||||
if (isinf(x)) {
|
||||
return x < 0 ? scr_str_new("-\xE2\x88\x9E", 4) : scr_str_new("\xE2\x88\x9E", 3);
|
||||
}
|
||||
bool neg = signbit(x) != 0;
|
||||
if (x == 0) return neg ? scr_str_new("-0", 2) : scr_str_new("0", 1);
|
||||
double a = neg ? -x : x;
|
||||
|
||||
/* Shortest digits: value = 0.d × 10^n (no trailing zeros, k ≤ 17). */
|
||||
char d[18];
|
||||
int n;
|
||||
int k = scr_f64_digits(a, d, &n);
|
||||
|
||||
/* Round at 3 fraction digits: fraction position p is digit index
|
||||
* n+p-1, so index n+3 is the first DROPPED digit. Half-up on the
|
||||
* decimal digits — the shortest string ends right after them, so
|
||||
* "first dropped digit ≥ 5" IS the whole decision. */
|
||||
int keep = n + 3;
|
||||
if (keep < k) {
|
||||
bool up = keep >= 0 && d[keep] >= '5';
|
||||
k = keep < 0 ? 0 : keep;
|
||||
if (up && scr_dec_inc(d, k)) {
|
||||
/* Carried out (all nines, or the round-up-from-nothing 0.0005
|
||||
* case): one leading 1, the dropped nines fold into the scale. */
|
||||
k = 1;
|
||||
n += 1;
|
||||
} else if (k == 0) {
|
||||
/* Everything rounded away: ±0 with the sign preserved. */
|
||||
return neg ? scr_str_new("-0", 2) : scr_str_new("0", 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Assemble: integer digits (indices [0, n)), zero-padded past k, then
|
||||
* the ≤ 3 fraction digits (indices n..n+2, '0' outside [0, k)) with
|
||||
* trailing zeros trimmed, then commas every three integer digits. */
|
||||
char frac[3];
|
||||
int flen = 0;
|
||||
for (int p = 1; p <= 3; p++) {
|
||||
int idx = n + p - 1;
|
||||
frac[flen++] = (idx >= 0 && idx < k) ? d[idx] : '0';
|
||||
}
|
||||
while (flen > 0 && frac[flen - 1] == '0') flen--;
|
||||
|
||||
char out[512];
|
||||
int o = 0;
|
||||
if (neg) out[o++] = '-';
|
||||
if (n <= 0) {
|
||||
out[o++] = '0';
|
||||
} else {
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (i > 0 && (n - i) % 3 == 0) out[o++] = ',';
|
||||
out[o++] = (i < k) ? d[i] : '0';
|
||||
}
|
||||
}
|
||||
if (flen > 0) {
|
||||
out[o++] = '.';
|
||||
memcpy(out + o, frac, (size_t)flen);
|
||||
o += flen;
|
||||
}
|
||||
return scr_str_new(out, (size_t)o);
|
||||
}
|
||||
|
||||
/* Object.is over two numbers — the spec's SameValue on doubles: NaN
|
||||
* equals NaN, +0 differs from -0, everything else is ==. */
|
||||
bool scr_num_same_value(double a, double b) {
|
||||
if (a != a) return b != b;
|
||||
if (a == 0 && b == 0) return signbit(a) == signbit(b);
|
||||
return a == b;
|
||||
}
|
||||
|
||||
bool scr_num_is_nan(double x) { return isnan(x) != 0; }
|
||||
|
||||
bool scr_num_is_integer(double x) { return isfinite(x) && trunc(x) == x; }
|
||||
|
||||
@@ -24,27 +24,19 @@
|
||||
* unchanged. Provides d2d(), d2d_small_int(), decimalLength17(), div10(). */
|
||||
#include "../vendor/ryu/d2s.c"
|
||||
|
||||
size_t scr_f64_to_str(double x, char *buf) {
|
||||
if (isnan(x)) return (size_t)(stpcpy(buf, "NaN") - buf);
|
||||
if (x == 0) return (size_t)(stpcpy(buf, "0") - buf); /* covers -0 */
|
||||
if (isinf(x)) {
|
||||
return (size_t)(stpcpy(buf, x < 0 ? "-Infinity" : "Infinity") - buf);
|
||||
}
|
||||
|
||||
char *out = buf;
|
||||
if (x < 0) {
|
||||
*out++ = '-';
|
||||
x = -x;
|
||||
}
|
||||
|
||||
/* Shortest round-tripping digits via Ryū. Mirrors d2s_buffered_n's
|
||||
* dispatch: the exact small-integer fast path first (trailing decimal
|
||||
* zeros folded into the exponent), the full algorithm otherwise. */
|
||||
/* The Ryū digit core, shared by the ECMA placement below and the Intl
|
||||
* en-US number formatter (scr_lib.c): the shortest round-tripping digit
|
||||
* string for a positive finite double — value = 0.digits × 10^n with no
|
||||
* trailing zeros. Returns k (the digit count, ≤ 17); digits is
|
||||
* NUL-terminated. Mirrors d2s_buffered_n's dispatch: the exact
|
||||
* small-integer fast path first (trailing decimal zeros folded into the
|
||||
* exponent), the full algorithm otherwise. */
|
||||
int scr_f64_digits(double x, char digits[18], int *n_out) {
|
||||
uint64_t bits;
|
||||
memcpy(&bits, &x, sizeof bits);
|
||||
const uint64_t ieeeMantissa = bits & ((1ull << DOUBLE_MANTISSA_BITS) - 1);
|
||||
const uint32_t ieeeExponent =
|
||||
(uint32_t)(bits >> DOUBLE_MANTISSA_BITS); /* sign already stripped */
|
||||
(uint32_t)(bits >> DOUBLE_MANTISSA_BITS); /* sign must be stripped */
|
||||
floating_decimal_64 v;
|
||||
if (d2d_small_int(ieeeMantissa, ieeeExponent, &v)) {
|
||||
for (;;) {
|
||||
@@ -60,9 +52,8 @@ size_t scr_f64_to_str(double x, char *buf) {
|
||||
|
||||
/* Ryū's (mantissa, exponent) → ECMA's (digits, k, n): the k mantissa
|
||||
* digits have no trailing zeros, and value = 0.digits * 10^n. */
|
||||
char digits[18];
|
||||
int k = (int)decimalLength17(v.mantissa);
|
||||
int n = v.exponent + k;
|
||||
*n_out = v.exponent + k;
|
||||
digits[k] = '\0';
|
||||
uint64_t m = v.mantissa;
|
||||
for (int i = k - 1; i >= 0; i--) {
|
||||
@@ -70,6 +61,25 @@ size_t scr_f64_to_str(double x, char *buf) {
|
||||
digits[i] = (char)('0' + (uint32_t)m - 10 * (uint32_t)q);
|
||||
m = q;
|
||||
}
|
||||
return k;
|
||||
}
|
||||
|
||||
size_t scr_f64_to_str(double x, char *buf) {
|
||||
if (isnan(x)) return (size_t)(stpcpy(buf, "NaN") - buf);
|
||||
if (x == 0) return (size_t)(stpcpy(buf, "0") - buf); /* covers -0 */
|
||||
if (isinf(x)) {
|
||||
return (size_t)(stpcpy(buf, x < 0 ? "-Infinity" : "Infinity") - buf);
|
||||
}
|
||||
|
||||
char *out = buf;
|
||||
if (x < 0) {
|
||||
*out++ = '-';
|
||||
x = -x;
|
||||
}
|
||||
|
||||
char digits[18];
|
||||
int n;
|
||||
int k = scr_f64_digits(x, digits, &n);
|
||||
|
||||
if (k <= n && n <= 21) {
|
||||
/* Integer: digits followed by n-k zeros. */
|
||||
|
||||
@@ -3896,6 +3896,12 @@ void scr_jsval_cast_fail(ScrJsval *v, const ScrStr *target);
|
||||
*/
|
||||
size_t scr_f64_to_str(double x, char *buf);
|
||||
|
||||
/* The Ryū digit core (scr_number.c), shared with the Intl en-US number
|
||||
* formatter: the shortest round-tripping digits of a POSITIVE finite
|
||||
* double — value = 0.digits × 10^n, no trailing zeros, NUL-terminated.
|
||||
* Returns k, the digit count (≤ 17). */
|
||||
int scr_f64_digits(double x, char digits[18], int *n_out);
|
||||
|
||||
/* ToString for template literals / string coercion. Returns +1. */
|
||||
ScrStr *scr_f64_to_scrstr(double x);
|
||||
ScrStr *scr_bool_to_scrstr(bool b); /* interned "true"/"false" */
|
||||
@@ -3927,6 +3933,18 @@ bool scr_num_is_safe_integer(double x);
|
||||
ScrStr *scr_num_to_exponential(double x);
|
||||
ScrStr *scr_num_to_fixed0(double x);
|
||||
|
||||
/* Object.is over two numbers — the spec's SameValue on doubles: NaN
|
||||
* equals NaN, +0 differs from -0, everything else is ==. Never throws. */
|
||||
bool scr_num_same_value(double a, double b);
|
||||
|
||||
/* Intl.NumberFormat("en-US").format(x) / x.toLocaleString("en-US") with
|
||||
* default options: decimal notation, 0–3 fraction digits rounded half-up
|
||||
* on the shortest round-tripping decimal (ICU's rounding input — NOT
|
||||
* toFixed's exact-value rounding), "," grouping every three integer
|
||||
* digits, "∞"/"NaN" texts, "-0" for negative inputs rounding to zero.
|
||||
* en-US is the one embedded locale. Result +1; never throws. */
|
||||
ScrStr *scr_intl_num_format_en_us(double x);
|
||||
|
||||
/* ── Date, the composed slice (scr_lib.c) ─────────────────────────────
|
||||
* Date values have no representation; the runtime surface is exactly
|
||||
* Date.now() and toISOString over a millisecond time value. */
|
||||
@@ -4081,6 +4099,14 @@ typedef enum ScrDataViewGet {
|
||||
} ScrDataViewGet;
|
||||
double scr_dataview_get(const ScrBytes *b, double byte_off, ScrDataViewGet kind, bool le);
|
||||
|
||||
/* DataView setters (the integer/float kinds only — the BIG kinds never
|
||||
* lower: bigint arguments have no representation). Values coerce
|
||||
* JS-exactly: the integer kinds by modular truncation (ToUint32's residue
|
||||
* — the narrower widths store its low bytes, the same 2^width residue),
|
||||
* F32 by double→float round-to-nearest-even. Offsets go through ToIndex
|
||||
* with the getters' one RangeError. */
|
||||
void scr_dataview_set(ScrBytes *b, double byte_off, double value, ScrDataViewGet kind, bool le);
|
||||
|
||||
/* Element read/write. Any invalid index — negative, fractional, NaN, or
|
||||
* out of bounds — TRAPS like the array runtime (SEMANTICS.md documents
|
||||
* the divergence from JS's undefined-read/ignored-write). Writes coerce
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
// Fresh-ArrayBuffer erasure and DataView setters. `new T(new
|
||||
// ArrayBuffer(n))` and `new DataView(new ArrayBuffer(n), ...)` erase the
|
||||
// buffer into the view (nothing else can reference it, so aliasing is
|
||||
// unobservable); the setter family mirrors the getters: JS-exact value
|
||||
// coercions (modular truncation, double→float rounding), the optional
|
||||
// littleEndian flag, and Node's one constant RangeError on bad offsets.
|
||||
|
||||
// Erasure into every typed-array kind: zero-filled, correct element count.
|
||||
const u8 = new Uint8Array(new ArrayBuffer(8));
|
||||
u8[0] = 255;
|
||||
console.log(u8.length, u8.byteLength, u8[0], u8[7]);
|
||||
const u32 = new Uint32Array(new ArrayBuffer(16));
|
||||
u32[3] = 0xdeadbeef;
|
||||
console.log(u32.length, u32.byteLength, u32[3]);
|
||||
const i32 = new Int32Array(new ArrayBuffer(8));
|
||||
i32[1] = -2;
|
||||
console.log(i32.length, i32[1]);
|
||||
const f32 = new Float32Array(new ArrayBuffer(12));
|
||||
f32[2] = 1.5;
|
||||
console.log(f32.length, f32[2]);
|
||||
const empty = new Uint8Array(new ArrayBuffer(0));
|
||||
console.log(empty.length, empty.byteLength);
|
||||
|
||||
// DataView over a fresh buffer: zero bytes until written.
|
||||
const dv = new DataView(new ArrayBuffer(8));
|
||||
console.log(dv.byteLength, dv.byteOffset, dv.getFloat64(0));
|
||||
|
||||
// Setter/getter roundtrips on every width, both endiannesses.
|
||||
dv.setUint8(0, 0xab);
|
||||
dv.setInt8(1, -1);
|
||||
console.log(dv.getUint8(0), dv.getInt8(1), dv.getUint8(1));
|
||||
dv.setUint16(2, 0xbeef);
|
||||
console.log(dv.getUint16(2), dv.getUint8(2), dv.getUint8(3));
|
||||
dv.setUint16(2, 0xbeef, true);
|
||||
console.log(dv.getUint16(2, true), dv.getUint8(2), dv.getUint8(3));
|
||||
dv.setInt16(4, -2, false);
|
||||
console.log(dv.getInt16(4), dv.getUint16(4));
|
||||
dv.setUint32(4, 0x01020304);
|
||||
console.log(dv.getUint32(4), dv.getInt32(4), dv.getUint8(7));
|
||||
dv.setInt32(4, -16, true);
|
||||
console.log(dv.getInt32(4, true), dv.getUint32(4, true), dv.getUint32(4));
|
||||
dv.setFloat64(0, 1.5);
|
||||
console.log(dv.getFloat64(0), dv.getUint8(0), dv.getUint8(1));
|
||||
dv.setFloat64(0, 1.5, true);
|
||||
console.log(dv.getFloat64(0, true), dv.getUint8(6), dv.getUint8(7));
|
||||
dv.setFloat32(4, 1.1);
|
||||
console.log(dv.getFloat32(4), dv.getFloat64(0, true));
|
||||
|
||||
// Value coercions: modular truncation on the integer kinds (NaN and
|
||||
// ±Infinity store 0), double→float rounding on Float32.
|
||||
dv.setUint8(0, 257.9);
|
||||
dv.setUint8(1, -1);
|
||||
dv.setUint8(2, 0 / 0);
|
||||
dv.setUint8(3, 1 / 0);
|
||||
console.log(dv.getUint8(0), dv.getUint8(1), dv.getUint8(2), dv.getUint8(3));
|
||||
dv.setUint16(0, 65536 + 7);
|
||||
console.log(dv.getUint16(0));
|
||||
dv.setInt16(0, -32769);
|
||||
console.log(dv.getInt16(0));
|
||||
dv.setUint32(0, 4294967296 + 41);
|
||||
console.log(dv.getUint32(0));
|
||||
dv.setFloat32(0, 0.1);
|
||||
console.log(dv.getFloat32(0));
|
||||
|
||||
// Fractional and NaN offsets go through ToIndex like the getters.
|
||||
dv.setUint8(5.7, 9);
|
||||
console.log(dv.getUint8(5));
|
||||
dv.setUint8(0 / 0, 42);
|
||||
console.log(dv.getUint8(0));
|
||||
|
||||
// The offset/length constructor args keep their runtime story, and a
|
||||
// view over x.buffer aliases x — setters write through.
|
||||
const backing = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]);
|
||||
const win = new DataView(backing.buffer, 2, 4);
|
||||
win.setUint16(0, 0x0910);
|
||||
console.log(win.byteLength, win.byteOffset, backing[2], backing[3], backing[4]);
|
||||
const dvo = new DataView(new ArrayBuffer(8), 6);
|
||||
console.log(dvo.byteLength, dvo.byteOffset);
|
||||
|
||||
// Bad setter offsets: Node's one constant RangeError, catchably.
|
||||
function caught(label: string, fn: () => void): void {
|
||||
try {
|
||||
fn();
|
||||
console.log(label, "ok");
|
||||
} catch (e) {
|
||||
if (e instanceof RangeError) console.log(label, "RangeError:", e.message);
|
||||
else console.log(label, "unexpected");
|
||||
}
|
||||
}
|
||||
caught("set-end", () => dv.setUint8(8, 1));
|
||||
caught("set-neg", () => dv.setUint8(-1, 1));
|
||||
caught("set-wide", () => dv.setFloat64(1, 1));
|
||||
caught("set-edge", () => dv.setFloat64(0, 2.5));
|
||||
caught("set-u32", () => dv.setUint32(5, 1));
|
||||
caught("set-inf", () => dv.setUint8(1 / 0, 1));
|
||||
console.log(dv.getFloat64(0));
|
||||
|
||||
// Constructor bounds on the fresh-buffer form throw like Node too.
|
||||
caught("ctor-off", () => { new DataView(new ArrayBuffer(4), 5); });
|
||||
caught("ctor-len", () => { new DataView(new ArrayBuffer(4), 1, 4); });
|
||||
@@ -0,0 +1,68 @@
|
||||
// Object.is — SameValue across the static kinds: the two number
|
||||
// divergences from === (NaN equals NaN, +0 differs from -0), strings by
|
||||
// bytes, booleans and units, reference kinds by pointer identity, union
|
||||
// operands per arm (the number arm's payload compare upgrades to
|
||||
// SameValue), and statically disjoint kinds answering false with the
|
||||
// operands still evaluated.
|
||||
console.log(Object.is(NaN, NaN), Object.is(0 / 0, NaN), Object.is(NaN, 1));
|
||||
console.log(Object.is(0, -0), Object.is(-0, -0), Object.is(0, 0), Object.is(-0, 0));
|
||||
console.log(Object.is(1, 1), Object.is(1, 2), Object.is(1.5, 3 / 2), Object.is(1 / 0, 1 / 0), Object.is(1 / 0, -1 / 0));
|
||||
console.log(Object.is("a", "a"), Object.is("a", "b"), Object.is("", ""), Object.is("ab", "a" + "b"));
|
||||
console.log(Object.is(true, true), Object.is(true, false), Object.is(false, false));
|
||||
console.log(Object.is(null, null), Object.is(undefined, undefined), Object.is(null, undefined), Object.is(undefined, null));
|
||||
|
||||
// Mixed kinds: always false, and JS still evaluates both arguments.
|
||||
let hits = 0;
|
||||
function n(): number { hits++; return 0; }
|
||||
function s(): string { hits++; return "0"; }
|
||||
console.log(Object.is(n(), s()), hits);
|
||||
console.log(Object.is(1, "1"), Object.is(false, 0), Object.is("", 0), Object.is(null, 0), Object.is(undefined, ""));
|
||||
|
||||
// Reference identity: arrays, records, functions, Maps/Sets, class
|
||||
// instances (including through a base-typed slot), and class values.
|
||||
const arr = [1, 2];
|
||||
const arr2 = [1, 2];
|
||||
console.log(Object.is(arr, arr), Object.is(arr, arr2));
|
||||
const o = { x: 1 };
|
||||
console.log(Object.is(o, o), Object.is(o, { x: 1 }));
|
||||
const f = (): number => 1;
|
||||
const g = (): number => 2;
|
||||
console.log(Object.is(f, f), Object.is(f, g));
|
||||
const m = new Map<string, number>();
|
||||
console.log(Object.is(m, m), Object.is(m, new Map<string, number>()));
|
||||
const st = new Set<number>([1]);
|
||||
console.log(Object.is(st, st), Object.is(st, new Set<number>([1])));
|
||||
class Base { tag = "b"; }
|
||||
class Derived extends Base { extra = 1; }
|
||||
const d = new Derived();
|
||||
const asBase: Base = d;
|
||||
const otherBase = new Base();
|
||||
console.log(Object.is(d, d), Object.is(asBase, d), Object.is(asBase, otherBase));
|
||||
console.log(Object.is(Base, Base), Object.is(Base, Derived));
|
||||
|
||||
// Unions: SameValue per arm — NaN and ±0 payloads answer the spec, ref
|
||||
// arms compare identity, and a plain side wraps like ===.
|
||||
function u(v: number | string | null): number | string | null { return v; }
|
||||
const a1 = u(NaN);
|
||||
const b1 = u(NaN);
|
||||
console.log(Object.is(a1, b1), a1 === b1);
|
||||
const z1 = u(0);
|
||||
const z2 = u(-0);
|
||||
console.log(Object.is(z1, z2), z1 === z2, Object.is(z2, u(-0)));
|
||||
console.log(Object.is(u("hi"), u("hi")), Object.is(u("hi"), u(7)), Object.is(u(null), u(null)), Object.is(u(null), u(0)));
|
||||
console.log(Object.is(u(3), 3), Object.is(3, u(3)), Object.is(u("x"), "x"), Object.is(u(null), null), Object.is(u(3), 4));
|
||||
console.log(Object.is(u(NaN), NaN), Object.is(u(0), -0), Object.is(u(-0), -0));
|
||||
// A primitive the union cannot hold: constant false.
|
||||
console.log(Object.is(u(3), true));
|
||||
const ru: number[] | null = arr;
|
||||
console.log(Object.is(ru, arr), Object.is(ru, null));
|
||||
|
||||
// Object.is drives control flow like any boolean.
|
||||
const vals = [0, -0, NaN, 1];
|
||||
let negZeros = 0;
|
||||
let nans = 0;
|
||||
for (const v of vals) {
|
||||
if (Object.is(v, -0)) negZeros++;
|
||||
if (Object.is(v, NaN)) nans++;
|
||||
}
|
||||
console.log(negZeros, nans);
|
||||
@@ -0,0 +1,41 @@
|
||||
// The composed en-US Intl.NumberFormat forms with default options —
|
||||
// new Intl.NumberFormat("en-US").format(x), the callable spelling, and
|
||||
// x.toLocaleString("en-US"). Rounding is half-up on the SHORTEST
|
||||
// round-tripping decimal (ICU's rounding input): format(1.0005) is
|
||||
// "1.001" although toFixed(3) answers "1.000" on the same double, and
|
||||
// 1e23 prints the shortest form's zeros, not the double's exact
|
||||
// expansion. Grouping, ±0, NaN/∞ texts, and the tiny/huge edges pin
|
||||
// byte-identically against Node.
|
||||
const nf = (x: number): string => new Intl.NumberFormat("en-US").format(x);
|
||||
|
||||
// Grouping and plain decimals.
|
||||
console.log(nf(0), nf(1), nf(100), nf(1000), nf(10000), nf(999999), nf(-1000000));
|
||||
console.log(nf(1234567.891), nf(-1234567.891), nf(123456789.9999), nf(0.5), nf(-0.4));
|
||||
|
||||
// The shortest-decimal rounding discriminators — (1.0005).toFixed(3)
|
||||
// would answer "1.000" (exact-value rounding); format answers "1.001".
|
||||
console.log(nf(1.0005), nf(1.0015), nf(123.4565), nf(7.995));
|
||||
console.log(nf(0.0625), nf(999.9995), nf(0.1 + 0.2), nf(1.100000023841858));
|
||||
|
||||
// Signs, zeros, and the non-finite texts.
|
||||
console.log(nf(-0), nf(NaN), nf(1 / 0), nf(-1 / 0), nf(-2.5));
|
||||
|
||||
// Tiny values collapse to ±0 or the smallest kept fraction digit.
|
||||
console.log(nf(5e-4), nf(-5e-4), nf(0.00049), nf(1e-7), nf(-1e-7), nf(5e-324));
|
||||
|
||||
// Huge values keep full integer digits, grouped — the shortest form's
|
||||
// zeros for 1e23 (the exact expansion would end ...611,392).
|
||||
console.log(nf(1e21), nf(1e23), nf(9007199254740993));
|
||||
console.log(nf(1.7976931348623157e308));
|
||||
|
||||
// The callable spelling constructs the same formatter.
|
||||
console.log(Intl.NumberFormat("en-US").format(12345.678));
|
||||
|
||||
// Number.prototype.toLocaleString("en-US") is the same operation.
|
||||
console.log((1234567.891).toLocaleString("en-US"), (0.0625).toLocaleString("en-US"), (-0).toLocaleString("en-US"));
|
||||
const xs = [1234.5, -9876543.21, 0.5, 1e6];
|
||||
console.log(xs.map((x) => x.toLocaleString("en-US")).join("|"));
|
||||
|
||||
// Composed results are ordinary strings.
|
||||
const label = `total: ${nf(1234.5)} units`;
|
||||
console.log(label, label.length, nf(42).padStart(8, "."));
|
||||
@@ -10,6 +10,10 @@ const ab = new ArrayBuffer(8, { maxByteLength: 16 });
|
||||
ab.resize(16);
|
||||
const moved = ab.transfer();
|
||||
|
||||
// The resizable form fences by name even in an erasure position (the
|
||||
// fixed-length new Uint8Array(new ArrayBuffer(8)) lowers).
|
||||
const rz = new Uint8Array(new ArrayBuffer(8, { maxByteLength: 16 }));
|
||||
|
||||
// SharedArrayBuffer growth.
|
||||
const sab = new SharedArrayBuffer(8, { maxByteLength: 16 });
|
||||
sab.grow(16);
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
// The Intl surface. What lowers: the COMPOSED en-US NumberFormat forms
|
||||
// with default options — new Intl.NumberFormat("en-US").format(x) and
|
||||
// x.toLocaleString("en-US"). Everything else fences by NAME: the
|
||||
// environment-default locale a compiled binary cannot carry, other
|
||||
// locales (ICU data the binary does not embed), options bags, formatter
|
||||
// VALUES, and the rest of the Intl namespace (DurationFormat,
|
||||
// DateTimeFormat, PluralRules, getCanonicalLocales).
|
||||
|
||||
// The unlowered NumberFormat variants, each with its own reason.
|
||||
const noLocale = new Intl.NumberFormat().format(1234.5);
|
||||
const otherLocale = new Intl.NumberFormat("de-DE").format(1234.5);
|
||||
const withOptions = new Intl.NumberFormat("en-US", { maximumFractionDigits: 2 }).format(1234.5);
|
||||
const hoisted = new Intl.NumberFormat("en-US");
|
||||
const viaValue = hoisted.format(1234.5);
|
||||
|
||||
// toLocaleString's unlowered variants.
|
||||
const envDefault = (1234.5).toLocaleString();
|
||||
const german = (1234.5).toLocaleString("de-DE");
|
||||
const grouped = (1234.5).toLocaleString("en-US", { useGrouping: false });
|
||||
|
||||
// The rest of Intl keeps the ICU story.
|
||||
const when = new Intl.DateTimeFormat("en-US").format();
|
||||
const dur = new Intl.DurationFormat("en-US").format({ hours: 1, minutes: 23 });
|
||||
const rule = new Intl.PluralRules("en-US").select(1);
|
||||
const canon = Intl.getCanonicalLocales("en-us");
|
||||
@@ -67,4 +67,13 @@ const thened = work().then((v) => v + 1, () => 0);
|
||||
const caught = work().catch(() => 0);
|
||||
const resolved = Promise.resolve(1);
|
||||
|
||||
// Object.is lowers over one comparable kind; the unlowered forms fence
|
||||
// by name — a validate-first dynamic operand and partially-overlapping
|
||||
// unions needing narrowing.
|
||||
const dynIs = Object.is(JSON.parse("1") as unknown, 1);
|
||||
function pick(a: string | number, b: number | boolean): boolean {
|
||||
return Object.is(a, b); // partially-overlapping unions: narrow first
|
||||
}
|
||||
const picked = pick(1, 2);
|
||||
|
||||
const big = 10n;
|
||||
|
||||
@@ -5,7 +5,7 @@ dataview.ts:9:7 - error SC2020: 'ArrayBuffer' is part of the standard library ty
|
||||
| ^~~~~~~
|
||||
10 |
|
||||
|
||||
hint: the type checker sees the full standard library, but only the supported surface compiles (https://scriptc.dev/limitations)
|
||||
hint: no free-standing ArrayBuffer value exists — typed arrays own their storage (new Uint8Array(n) allocates; new Uint8Array(new ArrayBuffer(n)) erases the buffer into the view)
|
||||
|
||||
dataview.ts:9:17 - error SC1090: '.buffer' outside new DataView(x.buffer, ...) — scriptc typed arrays own their storage: no free-standing ArrayBuffer value exists, and slice()/subarray() copy is not supported yet
|
||||
|
||||
@@ -37,7 +37,7 @@ dataview.ts:15:7 - error SC2020: 'ArrayBuffer' is part of the standard library t
|
||||
| ^~
|
||||
16 | const direct = new DataView(ab);
|
||||
|
||||
hint: the type checker sees the full standard library, but only the supported surface compiles (https://scriptc.dev/limitations)
|
||||
hint: no free-standing ArrayBuffer value exists — typed arrays own their storage (new Uint8Array(n) allocates; new Uint8Array(new ArrayBuffer(n)) erases the buffer into the view)
|
||||
|
||||
dataview.ts:15:12 - error SC2020: 'new ArrayBuffer' is part of the standard library types but has no scriptc lowering yet
|
||||
|
||||
@@ -46,7 +46,7 @@ dataview.ts:15:12 - error SC2020: 'new ArrayBuffer' is part of the standard libr
|
||||
| ^~~~~~~~~~~~~~~~~~
|
||||
16 | const direct = new DataView(ab);
|
||||
|
||||
hint: the type checker sees the full standard library, but only the supported surface compiles (https://scriptc.dev/limitations)
|
||||
hint: no free-standing ArrayBuffer value exists — typed arrays own their storage: allocate the view directly (new Uint8Array(n)), or erase a fresh buffer into one (new Uint8Array(new ArrayBuffer(n)), new DataView(new ArrayBuffer(n), ...))
|
||||
|
||||
dataview.ts:16:29 - error SC2020: 'new DataView over this buffer expression' is part of the standard library types but has no scriptc lowering yet
|
||||
|
||||
@@ -55,4 +55,4 @@ dataview.ts:16:29 - error SC2020: 'new DataView over this buffer expression' is
|
||||
| ^~
|
||||
17 |
|
||||
|
||||
hint: views compile over a typed array's own storage — new DataView(x.buffer, byteOffset?, byteLength?) where x is a Uint8Array/Uint32Array/Float32Array/Buffer value; free-standing ArrayBuffers have no representation
|
||||
hint: views compile over a typed array's own storage — new DataView(x.buffer, byteOffset?, byteLength?) where x is a Uint8Array/Uint32Array/Float32Array/Buffer value — or a fresh buffer erased into the view: new DataView(new ArrayBuffer(n), ...); free-standing ArrayBuffers have no representation
|
||||
@@ -5,7 +5,7 @@ es2025-fences.ts:9:7 - error SC2020: 'ArrayBuffer' is part of the standard libra
|
||||
| ^~
|
||||
10 | ab.resize(16);
|
||||
|
||||
hint: the type checker sees the full standard library, but only the supported surface compiles (https://scriptc.dev/limitations)
|
||||
hint: no free-standing ArrayBuffer value exists — typed arrays own their storage (new Uint8Array(n) allocates; new Uint8Array(new ArrayBuffer(n)) erases the buffer into the view)
|
||||
|
||||
es2025-fences.ts:9:12 - error SC2020: 'new ArrayBuffer' is part of the standard library types but has no scriptc lowering yet
|
||||
|
||||
@@ -14,7 +14,7 @@ es2025-fences.ts:9:12 - error SC2020: 'new ArrayBuffer' is part of the standard
|
||||
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
10 | ab.resize(16);
|
||||
|
||||
hint: the type checker sees the full standard library, but only the supported surface compiles (https://scriptc.dev/limitations)
|
||||
hint: no free-standing ArrayBuffer value exists — typed arrays own their storage: allocate the view directly (new Uint8Array(n)), or erase a fresh buffer into one (new Uint8Array(new ArrayBuffer(n)), new DataView(new ArrayBuffer(n), ...))
|
||||
|
||||
es2025-fences.ts:10:1 - error SC2020: 'ArrayBuffer.resize' is part of the standard library types but has no scriptc lowering yet
|
||||
|
||||
@@ -23,7 +23,7 @@ es2025-fences.ts:10:1 - error SC2020: 'ArrayBuffer.resize' is part of the standa
|
||||
| ^~~~~~~~~
|
||||
11 | const moved = ab.transfer();
|
||||
|
||||
hint: the type checker sees the full standard library, but only the supported surface compiles (https://scriptc.dev/limitations)
|
||||
hint: resize/transfer/maxByteLength need the buffer to exist as a runtime value, and no free-standing ArrayBuffer value does — typed arrays own fixed-length storage (new Uint8Array(n)); allocate a new view and copy instead
|
||||
|
||||
es2025-fences.ts:11:7 - error SC2020: 'ArrayBuffer' is part of the standard library types but has no scriptc lowering yet
|
||||
|
||||
@@ -32,7 +32,7 @@ es2025-fences.ts:11:7 - error SC2020: 'ArrayBuffer' is part of the standard libr
|
||||
| ^~~~~
|
||||
12 |
|
||||
|
||||
hint: the type checker sees the full standard library, but only the supported surface compiles (https://scriptc.dev/limitations)
|
||||
hint: no free-standing ArrayBuffer value exists — typed arrays own their storage (new Uint8Array(n) allocates; new Uint8Array(new ArrayBuffer(n)) erases the buffer into the view)
|
||||
|
||||
es2025-fences.ts:11:15 - error SC2020: 'ArrayBuffer.transfer' is part of the standard library types but has no scriptc lowering yet
|
||||
|
||||
@@ -41,130 +41,139 @@ es2025-fences.ts:11:15 - error SC2020: 'ArrayBuffer.transfer' is part of the sta
|
||||
| ^~~~~~~~~~~
|
||||
12 |
|
||||
|
||||
hint: the type checker sees the full standard library, but only the supported surface compiles (https://scriptc.dev/limitations)
|
||||
hint: resize/transfer/maxByteLength need the buffer to exist as a runtime value, and no free-standing ArrayBuffer value does — typed arrays own fixed-length storage (new Uint8Array(n)); allocate a new view and copy instead
|
||||
|
||||
es2025-fences.ts:14:7 - error SC2020: 'SharedArrayBuffer' is part of the standard library types but has no scriptc lowering yet
|
||||
es2025-fences.ts:15:27 - error SC2020: 'new Uint8Array over a resizable ArrayBuffer' is part of the standard library types but has no scriptc lowering yet
|
||||
|
||||
13 | // SharedArrayBuffer growth.
|
||||
14 | const sab = new SharedArrayBuffer(8, { maxByteLength: 16 });
|
||||
14 | // fixed-length new Uint8Array(new ArrayBuffer(8)) lowers).
|
||||
15 | const rz = new Uint8Array(new ArrayBuffer(8, { maxByteLength: 16 }));
|
||||
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
16 |
|
||||
|
||||
hint: a maxByteLength options bag makes the buffer resizable, and resizing needs the buffer to exist as a runtime value — no free-standing ArrayBuffer value exists (the buffer here erases into the view): drop the options bag
|
||||
|
||||
es2025-fences.ts:18:7 - error SC2020: 'SharedArrayBuffer' is part of the standard library types but has no scriptc lowering yet
|
||||
|
||||
17 | // SharedArrayBuffer growth.
|
||||
18 | const sab = new SharedArrayBuffer(8, { maxByteLength: 16 });
|
||||
| ^~~
|
||||
15 | sab.grow(16);
|
||||
|
||||
hint: the type checker sees the full standard library, but only the supported surface compiles (https://scriptc.dev/limitations)
|
||||
|
||||
es2025-fences.ts:14:13 - error SC2020: 'new SharedArrayBuffer' is part of the standard library types but has no scriptc lowering yet
|
||||
|
||||
13 | // SharedArrayBuffer growth.
|
||||
14 | const sab = new SharedArrayBuffer(8, { maxByteLength: 16 });
|
||||
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
15 | sab.grow(16);
|
||||
19 | sab.grow(16);
|
||||
|
||||
hint: no shared-memory threads exist in a compiled program — Uint8Array is the byte storage
|
||||
|
||||
es2025-fences.ts:15:1 - error SC2020: 'SharedArrayBuffer.grow' is part of the standard library types but has no scriptc lowering yet
|
||||
es2025-fences.ts:18:13 - error SC2020: 'new SharedArrayBuffer' is part of the standard library types but has no scriptc lowering yet
|
||||
|
||||
14 | const sab = new SharedArrayBuffer(8, { maxByteLength: 16 });
|
||||
15 | sab.grow(16);
|
||||
17 | // SharedArrayBuffer growth.
|
||||
18 | const sab = new SharedArrayBuffer(8, { maxByteLength: 16 });
|
||||
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
19 | sab.grow(16);
|
||||
|
||||
hint: no shared-memory threads exist in a compiled program — Uint8Array is the byte storage
|
||||
|
||||
es2025-fences.ts:19:1 - error SC2020: 'SharedArrayBuffer.grow' is part of the standard library types but has no scriptc lowering yet
|
||||
|
||||
18 | const sab = new SharedArrayBuffer(8, { maxByteLength: 16 });
|
||||
19 | sab.grow(16);
|
||||
| ^~~~~~~~
|
||||
16 |
|
||||
|
||||
hint: the type checker sees the full standard library, but only the supported surface compiles (https://scriptc.dev/limitations)
|
||||
|
||||
es2025-fences.ts:19:7 - error SC2009: values of type '{ async: false; value: "not-equal" | "timed-out"; } | { async: true; value: Promise<"ok" | "timed-out">; }' cannot be compiled: the union shape is supported, but its arm '{ async: false; value: "not-equal" | "timed-out"; }' does not compile
|
||||
|
||||
18 | const ia = new Int32Array(4);
|
||||
19 | const waited = Atomics.waitAsync(ia, 0, 0, 1);
|
||||
| ^~~~~~
|
||||
20 |
|
||||
|
||||
hint: no shared-memory threads exist in a compiled program — Uint8Array is the byte storage (a fixed-length allocation: grow has nothing to share it with)
|
||||
|
||||
es2025-fences.ts:23:7 - error SC2009: values of type '{ async: false; value: "not-equal" | "timed-out"; } | { async: true; value: Promise<"ok" | "timed-out">; }' cannot be compiled: the union shape is supported, but its arm '{ async: false; value: "not-equal" | "timed-out"; }' does not compile
|
||||
|
||||
22 | const ia = new Int32Array(4);
|
||||
23 | const waited = Atomics.waitAsync(ia, 0, 0, 1);
|
||||
| ^~~~~~
|
||||
24 |
|
||||
|
||||
hint: the outer shape is supported — the named component is the blocker; restate it inside the compilable set (number, string, boolean, arrays, Maps, Sets, RegExp, functions, classes, records, unions of those, and 'unknown')
|
||||
|
||||
es2025-fences.ts:19:16 - error SC2020: 'Atomics.waitAsync' is part of the standard library types but has no scriptc lowering yet
|
||||
es2025-fences.ts:23:16 - error SC2020: 'Atomics.waitAsync' is part of the standard library types but has no scriptc lowering yet
|
||||
|
||||
18 | const ia = new Int32Array(4);
|
||||
19 | const waited = Atomics.waitAsync(ia, 0, 0, 1);
|
||||
22 | const ia = new Int32Array(4);
|
||||
23 | const waited = Atomics.waitAsync(ia, 0, 0, 1);
|
||||
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
20 |
|
||||
24 |
|
||||
|
||||
hint: Atomics.wait(int32Array, idx, expected, timeoutMs) is the supported Atomics surface (scriptc has no threads — wait is the synchronous-sleep idiom, and nothing else has anyone to race)
|
||||
|
||||
es2025-fences.ts:22:7 - error SC2020: 'Float16Array<ArrayBuffer>' is part of the standard library types but has no scriptc lowering yet
|
||||
es2025-fences.ts:26:7 - error SC2020: 'Float16Array<ArrayBuffer>' is part of the standard library types but has no scriptc lowering yet
|
||||
|
||||
21 | // Float16 (ES2025): the typed array, Math.f16round, DataView accessors.
|
||||
22 | const f16 = new Float16Array(4);
|
||||
25 | // Float16 (ES2025): the typed array, Math.f16round, DataView accessors.
|
||||
26 | const f16 = new Float16Array(4);
|
||||
| ^~~
|
||||
23 | console.log(Math.f16round(1.337));
|
||||
27 | console.log(Math.f16round(1.337));
|
||||
|
||||
hint: the type checker sees the full standard library, but only the supported surface compiles (https://scriptc.dev/limitations)
|
||||
|
||||
es2025-fences.ts:22:13 - error SC2020: 'new Float16Array' is part of the standard library types but has no scriptc lowering yet
|
||||
es2025-fences.ts:26:13 - error SC2020: 'new Float16Array' is part of the standard library types but has no scriptc lowering yet
|
||||
|
||||
21 | // Float16 (ES2025): the typed array, Math.f16round, DataView accessors.
|
||||
22 | const f16 = new Float16Array(4);
|
||||
25 | // Float16 (ES2025): the typed array, Math.f16round, DataView accessors.
|
||||
26 | const f16 = new Float16Array(4);
|
||||
| ^~~~~~~~~~~~~~~~~~~
|
||||
23 | console.log(Math.f16round(1.337));
|
||||
27 | console.log(Math.f16round(1.337));
|
||||
|
||||
hint: the type checker sees the full standard library, but only the supported surface compiles (https://scriptc.dev/limitations)
|
||||
|
||||
es2025-fences.ts:23:13 - error SC2020: 'Math.f16round' is part of the standard library types but has no scriptc lowering yet
|
||||
es2025-fences.ts:27:13 - error SC2020: 'Math.f16round' is part of the standard library types but has no scriptc lowering yet
|
||||
|
||||
22 | const f16 = new Float16Array(4);
|
||||
23 | console.log(Math.f16round(1.337));
|
||||
26 | const f16 = new Float16Array(4);
|
||||
27 | console.log(Math.f16round(1.337));
|
||||
| ^~~~~~~~~~~~~
|
||||
24 | const dv = new DataView(new Uint8Array(8).buffer);
|
||||
28 | const dv = new DataView(new Uint8Array(8).buffer);
|
||||
|
||||
hint: the type checker sees the full standard library, but only the supported surface compiles (https://scriptc.dev/limitations)
|
||||
|
||||
es2025-fences.ts:25:1 - error SC2020: 'DataView<ArrayBuffer>.setFloat16' is part of the standard library types but has no scriptc lowering yet
|
||||
es2025-fences.ts:29:1 - error SC2020: 'DataView<ArrayBuffer>.setFloat16' is part of the standard library types but has no scriptc lowering yet
|
||||
|
||||
24 | const dv = new DataView(new Uint8Array(8).buffer);
|
||||
25 | dv.setFloat16(0, 1.5);
|
||||
28 | const dv = new DataView(new Uint8Array(8).buffer);
|
||||
29 | dv.setFloat16(0, 1.5);
|
||||
| ^~~~~~~~~~~~~
|
||||
26 |
|
||||
30 |
|
||||
|
||||
hint: the type checker sees the full standard library, but only the supported surface compiles (https://scriptc.dev/limitations)
|
||||
|
||||
es2025-fences.ts:28:7 - error SC2020: 'IteratorObject<number, undefined, unknown>' is part of the standard library types but has no scriptc lowering yet
|
||||
es2025-fences.ts:32:7 - error SC2020: 'IteratorObject<number, undefined, unknown>' is part of the standard library types but has no scriptc lowering yet
|
||||
|
||||
27 | // First-class iterator objects.
|
||||
28 | const it = Iterator.from([1, 2, 3].values());
|
||||
31 | // First-class iterator objects.
|
||||
32 | const it = Iterator.from([1, 2, 3].values());
|
||||
| ^~
|
||||
29 |
|
||||
33 |
|
||||
|
||||
hint: the type checker sees the full standard library, but only the supported surface compiles (https://scriptc.dev/limitations)
|
||||
|
||||
es2025-fences.ts:28:12 - error SC2020: 'Iterator.from' is part of the standard library types but has no scriptc lowering yet
|
||||
es2025-fences.ts:32:12 - error SC2020: 'Iterator.from' is part of the standard library types but has no scriptc lowering yet
|
||||
|
||||
27 | // First-class iterator objects.
|
||||
28 | const it = Iterator.from([1, 2, 3].values());
|
||||
31 | // First-class iterator objects.
|
||||
32 | const it = Iterator.from([1, 2, 3].values());
|
||||
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
29 |
|
||||
33 |
|
||||
|
||||
hint: first-class iterator objects have no lowering — iterator helpers compile as one chain on an array iterator, consumed in place: arr.values().map(f).take(n).toArray()
|
||||
|
||||
es2025-fences.ts:31:15 - error SC2020: 'Promise.try with 3 arguments' is part of the standard library types but has no scriptc lowering yet
|
||||
es2025-fences.ts:35:15 - error SC2020: 'Promise.try with 3 arguments' is part of the standard library types but has no scriptc lowering yet
|
||||
|
||||
30 | // Promise.try's ...args form (close over the values instead).
|
||||
31 | const tried = Promise.try((a: number, b: number) => a + b, 1, 2);
|
||||
34 | // Promise.try's ...args form (close over the values instead).
|
||||
35 | const tried = Promise.try((a: number, b: number) => a + b, 1, 2);
|
||||
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
32 |
|
||||
36 |
|
||||
|
||||
hint: the ...args form has no lowering — close over the values: Promise.try(() => f(a, b))
|
||||
|
||||
es2025-fences.ts:34:17 - error SC2020: 'Object.groupBy at this result type' is part of the standard library types but has no scriptc lowering yet
|
||||
es2025-fences.ts:38:17 - error SC2020: 'Object.groupBy at this result type' is part of the standard library types but has no scriptc lowering yet
|
||||
|
||||
33 | // Object.groupBy at a literal-union key type (a fixed-field record).
|
||||
34 | const grouped = Object.groupBy([1, 2, 3], (n) => (n % 2 === 0 ? "even" : "odd"));
|
||||
37 | // Object.groupBy at a literal-union key type (a fixed-field record).
|
||||
38 | const grouped = Object.groupBy([1, 2, 3], (n) => (n % 2 === 0 ? "even" : "odd"));
|
||||
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
35 |
|
||||
39 |
|
||||
|
||||
hint: a string-keyed result (Partial<Record<string, T[]>>) is the supported form — a literal-union key type makes a fixed-field record: widen the callback's key type to string (numbers stringify like JS property keys)
|
||||
|
||||
es2025-fences.ts:39:19 - error SC2020: 'Set.prototype.union over a set-like argument of type 'Map<number, string>'' is part of the standard library types but has no scriptc lowering yet
|
||||
es2025-fences.ts:43:19 - error SC2020: 'Set.prototype.union over a set-like argument of type 'Map<number, string>'' is part of the standard library types but has no scriptc lowering yet
|
||||
|
||||
38 | const m = new Map<number, string>([[1, "x"]]);
|
||||
39 | const u = s.union(m);
|
||||
42 | const m = new Map<number, string>([[1, "x"]]);
|
||||
43 | const u = s.union(m);
|
||||
| ^
|
||||
40 |
|
||||
44 |
|
||||
|
||||
hint: only a real Set lowers (the argument's has/size must be the builtins) — build one first: new Set(...)
|
||||
@@ -0,0 +1,114 @@
|
||||
intl.ts:10:18 - error SC2020: 'Intl.NumberFormat without a locale' is part of the standard library types but has no scriptc lowering yet
|
||||
|
||||
9 | // The unlowered NumberFormat variants, each with its own reason.
|
||||
10 | const noLocale = new Intl.NumberFormat().format(1234.5);
|
||||
| ^~~~~~~~~~~~~~~~~~~~~~~
|
||||
11 | const otherLocale = new Intl.NumberFormat("de-DE").format(1234.5);
|
||||
|
||||
hint: the default locale is the host environment's, which a compiled binary cannot carry — pass it explicitly: new Intl.NumberFormat("en-US").format(x)
|
||||
|
||||
intl.ts:11:43 - error SC2020: 'Intl.NumberFormat at locale "de-DE"' is part of the standard library types but has no scriptc lowering yet
|
||||
|
||||
10 | const noLocale = new Intl.NumberFormat().format(1234.5);
|
||||
11 | const otherLocale = new Intl.NumberFormat("de-DE").format(1234.5);
|
||||
| ^~~~~~~
|
||||
12 | const withOptions = new Intl.NumberFormat("en-US", { maximumFractionDigits: 2 }).format(1234.5);
|
||||
|
||||
hint: "en-US" (Node's default-build locale) is the one locale whose data the runtime embeds — everything else is ICU data the binary does not carry
|
||||
|
||||
intl.ts:12:52 - error SC2020: 'Intl.NumberFormat with an options bag' is part of the standard library types but has no scriptc lowering yet
|
||||
|
||||
11 | const otherLocale = new Intl.NumberFormat("de-DE").format(1234.5);
|
||||
12 | const withOptions = new Intl.NumberFormat("en-US", { maximumFractionDigits: 2 }).format(1234.5);
|
||||
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
13 | const hoisted = new Intl.NumberFormat("en-US");
|
||||
|
||||
hint: the embedded data covers DEFAULT options only (decimal notation, up to 3 fraction digits, grouping) — new Intl.NumberFormat("en-US").format(x)
|
||||
|
||||
intl.ts:13:7 - error SC2020: 'NumberFormat' is part of the standard library types but has no scriptc lowering yet
|
||||
|
||||
12 | const withOptions = new Intl.NumberFormat("en-US", { maximumFractionDigits: 2 }).format(1234.5);
|
||||
13 | const hoisted = new Intl.NumberFormat("en-US");
|
||||
| ^~~~~~~
|
||||
14 | const viaValue = hoisted.format(1234.5);
|
||||
|
||||
hint: Intl formatter values have no representation — the COMPOSED en-US forms lower: new Intl.NumberFormat("en-US").format(x) and x.toLocaleString("en-US") with default options; the rest is ICU locale data the binary does not carry
|
||||
|
||||
intl.ts:13:17 - error SC1090: constructing values other than classes declared in the program is not supported yet
|
||||
|
||||
12 | const withOptions = new Intl.NumberFormat("en-US", { maximumFractionDigits: 2 }).format(1234.5);
|
||||
13 | const hoisted = new Intl.NumberFormat("en-US");
|
||||
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
14 | const viaValue = hoisted.format(1234.5);
|
||||
|
||||
intl.ts:14:18 - error SC2020: 'NumberFormat.format' is part of the standard library types but has no scriptc lowering yet
|
||||
|
||||
13 | const hoisted = new Intl.NumberFormat("en-US");
|
||||
14 | const viaValue = hoisted.format(1234.5);
|
||||
| ^~~~~~~~~~~~~~
|
||||
15 |
|
||||
|
||||
hint: locale- and ICU-backed behavior lives outside the static runtime (the localeCompare stance: code-unit order, no collation/locale data) — what lowers: the composed new Intl.NumberFormat("en-US").format(x) and x.toLocaleString("en-US") with default options; format with template literals, toFixed, and toString otherwise
|
||||
|
||||
intl.ts:17:20 - error SC2020: 'Number.prototype.toLocaleString without a locale' is part of the standard library types but has no scriptc lowering yet
|
||||
|
||||
16 | // toLocaleString's unlowered variants.
|
||||
17 | const envDefault = (1234.5).toLocaleString();
|
||||
| ^~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
18 | const german = (1234.5).toLocaleString("de-DE");
|
||||
|
||||
hint: the default locale is the host environment's, which a compiled binary cannot carry — pass it explicitly: x.toLocaleString("en-US")
|
||||
|
||||
intl.ts:18:40 - error SC2020: 'Number.prototype.toLocaleString at locale "de-DE"' is part of the standard library types but has no scriptc lowering yet
|
||||
|
||||
17 | const envDefault = (1234.5).toLocaleString();
|
||||
18 | const german = (1234.5).toLocaleString("de-DE");
|
||||
| ^~~~~~~
|
||||
19 | const grouped = (1234.5).toLocaleString("en-US", { useGrouping: false });
|
||||
|
||||
hint: "en-US" (Node's default-build locale) is the one locale whose data the runtime embeds — everything else is ICU data the binary does not carry
|
||||
|
||||
intl.ts:19:17 - error SC2020: 'Number.prototype.toLocaleString with an options bag' is part of the standard library types but has no scriptc lowering yet
|
||||
|
||||
18 | const german = (1234.5).toLocaleString("de-DE");
|
||||
19 | const grouped = (1234.5).toLocaleString("en-US", { useGrouping: false });
|
||||
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
20 |
|
||||
|
||||
hint: the embedded data covers DEFAULT options only (decimal notation, up to 3 fraction digits, grouping) — x.toLocaleString("en-US")
|
||||
|
||||
intl.ts:22:14 - error SC2020: 'DateTimeFormat.format' is part of the standard library types but has no scriptc lowering yet
|
||||
|
||||
21 | // The rest of Intl keeps the ICU story.
|
||||
22 | const when = new Intl.DateTimeFormat("en-US").format();
|
||||
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
23 | const dur = new Intl.DurationFormat("en-US").format({ hours: 1, minutes: 23 });
|
||||
|
||||
hint: locale- and ICU-backed behavior lives outside the static runtime (the localeCompare stance: code-unit order, no collation/locale data) — what lowers: the composed new Intl.NumberFormat("en-US").format(x) and x.toLocaleString("en-US") with default options; format with template literals, toFixed, and toString otherwise
|
||||
|
||||
intl.ts:23:13 - error SC2020: 'DurationFormat.format' is part of the standard library types but has no scriptc lowering yet
|
||||
|
||||
22 | const when = new Intl.DateTimeFormat("en-US").format();
|
||||
23 | const dur = new Intl.DurationFormat("en-US").format({ hours: 1, minutes: 23 });
|
||||
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
24 | const rule = new Intl.PluralRules("en-US").select(1);
|
||||
|
||||
hint: locale- and ICU-backed behavior lives outside the static runtime (the localeCompare stance: code-unit order, no collation/locale data) — what lowers: the composed new Intl.NumberFormat("en-US").format(x) and x.toLocaleString("en-US") with default options; format with template literals, toFixed, and toString otherwise
|
||||
|
||||
intl.ts:24:14 - error SC2020: 'PluralRules.select' is part of the standard library types but has no scriptc lowering yet
|
||||
|
||||
23 | const dur = new Intl.DurationFormat("en-US").format({ hours: 1, minutes: 23 });
|
||||
24 | const rule = new Intl.PluralRules("en-US").select(1);
|
||||
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
25 | const canon = Intl.getCanonicalLocales("en-us");
|
||||
|
||||
hint: locale- and ICU-backed behavior lives outside the static runtime (the localeCompare stance: code-unit order, no collation/locale data) — what lowers: the composed new Intl.NumberFormat("en-US").format(x) and x.toLocaleString("en-US") with default options; format with template literals, toFixed, and toString otherwise
|
||||
|
||||
intl.ts:25:15 - error SC2020: 'Intl.getCanonicalLocales' is part of the standard library types but has no scriptc lowering yet
|
||||
|
||||
24 | const rule = new Intl.PluralRules("en-US").select(1);
|
||||
25 | const canon = Intl.getCanonicalLocales("en-us");
|
||||
| ^~~~~~~~~~~~~~~~~~~~~~~~
|
||||
26 |
|
||||
|
||||
hint: locale- and ICU-backed behavior lives outside the static runtime (the localeCompare stance: code-unit order, no collation/locale data) — what lowers: the composed new Intl.NumberFormat("en-US").format(x) and x.toLocaleString("en-US") with default options; format with template literals, toFixed, and toString otherwise
|
||||
@@ -48,7 +48,7 @@ stdlib-fence.ts:20:7 - error SC2020: 'NumberFormat' is part of the standard libr
|
||||
| ^~~
|
||||
21 |
|
||||
|
||||
hint: the type checker sees the full standard library, but only the supported surface compiles (https://scriptc.dev/limitations)
|
||||
hint: Intl formatter values have no representation — the COMPOSED en-US forms lower: new Intl.NumberFormat("en-US").format(x) and x.toLocaleString("en-US") with default options; the rest is ICU locale data the binary does not carry
|
||||
|
||||
stdlib-fence.ts:20:13 - error SC1090: constructing values other than classes declared in the program is not supported yet
|
||||
|
||||
@@ -109,7 +109,7 @@ stdlib-fence.ts:26:7 - error SC2020: 'ArrayBuffer' is part of the standard libra
|
||||
| ^~~
|
||||
27 | const st = new Set(new Set([1, 2])); // array seeds lower; Set/iterable seeds stay fenced
|
||||
|
||||
hint: the type checker sees the full standard library, but only the supported surface compiles (https://scriptc.dev/limitations)
|
||||
hint: no free-standing ArrayBuffer value exists — typed arrays own their storage (new Uint8Array(n) allocates; new Uint8Array(new ArrayBuffer(n)) erases the buffer into the view)
|
||||
|
||||
stdlib-fence.ts:26:13 - error SC2020: 'new ArrayBuffer' is part of the standard library types but has no scriptc lowering yet
|
||||
|
||||
@@ -118,7 +118,7 @@ stdlib-fence.ts:26:13 - error SC2020: 'new ArrayBuffer' is part of the standard
|
||||
| ^~~~~~~~~~~~~~~~~~
|
||||
27 | const st = new Set(new Set([1, 2])); // array seeds lower; Set/iterable seeds stay fenced
|
||||
|
||||
hint: the type checker sees the full standard library, but only the supported surface compiles (https://scriptc.dev/limitations)
|
||||
hint: no free-standing ArrayBuffer value exists — typed arrays own their storage: allocate the view directly (new Uint8Array(n)), or erase a fresh buffer into one (new Uint8Array(new ArrayBuffer(n)), new DataView(new ArrayBuffer(n), ...))
|
||||
|
||||
stdlib-fence.ts:27:12 - error SC2020: 'new Set(values)' is part of the standard library types but has no scriptc lowering yet
|
||||
|
||||
@@ -210,14 +210,14 @@ stdlib-fence.ts:42:17 - error SC2020: '.split with 2 arguments on strings' is pa
|
||||
|
||||
hint: the type checker sees the full standard library, but only the supported surface compiles (https://scriptc.dev/limitations)
|
||||
|
||||
stdlib-fence.ts:44:19 - error SC2020: 'number.toLocaleString' is part of the standard library types but has no scriptc lowering yet
|
||||
stdlib-fence.ts:44:19 - error SC2020: 'Number.prototype.toLocaleString without a locale' is part of the standard library types but has no scriptc lowering yet
|
||||
|
||||
43 | const fixed = (1.5).toFixed(); // digit-free toFixed lowers now (the static ties-up integer)
|
||||
44 | const localized = (1234.5).toLocaleString();
|
||||
| ^~~~~~~~~~~~~~~~~~~~~~~
|
||||
| ^~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
45 |
|
||||
|
||||
hint: locale- and ICU-backed behavior lives outside the static runtime (the localeCompare stance: code-unit order, no collation/locale data) — format with template literals, toFixed, and toString instead
|
||||
hint: the default locale is the host environment's, which a compiled binary cannot carry — pass it explicitly: x.toLocaleString("en-US")
|
||||
|
||||
stdlib-fence.ts:49:15 - error SC2020: '.indexOf with 2 arguments' is part of the standard library types but has no scriptc lowering yet
|
||||
|
||||
@@ -282,16 +282,34 @@ stdlib-fence.ts:66:16 - error SC2020: 'then with 2 arguments' is part of the sta
|
||||
|
||||
hint: the supported form takes exactly one fulfillment handler — chain .catch(...) for the rejection half
|
||||
|
||||
stdlib-fence.ts:70:7 - error SC2001: values of type '10n' cannot be compiled yet (supported: number, string, boolean, arrays, Maps, Sets, RegExp, functions, classes, records, unions of those, and 'unknown')
|
||||
stdlib-fence.ts:73:15 - error SC2020: 'Object.is over a dynamic operand' is part of the standard library types but has no scriptc lowering yet
|
||||
|
||||
69 |
|
||||
70 | const big = 10n;
|
||||
72 | // unions needing narrowing.
|
||||
73 | const dynIs = Object.is(JSON.parse("1") as unknown, 1);
|
||||
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
74 | function pick(a: string | number, b: number | boolean): boolean {
|
||||
|
||||
hint: validate/narrow the value first (strict equality's rule) — SameValue only differs from === on numbers (NaN, ±0)
|
||||
|
||||
stdlib-fence.ts:75:10 - error SC2020: 'Object.is over these union operands' is part of the standard library types but has no scriptc lowering yet
|
||||
|
||||
74 | function pick(a: string | number, b: number | boolean): boolean {
|
||||
75 | return Object.is(a, b); // partially-overlapping unions: narrow first
|
||||
| ^~~~~~~~~~~~~~~
|
||||
76 | }
|
||||
|
||||
hint: union-typed comparisons need one comparable shape (narrow first: check a discriminant field, or compare with '!== undefined'/'!== null' for unit arms)
|
||||
|
||||
stdlib-fence.ts:79:7 - error SC2001: values of type '10n' cannot be compiled yet (supported: number, string, boolean, arrays, Maps, Sets, RegExp, functions, classes, records, unions of those, and 'unknown')
|
||||
|
||||
78 |
|
||||
79 | const big = 10n;
|
||||
| ^~~
|
||||
71 |
|
||||
80 |
|
||||
|
||||
stdlib-fence.ts:70:13 - error SC1090: syntax 'BigIntLiteral' is not supported yet
|
||||
stdlib-fence.ts:79:13 - error SC1090: syntax 'BigIntLiteral' is not supported yet
|
||||
|
||||
69 |
|
||||
70 | const big = 10n;
|
||||
78 |
|
||||
79 | const big = 10n;
|
||||
| ^~~
|
||||
71 |
|
||||
80 |
|
||||
Reference in New Issue
Block a user