mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-31 07:23:06 +08:00
Merge main
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
// The assert surface's honest fences: loose deep equality over recursive
|
||||
// types (equal/notEqual, deepEqual/notDeepEqual and assert/strict's aliases lower),
|
||||
// reference-equality strictEqual on objects,
|
||||
// union operands (narrow first), mismatched or uncomparable deep types
|
||||
// reference-equality strictEqual on objects, union operands with object
|
||||
// arms (narrow first), mismatched or uncomparable deep types
|
||||
// (typed arrays, class instances), namespace-object calls, unsupported
|
||||
// throws/rejects expectations (properties outside the static error
|
||||
// surface, empty shapes, non-literal objects, doesNotReject shapes —
|
||||
@@ -19,9 +19,9 @@ assert.notDeepEqual([1], [2]);
|
||||
const box = { n: 1 };
|
||||
assert.strictEqual(box, box); // reference equality — deepStrictEqual hint
|
||||
|
||||
let maybe: string | undefined = "x";
|
||||
let maybe: { n: number } | undefined = box;
|
||||
if ("x".length === 0) maybe = undefined;
|
||||
assert.strictEqual(maybe, "x"); // union operand — narrow first
|
||||
assert.strictEqual(maybe, box); // union operand with an object arm — narrow first
|
||||
|
||||
const unk: unknown = JSON.parse("{}");
|
||||
assert.strictEqual(unk, { n: 1 }); // strict + composite static side: reference identity does not survive the unknown boundary
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
// Builtins without a lowered value refuse at compile time in TypeScript,
|
||||
// including as object literal members.
|
||||
function take(x: unknown): unknown {
|
||||
return x;
|
||||
}
|
||||
|
||||
const inferred = { json: JSON };
|
||||
const loose: any = { intl: Intl, proxy: Proxy };
|
||||
const timers: Record<string, unknown> = { setTimeout };
|
||||
const list: any[] = [Array, parseInt];
|
||||
console.log(inferred, loose, timers, list, take(process));
|
||||
@@ -1,5 +1,7 @@
|
||||
// Unsupported formatters must reach the Date method diagnostic, including
|
||||
// names inherited from Object.prototype by the compiler's dispatch table.
|
||||
// The default-locale toLocaleString()/toLocaleTimeString() lower now
|
||||
// (scr_intl.c); a locale or options argument still has none.
|
||||
const date = new Date(0);
|
||||
date.toLocaleTimeString();
|
||||
date.toLocaleString();
|
||||
date.toLocaleDateString();
|
||||
date.toLocaleString("en-US");
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
// trim/pad variants, parseInt, isNaN, the global parseFloat/isFinite and
|
||||
// Number.parseFloat/Number.parseInt over exactly-typed arguments,
|
||||
// string-pattern replacement and toPrecision compile statically now and no
|
||||
// longer appear here; toLocaleString without a locale still has no
|
||||
// lowering.)
|
||||
// longer appear here; toLocaleString at an explicit non-en-US locale still
|
||||
// has no lowering (the default-locale form lowers over ICU).)
|
||||
const up = Math.cbrt(2);
|
||||
const tau = Math.PI * 2;
|
||||
const price = (19.99).toLocaleString();
|
||||
const price = (19.99).toLocaleString("de-DE");
|
||||
const n = Number.parseFloat("3.14");
|
||||
void n;
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
import { EventEmitter } from "node:events";
|
||||
|
||||
const emitter = new EventEmitter();
|
||||
const suffix = process.argv[2] ?? "x";
|
||||
emitter.on(`topic:${suffix}`, () => {});
|
||||
emitter.listeners("topic:x");
|
||||
@@ -0,0 +1,25 @@
|
||||
// Declared-arity narrowing: a function value stored under a type that names
|
||||
// FEWER parameters than the value's own signature. Node's function object
|
||||
// still carries every parameter, so a later widened call would pass all of
|
||||
// its arguments (the surplus must not be dropped); a compiled function value
|
||||
// is one concrete native ABI, so the narrowing store is refused at every
|
||||
// observable use instead.
|
||||
const impl = (text: string, times: number) => text.length * times;
|
||||
|
||||
// (1) Narrowing at a binding, then calling through the narrow type.
|
||||
const narrowed = impl as unknown as (text: string) => number;
|
||||
console.log(narrowed("abc"));
|
||||
|
||||
// (2) The same narrowing in an object literal's member slot, with the member
|
||||
// read back and widened to the implementation's arity.
|
||||
interface Sink {
|
||||
write(text: string): number;
|
||||
}
|
||||
|
||||
function makeWriter(): Sink {
|
||||
return { write: impl as unknown as (text: string) => number };
|
||||
}
|
||||
|
||||
const sink = makeWriter();
|
||||
const widened = sink.write as (text: string, times: number) => number;
|
||||
console.log(widened("abcd", 3));
|
||||
@@ -19,14 +19,25 @@ console.log(typeof stored);
|
||||
const flipAlias = flip;
|
||||
console.log(flipAlias(5));
|
||||
|
||||
// Declared inside a function and reading its parameter: instances are
|
||||
// module functions and cannot capture the enclosing frame.
|
||||
// Declared inside a function and reading a binding declared AFTER it: a
|
||||
// call could run before the binding's initializer (a temporal dead zone).
|
||||
// (Bindings declared before it are supported — copied as trailing
|
||||
// parameters, or shared through a closure environment when reassigned.)
|
||||
function outer(base: number): number {
|
||||
const inner = <T>(x: T): [T, number] => [x, base];
|
||||
const inner = <T>(x: T): [T, number] => [x, scale];
|
||||
const scale = base + 1;
|
||||
return inner(3)[1];
|
||||
}
|
||||
console.log(outer(4));
|
||||
|
||||
// Declared inside a GENERIC function: the enclosing frame re-lowers per
|
||||
// instantiation, so the shared instance cannot carry its type argument.
|
||||
function outerGeneric<U>(seed: U): string {
|
||||
const inner = <T>(x: T): string => `${String(x)}${String(seed)}`;
|
||||
return inner(3);
|
||||
}
|
||||
console.log(outerGeneric<number>(1));
|
||||
|
||||
// Used as a value inside a function: each call of the enclosing function
|
||||
// makes a new function object (`make() !== make()`), and the compiled
|
||||
// instances are shared module functions.
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// The native implementation covers Unicode grapheme segmentation.
|
||||
// The native implementation covers Unicode grapheme segmentation, and ICU
|
||||
// answers word segmentation; locale negotiation over several tags and
|
||||
// sentence segmentation stay fenced.
|
||||
new Intl.Segmenter(["en-US", "de"]);
|
||||
new Intl.Segmenter(undefined, { granularity: "word" });
|
||||
new Intl.Segmenter(undefined, { granularity: "word", localeMatcher: "best fit" });
|
||||
new Intl.Segmenter(undefined, { granularity: "sentence" });
|
||||
// End of the diagnostic fixture.
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// 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
|
||||
// x.toLocaleString("en-US") — plus the default locale's own forms over ICU
|
||||
// (x.toLocaleString(), d.toLocaleString()). Everything else fences by
|
||||
// NAME: other locales (ICU locale negotiation), options bags, formatter
|
||||
// VALUES, and the rest of the Intl namespace (DurationFormat,
|
||||
// DateTimeFormat, PluralRules, getCanonicalLocales).
|
||||
|
||||
@@ -14,7 +14,6 @@ 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 });
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
// ECMAScript early errors the TypeScript checker does not model: a statement
|
||||
// list may not declare one lexical name twice, and a lexical name may not be
|
||||
// `var`-declared in the same list (Block : { StatementList }, and a switch's
|
||||
// case block is one such list). Both are SC0005/SC0006, reported at
|
||||
// preflight — the constructs come from Test262's negative-parse suites.
|
||||
{
|
||||
class collide {}
|
||||
async function collide() {}
|
||||
}
|
||||
|
||||
switch (1) {
|
||||
case 1:
|
||||
let scoped = 1;
|
||||
break;
|
||||
case 2:
|
||||
var scoped = 2;
|
||||
break;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// ECMAScript early errors the TypeScript checker does not model: the body of
|
||||
// a loop, a `with` statement, or an if clause admits only a Statement, and
|
||||
// Annex B's function-declaration allowance never covers an async function, a
|
||||
// generator, a class, or a lexical declaration. SC0007, reported at
|
||||
// preflight — the shapes come from Test262's negative-parse suites.
|
||||
while (false) function inLoop() {}
|
||||
l: class inLabel {}
|
||||
if (true) async function inIf() {}
|
||||
if (false) il: function labelledInIf() {}
|
||||
do function* inDoWhile() {} while (false);
|
||||
@@ -0,0 +1,8 @@
|
||||
// The strict half of SC0007: Annex B's allowance for a plain function
|
||||
// declaration in an if/else body or a labelled statement is non-strict code
|
||||
// only, so a "use strict" prologue takes it away — Node agrees on both
|
||||
// shapes. The sloppy fixture beside this one pins the refusals that hold in
|
||||
// both modes.
|
||||
"use strict";
|
||||
if (true) function inIf() {}
|
||||
l: function inLabel() {}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Patching a NATIVE host object: Node lets a library swap
|
||||
// `process.stdout.write`, hand out `process.stdout.write.bind(...)`, or
|
||||
// redefine a module member with Object.defineProperty. The process output
|
||||
// stream's write slot is the runtime stream object's, so the assignment and
|
||||
// the bind compile. A builtin module's exports are direct calls, so those
|
||||
// forms keep an explicit refusal instead of silently patching nothing. The
|
||||
// working split: use the module's functions directly.
|
||||
|
||||
import * as fs from "node:fs";
|
||||
|
||||
export function bindReadFileSync(): typeof fs.readFileSync {
|
||||
return fs.readFileSync.bind(fs);
|
||||
}
|
||||
|
||||
export function defineReadFileSync(): void {
|
||||
Object.defineProperty(fs, "readFileSync", { value: () => "" });
|
||||
}
|
||||
|
||||
bindReadFileSync();
|
||||
defineReadFileSync();
|
||||
@@ -0,0 +1,5 @@
|
||||
// Drain registration on the process streams lowers to nothing because a
|
||||
// synchronous write never asks for a drain; the listener table it would
|
||||
// fill is not observable, so reading it stays refused.
|
||||
process.stdout.once("drain", () => {});
|
||||
console.log(process.stdout.listenerCount("drain"));
|
||||
@@ -12,9 +12,9 @@ const loose: Loose = { a: 1 };
|
||||
const picked: PickedA = loose;
|
||||
console.log(picked);
|
||||
|
||||
// A method field whose parameter shapes relate only BIVARIANTLY: neither
|
||||
// parameter type converts into the other mechanically, so the field does
|
||||
// not lift.
|
||||
// A method field whose parameter shapes relate only BIVARIANTLY: the field
|
||||
// lifts as a checked adapter, so only the console.log below refuses (a
|
||||
// function value has no inspect lowering).
|
||||
type SrcM = { m(x: { a: number }): void; extra: number };
|
||||
type DstM = { m(x: { a: number; b: number }): void };
|
||||
const srcM: SrcM = {
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
// A sloppy-mode implicit-global WRITE in a JavaScript source. Node resolves
|
||||
// the write by creating a property on the global object — process-wide state
|
||||
// a native binary has none of — so the write is refused by name (SC1091) and
|
||||
// names the Node-island route the sloppy semantics need (plan D-2/D-5).
|
||||
implicitGlobal = 42;
|
||||
console.log(implicitGlobal);
|
||||
@@ -38,7 +38,7 @@ const arrAt = [1, 2].at(0);
|
||||
const norm = "abc".normalize();
|
||||
const limited = "a,b,c".split(Math.random() ? "," : /,/, 2); // union separators stay fenced; limits lower
|
||||
const fixed = (1.5).toFixed(); // digit-free toFixed lowers now (the static ties-up integer)
|
||||
const localized = (1234.5).toLocaleString();
|
||||
const localized = (1234.5).toLocaleString("de-DE");
|
||||
// Unlowered call FORMS of lowered members. (push is fully variadic now —
|
||||
// no fenced form remains to pin.)
|
||||
const nums = [1, 2, 3];
|
||||
|
||||
Reference in New Issue
Block a user