From 38867bd5eece09edd4cc41dd66f76e84a699b80e Mon Sep 17 00:00:00 2001 From: ASDAlexander77 Date: Mon, 19 Oct 2026 01:09:05 +0100 Subject: [PATCH 1/2] Toward a default library built under -mm=own: @nodrops C declarations and five rewrites lib.ts compiled under -mm=own had 62 errors; with the compiler's @nodrops support and fixes (TypeScriptCompiler branch own-nodrops-declarations) and these changes, 14 remain, all waiting on two compiler decisions (an exported class's methods may be overridden in another module; an exported function cannot keep a parameter). - Every C function declaration (lib.native.d.ts, lib.win32.ts, lib.linux.ts) is `@nodrops`: it destroys nothing the program owns, so a borrow of a field may be lent to a function that calls it. Not the GC_* ones, which can run finalizers. - ArrayBuffer.slice copies the bytes, as JavaScript's does, instead of a view sharing them; transfer copies too, and [Symbol.dispose] no longer deletes (the assignment releases under rc and own, the collector frees under gc). - MatchResults.indices makes a new MatchIndicesResults each time instead of caching one in a field it could only lend out. - Headers' constructor gives set copies of the caller's strings. - HashHelpers.hashCodeAny takes the address of its parameter, not of a local copy. Release gc and rc builds pass tests.ps1 (release compile and JIT). Co-Authored-By: Claude Opus 5.5 --- src/core/core.ts | 5 +- src/lib.linux.ts | 30 ++++---- src/lib.ts | 33 +++++++-- src/lib.win32.ts | 28 +++---- src/native/lib.native.d.ts | 146 +++++++++++++++++++------------------ 5 files changed, 133 insertions(+), 109 deletions(-) diff --git a/src/core/core.ts b/src/core/core.ts index 2de05b0..1973467 100644 --- a/src/core/core.ts +++ b/src/core/core.ts @@ -117,8 +117,9 @@ export namespace HashHelpers } export function hashCodeAny(key: any): int { - let keyLocal = key; - const anyPtr = Deref(> Ref(keyLocal)); + // the parameter's own slot: a copy of it in a local is a second reference, which -mm=own + // cannot give an exported function's parameter + const anyPtr = Deref(> Ref(key)); let anyStruct: Reference<[size: index, type: string, data: byte]> = anyPtr; return hashCodeBinary(Ref(anyStruct.data), anyStruct.size); } diff --git a/src/lib.linux.ts b/src/lib.linux.ts index 044820d..50d2791 100644 --- a/src/lib.linux.ts +++ b/src/lib.linux.ts @@ -5,14 +5,14 @@ type errno_t = i32; type time_t = long; // locale -declare function setlocale (category: int, locale: string): void; -declare function newlocale (category: int, locale: string, base: Opaque | null): Opaque; -declare function uselocale (locale: Opaque): Opaque; -declare function freelocale (locale: Opaque): void; +@nodrops declare function setlocale (category: int, locale: string): void; +@nodrops declare function newlocale (category: int, locale: string, base: Opaque | null): Opaque; +@nodrops declare function uselocale (locale: Opaque): Opaque; +@nodrops declare function freelocale (locale: Opaque): void; // LC_TIME @varargs -declare function snprintf(out: string, n: index, format: string): void; +@nodrops declare function snprintf(out: string, n: index, format: string): void; export function convertNumber(bufferSize: int, format: string, value: number): string { @@ -36,13 +36,13 @@ export function convertInteger(bufferSize: int, format: string, value: i32): str // Date & Time -declare function tzset(); +@nodrops declare function tzset(); export function init_time() { tzset(); } type timeval = [tv_sec: i64, tv_usec: i32]; -declare function timespec_get(tv: Reference, base: int): int; +@nodrops declare function timespec_get(tv: Reference, base: int): int; const TIME_UTC = 1; export function getMilliseconds(): i64 { @@ -60,7 +60,7 @@ type tm = [tm_sec: i32, tm_min: i32, tm_hour: i32, tm_mday: i32, tm_mon: i32, tm // struct, and strftime's %z and %Z read the last two, so every C call takes this one. type c_tm = [tm_sec: i32, tm_min: i32, tm_hour: i32, tm_mday: i32, tm_mon: i32, tm_year: i32, tm_wday: i32, tm_yday: i32, tm_isdst: i32, tm_gmtoff: index, tm_zone: index]; -declare function timegm(tv: Reference): long; +@nodrops declare function timegm(tv: Reference): long; export function makegmtime(year: i32, month: i32, day: i32, hour: i32, minutes: i32, seconds: i32, milliseconds: i32): i64 { let tm1: c_tm = [seconds, minutes, hour, day, month, year, 0, 0, 0, 0, 0]; const sec = timegm(Ref(tm1)); @@ -73,7 +73,7 @@ export function makegmtime(year: i32, month: i32, day: i32, hour: i32, minutes: return sec * 1000 + milliseconds; // return ms } -declare function mktime(tv: Reference): long; +@nodrops declare function mktime(tv: Reference): long; export function maketime(year: i32, month: i32, day: i32, hour: i32, minutes: i32, seconds: i32, milliseconds: i32): i64 { // tm_isdst -1: a local time is in daylight saving time or not as the date says (0 would read // a summer date as standard time, an hour off) @@ -88,8 +88,8 @@ export function maketime(year: i32, month: i32, day: i32, hour: i32, minutes: i3 return sec * 1000 + milliseconds; // return ms } -declare function gmtime_r(time: Reference, tm: Reference) : Reference; -declare function localtime_r(time: Reference, tm: Reference) : Reference; +@nodrops declare function gmtime_r(time: Reference, tm: Reference) : Reference; +@nodrops declare function localtime_r(time: Reference, tm: Reference) : Reference; function c_time(time: long, isUtc: boolean): c_tm { let tmDest: c_tm = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; let timeInSec: time_t = time / 1000; @@ -120,7 +120,7 @@ export function gettimezone(): i32 { } // returns `buffer`: a pointer, not copied -declare function ctime_r(time: Reference, buffer: string): Opaque; +@nodrops declare function ctime_r(time: Reference, buffer: string): Opaque; export function timestamp_to_string(maxsize: index, time: long): string { let timeInSec: time_t = time / 1000; let buffer : char[] = []; @@ -130,7 +130,7 @@ export function timestamp_to_string(maxsize: index, time: long): string { return s; } -declare function asctime_r(time: Reference, buffer: string): Opaque; +@nodrops declare function asctime_r(time: Reference, buffer: string): Opaque; export function time_to_string(maxsize: index, time: long, isUtc: boolean): string { let tm = c_time(time, isUtc); @@ -141,7 +141,7 @@ export function time_to_string(maxsize: index, time: long, isUtc: boolean): stri return s; } -declare function strftime(out: string, maxsize: index, format: string, tm: Reference): index; +@nodrops declare function strftime(out: string, maxsize: index, format: string, tm: Reference): index; export function time_format(maxsize: index, format: string, time: long, isUtc: boolean): string { let tm = c_time(time, isUtc); @@ -152,7 +152,7 @@ export function time_format(maxsize: index, format: string, time: long, isUtc: b return s; } -declare function strftime_l(out: string, maxsize: index, format: string, tm: Reference, locale: Opaque): index; +@nodrops declare function strftime_l(out: string, maxsize: index, format: string, tm: Reference, locale: Opaque): index; export function time_format_locale(maxsize: index, format: string, time: long, locale: string, isUtc: boolean): string { let tm = c_time(time, isUtc); diff --git a/src/lib.ts b/src/lib.ts index e83834a..91f9671 100644 --- a/src/lib.ts +++ b/src/lib.ts @@ -479,7 +479,9 @@ export class MatchResults // TODO: finish it //if (!this.hasIndices) // return undefined; - return this.indices_ ??= new MatchIndicesResults(this.match, this.lastIndex); + // a new one each time: it holds no state of its own, and a cached one in a field could + // only be lent out, never returned, under -mm=own + return new MatchIndicesResults(this.match, this.lastIndex); } } @@ -1831,7 +1833,7 @@ export class ArrayBuffer { const newArrayBuffer = new ArrayBuffer(0); newArrayBuffer.states = this.states | States.NonResizable; newArrayBuffer.maxByteLength = this.maxByteLength; - newArrayBuffer.arrayOfView = this.arrayOfView.view(start, end); + newArrayBuffer.arrayOfView = this.copyOf(start, end); return newArrayBuffer; } @@ -1840,7 +1842,11 @@ export class ArrayBuffer { const newArrayBuffer = new ArrayBuffer(0); newArrayBuffer.states = this.states; newArrayBuffer.maxByteLength = this.maxByteLength; - newArrayBuffer.arrayOfView = this.detach(); + if (this.arrayOfView != null) { + newArrayBuffer.arrayOfView = this.copyOf(0, this.arrayOfView.length); + } + + this.detach(); return newArrayBuffer; } @@ -1850,16 +1856,27 @@ export class ArrayBuffer { return newArrayBuffer; } + // The bytes [start, end) in an array of their own. A slice is a copy, as in JavaScript, and a + // transfer copies too: the bytes cannot move out of the field into another object under + // -mm=own, where a read of a field only borrows it. + private copyOf(start: int, end: int): char[] { + let copy: char[] = []; + copy.length = end - start; + for (let i = start; i < end; i++) { + copy[i - start] = this.arrayOfView[i]; + } + + return copy; + } + private detach() { this.states |= States.Detached; - const inst = this.arrayOfView; this.arrayOfView = null; - return inst; } + // the assignment releases the bytes under rc and own, and leaves them to the collector under gc [Symbol.dispose]() { if (this.arrayOfView != null && !this.detached) { - delete this.arrayOfView; this.arrayOfView = null; } } @@ -1874,7 +1891,9 @@ export class Headers { this.values = []; if (init != undefined) { - for (const [k, v] of init) this.set(k, v); + // copies: set is a method another module may override, so under -mm=own it may destroy + // anything, and the caller's strings cannot be lent to it + for (const [k, v] of init) this.set(k + "", v + ""); } } diff --git a/src/lib.win32.ts b/src/lib.win32.ts index d524eb4..5404a66 100644 --- a/src/lib.win32.ts +++ b/src/lib.win32.ts @@ -5,12 +5,12 @@ type errno_t = i32; type time_t = long; // locale -declare function _create_locale (category: int, locale: string): Opaque; -declare function _free_locale (locale: Opaque): void; +@nodrops declare function _create_locale (category: int, locale: string): Opaque; +@nodrops declare function _free_locale (locale: Opaque): void; // LC_TIME @varargs -declare function sprintf_s(out: string, n: index, format: string): void; +@nodrops declare function sprintf_s(out: string, n: index, format: string): void; export function convertNumber(bufferSize: int, format: string, value: number): string { @@ -33,13 +33,13 @@ export function convertInteger(bufferSize: int, format: string, value: i32): str } // Date & Time -declare function _tzset(); +@nodrops declare function _tzset(); export function init_time() { _tzset(); } type timeval64 = [tv_sec: i64, tv_usec: i32]; -declare function _timespec64_get(tv: Reference, base: int): int; +@nodrops declare function _timespec64_get(tv: Reference, base: int): int; const TIME_UTC = 1; export function getMilliseconds(): i64 { @@ -49,7 +49,7 @@ export function getMilliseconds(): i64 { } type tm = [tm_sec: i32, tm_min: i32, tm_hour: i32, tm_mday: i32, tm_mon: i32, tm_year: i32, tm_wday: i32, tm_yday: i32, tm_isdst: i32]; -declare function _mkgmtime64(tv: Reference): long; +@nodrops declare function _mkgmtime64(tv: Reference): long; export function makegmtime(year: i32, month: i32, day: i32, hour: i32, minutes: i32, seconds: i32, milliseconds: i32): i64 { let tm1: tm = [seconds, minutes, hour, day, month, year, 0, 0, 0]; const sec = _mkgmtime64(Ref(tm1)); @@ -62,7 +62,7 @@ export function makegmtime(year: i32, month: i32, day: i32, hour: i32, minutes: return sec * 1000 + milliseconds; // return ms } -declare function _mktime64(tv: Reference): long; +@nodrops declare function _mktime64(tv: Reference): long; export function maketime(year: i32, month: i32, day: i32, hour: i32, minutes: i32, seconds: i32, milliseconds: i32): i64 { // tm_isdst -1: a local time is in daylight saving time or not as the date says (0 would read // a summer date as standard time, an hour off) @@ -77,7 +77,7 @@ export function maketime(year: i32, month: i32, day: i32, hour: i32, minutes: i3 return sec * 1000 + milliseconds; // return ms } -declare function _gmtime64_s(tm: Reference, time: Reference) : errno_t; +@nodrops declare function _gmtime64_s(tm: Reference, time: Reference) : errno_t; export function gmtime(time: long): tm { let tmDest: tm = [0, 0, 0, 0, 0, 0, 0, 0, 0]; let timeInSec: long = time / 1000; @@ -85,7 +85,7 @@ export function gmtime(time: long): tm { return tmDest; // return ms } -declare function _localtime64_s(tm: Reference, time: Reference) : errno_t; +@nodrops declare function _localtime64_s(tm: Reference, time: Reference) : errno_t; export function localtime(time: long): tm { let tmDest: tm = [0, 0, 0, 0, 0, 0, 0, 0, 0]; let timeInSec: long = time / 1000; @@ -93,7 +93,7 @@ export function localtime(time: long): tm { return tmDest; // return ms } -declare function _get_timezone(time: Reference): int; +@nodrops declare function _get_timezone(time: Reference): int; export function gettimezone(): i32 { let time = 0; const error = _get_timezone(Ref(time)); @@ -101,7 +101,7 @@ export function gettimezone(): i32 { } // errno_t, not a string: declared `string`, a nonzero error would be copied as one -declare function _ctime64_s(buffer: string, numberOfElements: index, time: Reference): int; +@nodrops declare function _ctime64_s(buffer: string, numberOfElements: index, time: Reference): int; export function timestamp_to_string(maxsize: index, time: long): string { let timeInSec: time_t = time / 1000; let buffer : char[] = []; @@ -112,7 +112,7 @@ export function timestamp_to_string(maxsize: index, time: long): string { } // errno_t -declare function asctime_s(buffer: string, numberOfElements: index, time: Reference): int; +@nodrops declare function asctime_s(buffer: string, numberOfElements: index, time: Reference): int; export function time_to_string(maxsize: index, time: long, isUtc: boolean): string { let tm = isUtc ? gmtime(time) : localtime(time); @@ -123,7 +123,7 @@ export function time_to_string(maxsize: index, time: long, isUtc: boolean): stri return s; } -declare function strftime(out: string, maxsize: index, format: string, tm: Reference): index; +@nodrops declare function strftime(out: string, maxsize: index, format: string, tm: Reference): index; export function time_format(maxsize: index, format: string, time: long, isUtc: boolean): string { let tm = isUtc ? gmtime(time) : localtime(time); @@ -134,7 +134,7 @@ export function time_format(maxsize: index, format: string, time: long, isUtc: b return s; } -declare function _strftime_l(out: string, maxsize: index, format: string, tm: Reference, locale: Opaque): index; +@nodrops declare function _strftime_l(out: string, maxsize: index, format: string, tm: Reference, locale: Opaque): index; export function time_format_locale(maxsize: index, format: string, time: long, locale: string, isUtc: boolean): string { let tm = isUtc ? gmtime(time) : localtime(time); diff --git a/src/native/lib.native.d.ts b/src/native/lib.native.d.ts index 20a6b8d..0ec4f0b 100644 --- a/src/native/lib.native.d.ts +++ b/src/native/lib.native.d.ts @@ -1,95 +1,99 @@ +// `@nodrops`: a C function that destroys nothing the TypeScript program owns, so -mm=own may lend +// it a borrow (a field, an element) and keep using that borrow after the call. Only for functions +// that never call back into TypeScript; the GC_* ones below can run finalizers and have none. + // IO -declare function write(fileNo: int, s: string, count: int); +@nodrops declare function write(fileNo: int, s: string, count: int); // Buffered counterpart of write(), sharing stdio's buffer with print()/puts() so the // two cannot reorder relative to each other. See src/wrappers/io.cpp. -declare function stdio_write(fileNo: int, s: string, count: int); +@nodrops declare function stdio_write(fileNo: int, s: string, count: int); // Time (in seconds) -declare function time(destTime: Reference); +@nodrops declare function time(destTime: Reference); // String -declare function strtol(str: string, end: Reference, base: int): int; -declare function strtod(str: string, end: Reference): double; -declare function strncmp(str: string, other: string, count: index): int; +@nodrops declare function strtol(str: string, end: Reference, base: int): int; +@nodrops declare function strtod(str: string, end: Reference): double; +@nodrops declare function strncmp(str: string, other: string, count: index): int; // a pointer into `str`, not a string of its own: a `string` result from C is copied -declare function strstr(str: string, other: string): Opaque; -declare function strcoll(str: string, other: string): int; -declare function setlocale (category: int, locale: string): Opaque; -declare function toupper(c: int): int; -declare function tolower(c: int): int; -declare function isspace(argument: int): int; +@nodrops declare function strstr(str: string, other: string): Opaque; +@nodrops declare function strcoll(str: string, other: string): int; +@nodrops declare function setlocale (category: int, locale: string): Opaque; +@nodrops declare function toupper(c: int): int; +@nodrops declare function tolower(c: int): int; +@nodrops declare function isspace(argument: int): int; // Random -declare function srand(seed: int); +@nodrops declare function srand(seed: int); -declare function rand(): int; +@nodrops declare function rand(): int; // Math -declare function fabs(x: number): number; +@nodrops declare function fabs(x: number): number; -declare function acos(x: number): number; +@nodrops declare function acos(x: number): number; -declare function acosh(x: number): number; +@nodrops declare function acosh(x: number): number; -declare function asin(x: number): number; +@nodrops declare function asin(x: number): number; -declare function asinh(x: number): number; +@nodrops declare function asinh(x: number): number; -declare function atan(x: number): number; +@nodrops declare function atan(x: number): number; -declare function atan2(y: number, x: number): number; +@nodrops declare function atan2(y: number, x: number): number; -declare function atanh(x: number): number; +@nodrops declare function atanh(x: number): number; -declare function cbrt(x: number): number; +@nodrops declare function cbrt(x: number): number; -declare function ceil(x: number): number; +@nodrops declare function ceil(x: number): number; -declare function cos(x: number): number; +@nodrops declare function cos(x: number): number; -declare function cosh(x: number): number; +@nodrops declare function cosh(x: number): number; -declare function exp(x: number): number; +@nodrops declare function exp(x: number): number; -declare function expm1(x: number): number; +@nodrops declare function expm1(x: number): number; -declare function floor(x: number): number; +@nodrops declare function floor(x: number): number; -declare function log(x: number): number; +@nodrops declare function log(x: number): number; -declare function log10(x: number): number; +@nodrops declare function log10(x: number): number; -declare function log1p(x: number): number; +@nodrops declare function log1p(x: number): number; -declare function log2(x: number): number; +@nodrops declare function log2(x: number): number; -declare function min(x: number, y: number): number; +@nodrops declare function min(x: number, y: number): number; -declare function max(x: number, y: number): number; +@nodrops declare function max(x: number, y: number): number; -declare function pow(x: number, y: number): number; +@nodrops declare function pow(x: number, y: number): number; -declare function round(x: number): number; +@nodrops declare function round(x: number): number; -declare function sin(x: number): number; +@nodrops declare function sin(x: number): number; -declare function sinh(x: number): number; +@nodrops declare function sinh(x: number): number; -declare function sqrt(x: number): number; +@nodrops declare function sqrt(x: number): number; -declare function tan(x: number): number; +@nodrops declare function tan(x: number): number; -declare function tanh(x: number): number; +@nodrops declare function tanh(x: number): number; -declare function trunc(x: number): number; +@nodrops declare function trunc(x: number): number; // memory -declare function memcpy(dest: Opaque, src: Opaque, bytes: index): Opaque; -declare function memmove(dest: Opaque, src: Opaque, bytes: index): Opaque; +@nodrops declare function memcpy(dest: Opaque, src: Opaque, bytes: index): Opaque; +@nodrops declare function memmove(dest: Opaque, src: Opaque, bytes: index): Opaque; // wrappers/memory.cpp: memcpy_s on Windows, __memcpy_chk elsewhere - ends the process rather // than copy more than `destSize` bytes -declare function boundedCopy(dest: Opaque, destSize: index, src: Opaque, count: index): void; -declare function memcmp(dest: Opaque, src: Opaque, bytes: index): int; +@nodrops declare function boundedCopy(dest: Opaque, destSize: index, src: Opaque, count: index): void; +@nodrops declare function memcmp(dest: Opaque, src: Opaque, bytes: index): int; // gc weak references declare function GC_general_register_disappearing_link(link: Reference, obj: Opaque): int; @@ -97,34 +101,34 @@ declare function GC_unregister_disappearing_link(link: Reference): int; declare function GC_gcollect(): void; // datetime -declare function parse_date(dateStr: string): time_t; +@nodrops declare function parse_date(dateStr: string): time_t; // regex -declare function regexp_test(expr: string, flags: string, s: string): int; -declare function regexp_exec(expr: string, flags: string, s: string, cm: Opaque): Opaque; -declare function regexp_match_results_size(cm: Opaque): index; -declare function regexp_match_results_prefix_length(cm: Opaque): index; -declare function regexp_match_results_suffix_position(cm: Opaque): index; -declare function regexp_match_results_sub_match_str_length(cm: Opaque, subIndex: index): index; -declare function regexp_match_results_sub_match_str_copy_to(cm: Opaque, subIndex: index, buffer: string, count: index); -declare function regexp_match_results_sub_match_position(cm: Opaque, subIndex: index): index; -declare function regexp_match_results_sub_match_length(cm: Opaque, subIndex: index): index; +@nodrops declare function regexp_test(expr: string, flags: string, s: string): int; +@nodrops declare function regexp_exec(expr: string, flags: string, s: string, cm: Opaque): Opaque; +@nodrops declare function regexp_match_results_size(cm: Opaque): index; +@nodrops declare function regexp_match_results_prefix_length(cm: Opaque): index; +@nodrops declare function regexp_match_results_suffix_position(cm: Opaque): index; +@nodrops declare function regexp_match_results_sub_match_str_length(cm: Opaque, subIndex: index): index; +@nodrops declare function regexp_match_results_sub_match_str_copy_to(cm: Opaque, subIndex: index, buffer: string, count: index); +@nodrops declare function regexp_match_results_sub_match_position(cm: Opaque, subIndex: index): index; +@nodrops declare function regexp_match_results_sub_match_length(cm: Opaque, subIndex: index): index; // malloc'ed C strings, given back with regexp_free_string: kept as pointers, not copied -declare function regexp_match_results_format(cm: Opaque, replacement: string): Opaque; -declare function regexp_replace(expr: string, flags: string, s: string, replacement: string): Opaque; -declare function regexp_free_string(s: Opaque): void; -declare function regexp_free(cm: Opaque): void; +@nodrops declare function regexp_match_results_format(cm: Opaque, replacement: string): Opaque; +@nodrops declare function regexp_replace(expr: string, flags: string, s: string, replacement: string): Opaque; +@nodrops declare function regexp_free_string(s: Opaque): void; +@nodrops declare function regexp_free(cm: Opaque): void; // thread -declare function std_this_thread_sleep_for(milliseconds: u32): void; +@nodrops declare function std_this_thread_sleep_for(milliseconds: u32): void; // http -declare function http_request(method: string, url: string, headers: string, body: string, bodyLength: index): Opaque; -declare function http_response_success(r: Opaque): boolean; -declare function http_response_error_code(r: Opaque): int; -declare function http_response_status(r: Opaque): int; -declare function http_response_headers_length(r: Opaque): index; -declare function http_response_headers_copy_to(r: Opaque, buffer: string, count: index): void; -declare function http_response_body_length(r: Opaque): index; -declare function http_response_body_copy_to(r: Opaque, buffer: string, count: index): void; -declare function http_response_free(r: Opaque): void; +@nodrops declare function http_request(method: string, url: string, headers: string, body: string, bodyLength: index): Opaque; +@nodrops declare function http_response_success(r: Opaque): boolean; +@nodrops declare function http_response_error_code(r: Opaque): int; +@nodrops declare function http_response_status(r: Opaque): int; +@nodrops declare function http_response_headers_length(r: Opaque): index; +@nodrops declare function http_response_headers_copy_to(r: Opaque, buffer: string, count: index): void; +@nodrops declare function http_response_body_length(r: Opaque): index; +@nodrops declare function http_response_body_copy_to(r: Opaque, buffer: string, count: index): void; +@nodrops declare function http_response_free(r: Opaque): void; From 7c6807ec640ad4debc7859ac9c751825cf2a0f4e Mon Sep 17 00:00:00 2001 From: ASDAlexander77 Date: Mon, 19 Oct 2026 10:22:01 +0100 Subject: [PATCH 2/2] @final on the library's classes: own resolves their methods Boolean, Number, BigInt, Date, MatchIndicesResults, MatchResults, RegExp, String, Headers and Response are @final in lib.ts and lib.d.ts (ArrayBuffer in lib.ts only: it has no declaration). Calls on them are direct (TypeScriptCompiler branch own-final-classes), so -mm=own knows their facts: lib.ts under own goes from 14 errors to 5 (release; 17 to 7 with --di), all of them matchAll, String[Symbol.iterator] and Response.constructor, which keep or borrow a parameter of an exported function. The Headers constructor still copies the caller's strings, with its comment corrected: set is a direct call now, but it may overwrite an element of this.values. Release gc and rc builds pass tests.ps1 (compile and JIT). Co-Authored-By: Claude Opus 5.5 --- src/lib.d.ts | 10 ++++++++++ src/lib.ts | 15 +++++++++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/lib.d.ts b/src/lib.d.ts index 4f1135a..d24dd4e 100644 --- a/src/lib.d.ts +++ b/src/lib.d.ts @@ -15,6 +15,7 @@ declare namespace __Boolean { function toString(this: boolean): string; } +@final declare class Boolean { private value: boolean; @@ -36,6 +37,7 @@ declare namespace __Number { function toString(this: number, radix?: int): string; } +@final declare class Number { static EPSILON: number; @@ -101,6 +103,7 @@ declare namespace __BigInt { function toString(this: bigint): string; } +@final declare class BigInt { private value: bigint; @@ -113,6 +116,7 @@ declare class BigInt { valueOf(): bigint; } +@final declare class Date { private timestamp: time_t; @@ -217,6 +221,7 @@ declare class Date { [Symbol.toPrimitive](hint: string) : string | number; } +@final declare class MatchIndicesResults { private match: Opaque | null; @@ -232,6 +237,7 @@ declare class MatchIndicesResults get length(): index; } +@final declare class MatchResults { private indices_: MatchIndicesResults; @@ -253,6 +259,7 @@ declare class MatchResults get indices(): MatchIndicesResults/* | undefined*/; } +@final declare class RegExp { dotAll: boolean; @@ -357,6 +364,7 @@ declare namespace __String { function valueOf(this: string): string; } +@final declare class String { private value: string; @@ -639,6 +647,7 @@ declare namespace Math { function trunc(x: number): number; } +@final declare class Headers { private names: string[]; private values: string[]; @@ -660,6 +669,7 @@ declare class Headers { toRawString(): string; } +@final declare class Response { status: int; headers: Headers; diff --git a/src/lib.ts b/src/lib.ts index 91f9671..89a8ef7 100644 --- a/src/lib.ts +++ b/src/lib.ts @@ -28,6 +28,7 @@ namespace __Boolean { } } +@final export class Boolean { public constructor(private value: boolean) { } @@ -65,6 +66,7 @@ namespace __Number { } } +@final export class Number { public static EPSILON = 2 ** (-52); @@ -158,6 +160,7 @@ namespace __BigInt { } } +@final export class BigInt { public constructor(private value: bigint) { } @@ -175,6 +178,7 @@ export class BigInt { } } +@final export class Date { private timestamp: time_t; constructor (valueOrYear?: time_t | i32, monthIndex?: i32, day = 1, hours = 0, minutes = 0, seconds = 0, milliseconds = 0) { @@ -436,6 +440,7 @@ export class Date { } } +@final export class MatchIndicesResults { constructor(private match: Opaque | null = null, private lastIndex: index = 0) { @@ -455,6 +460,7 @@ export class MatchIndicesResults } } +@final export class MatchResults { private indices_: MatchIndicesResults = null; @@ -485,6 +491,7 @@ export class MatchResults } } +@final export class RegExp { dotAll = false; @@ -1286,6 +1293,7 @@ class StringIterator implements ClassIterator { } */ +@final export class String { public constructor(private value: string) { @@ -1782,6 +1790,7 @@ enum States { View = 8, } +@final export class ArrayBuffer { private arrayOfView: char[]; @@ -1882,6 +1891,7 @@ export class ArrayBuffer { } } +@final export class Headers { private names: string[]; private values: string[]; @@ -1891,8 +1901,8 @@ export class Headers { this.values = []; if (init != undefined) { - // copies: set is a method another module may override, so under -mm=own it may destroy - // anything, and the caller's strings cannot be lent to it + // copies: set may overwrite an element of this.values, and under -mm=own a call that + // may destroy anything cannot be lent the caller's strings for (const [k, v] of init) this.set(k + "", v + ""); } } @@ -1982,6 +1992,7 @@ export class Headers { } } +@final export class Response { public constructor( public status: int,