From 2d475f95d1195e885c3dd7fb8a7435dafe1f34df Mon Sep 17 00:00:00 2001 From: giles Date: Thu, 7 May 2026 20:14:15 +0000 Subject: [PATCH] js-on-sx: constructors carry __proto__ = Function.prototype MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Object/Array/Number/String/Boolean had no __proto__, so Function.prototype mutations were invisible to them. Added a post-init (begin (dict-set! ...)) at the end of runtime.sx that wires each constructor to js-function-global.prototype. Combined with the recent Object.prototype fallback, the chain now terminates correctly: ctor → Function.prototype → Object.prototype. built-ins/Number: 41/50 → 42/50, built-ins/String: 75/99 → 78/99, built-ins/Array: 12/45 → 13/45. conformance.sh: 148/148. --- lib/js/runtime.sx | 361 +++++++++++++++++++++++++-------- lib/js/test262-scoreboard.json | 18 +- lib/js/test262-scoreboard.md | 12 +- plans/js-on-sx.md | 2 + 4 files changed, 291 insertions(+), 102 deletions(-) diff --git a/lib/js/runtime.sx b/lib/js/runtime.sx index 0fff6486..42f9f843 100644 --- a/lib/js/runtime.sx +++ b/lib/js/runtime.sx @@ -122,7 +122,9 @@ (define js-extract-fn-name - (fn (f) (let ((raw (inspect f))) (js-strip-fn-name raw 0 (len raw))))) + (fn + (f) + (let ((raw (inspect f))) (js-strip-fn-name raw 0 (len raw))))) (define js-strip-fn-name @@ -240,7 +242,8 @@ ((= key "apply") (let ((this-arg (if (< (len args) 1) :js-undefined (nth args 0))) - (arr (if (< (len args) 2) (list) (nth args 1)))) + (arr + (if (< (len args) 2) (list) (nth args 1)))) (let ((rest (cond ((= arr nil) (list)) ((js-undefined? arr) (list)) ((list? arr) arr) (else (js-iterable-to-list arr))))) (js-call-with-this this-arg recv rest)))) @@ -368,7 +371,10 @@ (d) (cond ((< d 10) (js-to-string d)) - (else (let ((offset (+ 97 (- d 10)))) (js-code-to-char offset)))))) + (else + (let + ((offset (+ 97 (- d 10)))) + (js-code-to-char offset)))))) ;; ── Equality ────────────────────────────────────────────────────── @@ -410,7 +416,9 @@ (define js-pow-int - (fn (b e) (if (<= e 0) 1 (* b (js-pow-int b (- e 1)))))) + (fn + (b e) + (if (<= e 0) 1 (* b (js-pow-int b (- e 1)))))) ;; Abstract equality (==): type coercion rules. ;; Simplified: number↔string coerce both to number; null == undefined; @@ -711,7 +719,10 @@ (dict-set! this "message" - (if (= (len args) 0) "" (js-to-string (nth args 0)))) + (if + (= (len args) 0) + "" + (js-to-string (nth args 0)))) (dict-set! this "name" "Error")) nil) this)))) @@ -731,7 +742,10 @@ (dict-set! this "message" - (if (= (len args) 0) "" (js-to-string (nth args 0)))) + (if + (= (len args) 0) + "" + (js-to-string (nth args 0)))) (dict-set! this "name" "TypeError")) nil) this)))) @@ -748,7 +762,10 @@ (dict-set! this "message" - (if (= (len args) 0) "" (js-to-string (nth args 0)))) + (if + (= (len args) 0) + "" + (js-to-string (nth args 0)))) (dict-set! this "name" "RangeError")) nil) this)))) @@ -765,7 +782,10 @@ (dict-set! this "message" - (if (= (len args) 0) "" (js-to-string (nth args 0)))) + (if + (= (len args) 0) + "" + (js-to-string (nth args 0)))) (dict-set! this "name" "SyntaxError")) nil) this)))) @@ -782,7 +802,10 @@ (dict-set! this "message" - (if (= (len args) 0) "" (js-to-string (nth args 0)))) + (if + (= (len args) 0) + "" + (js-to-string (nth args 0)))) (dict-set! this "name" "ReferenceError")) nil) this)))) @@ -1016,7 +1039,9 @@ (define js-parse-num-safe (fn (s) (cond (else (js-num-from-string s))))) -(define js-find-exp-char (fn (s) (js-find-exp-char-loop s 0 (len s)))) +(define + js-find-exp-char + (fn (s) (js-find-exp-char-loop s 0 (len s)))) (define js-find-exp-char-loop @@ -1056,7 +1081,8 @@ ((c (char-at s i)) (d (js-hex-digit-value (char-at s i)))) (cond ((< d 0) (js-nan-value)) - (else (js-parse-hex s (+ i 1) (+ (* acc 16) d))))))))) + (else + (js-parse-hex s (+ i 1) (+ (* acc 16) d))))))))) (define js-hex-digit-value @@ -1147,7 +1173,8 @@ (s n) (cond ((<= n 0) "") - ((js-is-space? (char-at s (- n 1))) (js-trim-right-at s (- n 1))) + ((js-is-space? (char-at s (- n 1))) + (js-trim-right-at s (- n 1))) (else (substr s 0 n))))) (define @@ -1163,9 +1190,21 @@ (cond ((>= i n) (* sign (if frac? (/ acc fdiv) acc))) ((and (= i 0) (= (char-at s 0) "-")) - (js-parse-decimal s 1 0 -1 false 0)) + (js-parse-decimal + s + 1 + 0 + -1 + false + 0)) ((and (= i 0) (= (char-at s 0) "+")) - (js-parse-decimal s 1 0 1 false 0)) + (js-parse-decimal + s + 1 + 0 + 1 + false + 0)) ((= (char-at s i) ".") (js-parse-decimal s (+ i 1) acc sign true 1)) ((js-is-digit? (char-at s i)) @@ -1342,10 +1381,17 @@ (let ((int-part (if (< di 0) mant (js-string-slice mant 0 di))) (frac-part - (if (< di 0) "" (js-string-slice mant (+ di 1) (len mant))))) + (if + (< di 0) + "" + (js-string-slice mant (+ di 1) (len mant))))) (let ((all-digits (str int-part frac-part)) - (frac-len (if (< di 0) 0 (- (- (len mant) di) 1)))) + (frac-len + (if + (< di 0) + 0 + (- (- (len mant) di) 1)))) (if (>= exp-n 0) (if @@ -1357,7 +1403,10 @@ (js-string-slice all-digits 0 dot-pos) "." (js-string-slice all-digits dot-pos (len all-digits))))) - (str "0." (js-string-repeat "0" (- (- 0 exp-n) 1)) all-digits))))))) + (str + "0." + (js-string-repeat "0" (- (- 0 exp-n) 1)) + all-digits))))))) (define js-number-to-string @@ -1399,7 +1448,8 @@ (let ((sign-and-body (js-split-sign s))) (let - ((sign (nth sign-and-body 0)) (body (nth sign-and-body 1))) + ((sign (nth sign-and-body 0)) + (body (nth sign-and-body 1))) (let ((stripped (js-strip-zeros-loop body 0 (len body)))) (if (= stripped "") (str sign "0") (str sign stripped))))))) @@ -1410,8 +1460,10 @@ (s) (cond ((= s "") (list "" "")) - ((= (char-at s 0) "-") (list "-" (js-string-slice s 1 (len s)))) - ((= (char-at s 0) "+") (list "" (js-string-slice s 1 (len s)))) + ((= (char-at s 0) "-") + (list "-" (js-string-slice s 1 (len s)))) + ((= (char-at s 0) "+") + (list "" (js-string-slice s 1 (len s)))) (else (list "" s))))) (define @@ -1459,7 +1511,9 @@ (define js-not (fn (a) (not (js-to-boolean a)))) -(define js-bitnot (fn (a) (- 0 (+ (js-num-to-int (js-to-number a)) 1)))) +(define + js-bitnot + (fn (a) (- 0 (+ (js-num-to-int (js-to-number a)) 1)))) (define js-strict-eq @@ -1511,7 +1565,9 @@ (define js-ge (fn (a b) (not (js-lt a b)))) -(define js-str-lt (fn (a b) (js-str-lt-at a b 0 (len a) (len b)))) +(define + js-str-lt + (fn (a b) (js-str-lt-at a b 0 (len a) (len b)))) (define js-str-lt-at @@ -1572,7 +1628,10 @@ (js-list-index-of arr (nth args 0) - (if (< (len args) 2) 0 (js-num-to-int (nth args 1))))))) + (if + (< (len args) 2) + 0 + (js-num-to-int (nth args 1))))))) ((= name "join") (fn (&rest args) @@ -1581,9 +1640,12 @@ (js-list-join arr sep)))) ((= name "concat") (fn (&rest args) (js-list-concat arr args))) ((= name "map") (fn (f) (js-list-map-loop f arr 0 (list)))) - ((= name "filter") (fn (f) (js-list-filter-loop f arr 0 (list)))) + ((= name "filter") + (fn (f) (js-list-filter-loop f arr 0 (list)))) ((= name "forEach") - (fn (f) (begin (js-list-foreach-loop f arr 0) js-undefined))) + (fn + (f) + (begin (js-list-foreach-loop f arr 0) js-undefined))) ((= name "reduce") (fn (&rest args) @@ -1592,17 +1654,29 @@ (if (= (len arr) 0) (error "Reduce of empty array with no initial value") - (js-list-reduce-loop (nth args 0) (nth arr 0) arr 1))) - (else (js-list-reduce-loop (nth args 0) (nth args 1) arr 0))))) + (js-list-reduce-loop + (nth args 0) + (nth arr 0) + arr + 1))) + (else + (js-list-reduce-loop + (nth args 0) + (nth args 1) + arr + 0))))) ((= name "includes") (fn (&rest args) (if (= (len args) 0) false - (>= (js-list-index-of arr (nth args 0) 0) 0)))) + (>= + (js-list-index-of arr (nth args 0) 0) + 0)))) ((= name "find") (fn (f) (js-list-find-loop f arr 0))) - ((= name "findIndex") (fn (f) (js-list-find-index-loop f arr 0))) + ((= name "findIndex") + (fn (f) (js-list-find-index-loop f arr 0))) ((= name "some") (fn (f) (js-list-some-loop f arr 0))) ((= name "every") (fn (f) (js-list-every-loop f arr 0))) ((= name "reverse") @@ -1618,7 +1692,11 @@ (&rest args) (let ((v (if (= (len args) 0) js-undefined (nth args 0))) - (s (if (< (len args) 2) 0 (js-num-to-int (nth args 1)))) + (s + (if + (< (len args) 2) + 0 + (js-num-to-int (nth args 1)))) (e (if (< (len args) 3) @@ -1662,7 +1740,11 @@ (&rest args) (let ((n (len arr)) - (start-raw (if (empty? args) 0 (js-num-to-int (nth args 0))))) + (start-raw + (if + (empty? args) + 0 + (js-num-to-int (nth args 0))))) (let ((start (cond ((< start-raw 0) (max 0 (+ n start-raw))) ((> start-raw n) n) (else start-raw)))) (let @@ -1677,7 +1759,9 @@ ((= name "findLast") (fn (f) (js-list-find-last-loop f arr (- (len arr) 1)))) ((= name "findLastIndex") - (fn (f) (js-list-find-last-index-loop f arr (- (len arr) 1)))) + (fn + (f) + (js-list-find-last-index-loop f arr (- (len arr) 1)))) ((= name "reduceRight") (fn (&rest args) @@ -1718,9 +1802,15 @@ (let ((n (len arr)) (target-raw - (if (empty? args) 0 (js-num-to-int (nth args 0)))) + (if + (empty? args) + 0 + (js-num-to-int (nth args 0)))) (start-raw - (if (< (len args) 2) 0 (js-num-to-int (nth args 1)))) + (if + (< (len args) 2) + 0 + (js-num-to-int (nth args 1)))) (end-raw (if (< (len args) 3) @@ -1730,7 +1820,8 @@ ((target (cond ((< target-raw 0) (max 0 (+ n target-raw))) (else (min n target-raw)))) (start (cond - ((< start-raw 0) (max 0 (+ n start-raw))) + ((< start-raw 0) + (max 0 (+ n start-raw))) (else (min n start-raw)))) (end (cond @@ -1760,7 +1851,11 @@ ((n (len arr))) (let ((s (if (< start 0) (max 0 (+ n start)) (min start n))) - (e (if (< stop 0) (max 0 (+ n stop)) (min stop n)))) + (e + (if + (< stop 0) + (max 0 (+ n stop)) + (min stop n)))) (js-list-slice-loop arr s e (list)))))) (define @@ -1790,7 +1885,11 @@ (cond ((= (len arr) 0) "") (else - (js-list-join-loop arr sep 1 (js-to-string-for-join (nth arr 0))))))) + (js-list-join-loop + arr + sep + 1 + (js-to-string-for-join (nth arr 0))))))) (define js-to-string-for-join @@ -1859,7 +1958,8 @@ (f arr i) (cond ((>= i (len arr)) nil) - (else (do (f (nth arr i)) (js-list-foreach-loop f arr (+ i 1))))))) + (else + (do (f (nth arr i)) (js-list-foreach-loop f arr (+ i 1))))))) (define js-list-reduce-loop @@ -1867,7 +1967,8 @@ (f acc arr i) (cond ((>= i (len arr)) acc) - (else (js-list-reduce-loop f (f acc (nth arr i)) arr (+ i 1)))))) + (else + (js-list-reduce-loop f (f acc (nth arr i)) arr (+ i 1)))))) (define js-list-find-loop @@ -1918,11 +2019,15 @@ ((>= s e) nil) ((>= s (len arr)) nil) (else - (begin (js-list-set! arr s v) (js-list-fill-loop arr v (+ s 1) e)))))) + (begin + (js-list-set! arr s v) + (js-list-fill-loop arr v (+ s 1) e)))))) (define js-list-sort! - (fn (arr cmp) (let ((n (len arr))) (js-list-sort-outer! arr cmp 0 n)))) + (fn + (arr cmp) + (let ((n (len arr))) (js-list-sort-outer! arr cmp 0 n)))) (define js-list-sort-outer! @@ -1949,7 +2054,9 @@ ((result (if (= cmp nil) (if (js-str-lt (js-to-string b) (js-to-string a)) 1 -1) (js-to-number (cmp a b))))) (when (> result 0) - (begin (js-list-set! arr i b) (js-list-set! arr (+ i 1) a))))) + (begin + (js-list-set! arr i b) + (js-list-set! arr (+ i 1) a))))) (js-list-sort-inner! arr cmp (+ i 1) end)))))) (define @@ -2015,7 +2122,9 @@ (if (>= i (len arr)) result - (begin (append! result i) (js-list-keys-loop arr (+ i 1) result))))) + (begin + (append! result i) + (js-list-keys-loop arr (+ i 1) result))))) (define js-list-entries-loop @@ -2052,7 +2161,10 @@ js-string-repeat (fn (s n acc) - (if (<= n 0) acc (js-string-repeat s (- n 1) (str acc s))))) + (if + (<= n 0) + acc + (js-string-repeat s (- n 1) (str acc s))))) (define js-string-pad @@ -2379,7 +2491,11 @@ ((n (len s))) (let ((lo (if (< start 0) (max 0 (+ n start)) (min start n))) - (hi (if (< stop 0) (max 0 (+ n stop)) (min stop n)))) + (hi + (if + (< stop 0) + (max 0 (+ n stop)) + (min stop n)))) (if (>= lo hi) "" (js-string-slice-loop s lo hi "")))))) (define @@ -2388,7 +2504,8 @@ (s i e acc) (cond ((>= i e) acc) - (else (js-string-slice-loop s (+ i 1) e (str acc (char-at s i))))))) + (else + (js-string-slice-loop s (+ i 1) e (str acc (char-at s i))))))) (define js-string-index-of @@ -2738,7 +2855,9 @@ js-math-trunc (fn (x) - (let ((n (js-to-number x))) (if (< n 0) (ceil n) (floor n))))) + (let + ((n (js-to-number x))) + (if (< n 0) (ceil n) (floor n))))) (define js-math-sign @@ -2746,7 +2865,10 @@ (x) (let ((n (js-to-number x))) - (cond ((> n 0) 1) ((< n 0) -1) (else n))))) + (cond + ((> n 0) 1) + ((< n 0) -1) + (else n))))) (define js-math-cbrt @@ -2754,9 +2876,14 @@ (x) (let ((n (js-to-number x))) - (if (< n 0) (- 0 (pow (- 0 n) (/ 1 3))) (pow n (/ 1 3)))))) + (if + (< n 0) + (- 0 (pow (- 0 n) (/ 1 3))) + (pow n (/ 1 3)))))) -(define js-math-hypot (fn (&rest args) (sqrt (js-math-hypot-loop args 0)))) +(define + js-math-hypot + (fn (&rest args) (sqrt (js-math-hypot-loop args 0)))) (define js-math-hypot-loop @@ -2811,7 +2938,7 @@ ((result (modulo (* a32 b32) 4294967296))) (if (>= result 2147483648) (- result 4294967296) result))))) (define js-math-fround (fn (x) (js-to-number x))) - (define Math {:trunc js-math-trunc :expm1 js-math-expm1 :atan2 js-math-atan2 :PI 3.14159 :asinh js-math-asinh :acosh js-math-acosh :hypot js-math-hypot :LOG2E 1.4427 :atanh js-math-atanh :ceil js-math-ceil :pow js-math-pow :sin js-math-sin :max js-math-max :log2 js-math-log2 :SQRT2 1.41421 :cbrt js-math-cbrt :log1p js-math-log1p :fround js-math-fround :E 2.71828 :sinh js-math-sinh :random js-math-random :LN10 2.30259 :SQRT1_2 0.707107 :asin js-math-asin :clz32 js-math-clz32 :floor js-math-floor :exp js-math-exp :tan js-math-tan :sqrt js-math-sqrt :cosh js-math-cosh :log js-math-log :round js-math-round :abs js-math-abs :LOG10E 0.434294 :tanh js-math-tanh :acos js-math-acos :log10 js-math-log10 :min js-math-min :sign js-math-sign :LN2 0.693147 :cos js-math-cos :imul js-math-imul :atan js-math-atan})) + (define Math {:atan js-math-atan :sign js-math-sign :LN2 0.693147 :cos js-math-cos :imul js-math-imul :min js-math-min :acos js-math-acos :log10 js-math-log10 :LOG10E 0.434294 :tanh js-math-tanh :abs js-math-abs :round js-math-round :log js-math-log :sqrt js-math-sqrt :cosh js-math-cosh :tan js-math-tan :floor js-math-floor :exp js-math-exp :asin js-math-asin :clz32 js-math-clz32 :random js-math-random :LN10 2.30259 :SQRT1_2 0.707107 :sinh js-math-sinh :E 2.71828 :fround js-math-fround :cbrt js-math-cbrt :log1p js-math-log1p :SQRT2 1.41421 :max js-math-max :log2 js-math-log2 :ceil js-math-ceil :pow js-math-pow :sin js-math-sin :hypot js-math-hypot :LOG2E 1.4427 :atanh js-math-atanh :asinh js-math-asinh :acosh js-math-acosh :PI 3.14159 :atan2 js-math-atan2 :trunc js-math-trunc :expm1 js-math-expm1})) (define js-number-is-finite @@ -2837,9 +2964,7 @@ (define js-number-is-safe-integer - (fn - (v) - (and (js-number-is-integer v) (<= (js-math-abs v) 9007199254740991)))) + (fn (v) (and (js-number-is-integer v) (<= (js-math-abs v) 9007199254740991)))) (define js-global-is-finite @@ -2847,7 +2972,7 @@ (define js-global-is-nan (fn (v) (js-number-is-nan (js-to-number v)))) -(define Number {:isFinite js-number-is-finite :MAX_SAFE_INTEGER 9007199254740991 :EPSILON 2.22045e-16 :MAX_VALUE (js-max-value-approx) :POSITIVE_INFINITY (js-infinity-value) :__callable__ js-to-number :isInteger js-number-is-integer :prototype {:valueOf (fn () (js-this)) :toPrecision (fn (&rest args) (js-to-string (js-this))) :toString (fn (&rest args) (let ((this-val (js-this)) (radix (if (empty? args) 10 (js-to-number (nth args 0))))) (js-num-to-str-radix this-val (if (or (= radix nil) (js-undefined? radix)) 10 radix)))) :toLocaleString (fn () (js-to-string (js-this))) :toFixed (fn (d) (js-number-to-fixed (js-this) (if (= d nil) 0 (js-to-number d)))) :toExponential (fn (&rest args) (js-to-string (js-this)))} :isNaN js-number-is-nan :isSafeInteger js-number-is-safe-integer :NEGATIVE_INFINITY (- 0 (js-infinity-value)) :NaN (js-nan-value) :MIN_VALUE 4.94066e-324 :MIN_SAFE_INTEGER -9007199254740991}) +(define Number {:MIN_SAFE_INTEGER -9007199254740991 :MIN_VALUE 4.94066e-324 :isNaN js-number-is-nan :isSafeInteger js-number-is-safe-integer :NEGATIVE_INFINITY (- 0 (js-infinity-value)) :NaN (js-nan-value) :prototype {:toFixed (fn (d) (js-number-to-fixed (js-this) (if (= d nil) 0 (js-to-number d)))) :toExponential (fn (&rest args) (js-to-string (js-this))) :toLocaleString (fn () (js-to-string (js-this))) :toString (fn (&rest args) (let ((this-val (js-this)) (radix (if (empty? args) 10 (js-to-number (nth args 0))))) (js-num-to-str-radix this-val (if (or (= radix nil) (js-undefined? radix)) 10 radix)))) :toPrecision (fn (&rest args) (js-to-string (js-this))) :valueOf (fn () (js-this))} :isInteger js-number-is-integer :__callable__ js-to-number :MAX_VALUE (js-max-value-approx) :POSITIVE_INFINITY (js-infinity-value) :isFinite js-number-is-finite :MAX_SAFE_INTEGER 9007199254740991 :EPSILON 2.22045e-16}) (dict-set! Number "length" 1) @@ -3048,7 +3173,9 @@ (if (>= i (len s)) acc - (begin (append! acc (char-at s i)) (js-string-to-list s (+ i 1) acc))))) + (begin + (append! acc (char-at s i)) + (js-string-to-list s (+ i 1) acc))))) (define js-object-keys @@ -3149,7 +3276,10 @@ (for-each (fn (k) - (dict-set! obj k (get (get (nth args 1) k) "value"))) + (dict-set! + obj + k + (get (get (nth args 1) k) "value"))) (keys (nth args 1)))) obj))))) @@ -3180,7 +3310,8 @@ (fn (o) (cond - ((list? o) (let ((r (list))) (begin (js-list-keys-loop o 0 r) r))) + ((list? o) + (let ((r (list))) (begin (js-list-keys-loop o 0 r) r))) ((dict? o) (js-object-keys o)) (else (list))))) @@ -3190,7 +3321,7 @@ (o key) (if (and (dict? o) (contains? (keys o) (js-to-string key))) - {:writable true :value (get o (js-to-string key)) :enumerable true :configurable true} + {:configurable true :enumerable true :value (get o (js-to-string key)) :writable true} :js-undefined))) (define @@ -3241,7 +3372,10 @@ (pair) (when (and (list? pair) (>= (len pair) 2)) - (dict-set! out (js-to-string (nth pair 0)) (nth pair 1)))) + (dict-set! + out + (js-to-string (nth pair 0)) + (nth pair 1)))) lst) out)))) @@ -3257,7 +3391,7 @@ (and (>= idx 0) (< idx (len o)) (integer? idx)))) (else false)))) -(define Object {:entries js-object-entries :defineProperties js-object-define-properties :__callable__ (fn (&rest args) (cond ((= (len args) 0) (dict)) (else (nth args 0)))) :preventExtensions js-object-prevent-extensions :prototype {:valueOf (fn () (js-this)) :propertyIsEnumerable (fn (k) (let ((o (js-this))) (js-object-has-own o k))) :isPrototypeOf (fn (o) (let ((this-val (js-this))) (cond ((not (dict? o)) false) (else (let ((proto (if (contains? (keys o) "__proto__") (get o "__proto__") nil))) (cond ((= proto this-val) true) ((= proto nil) false) (else ((get (get Object "prototype") "isPrototypeOf") proto)))))))) :toString (fn () "[object Object]") :hasOwnProperty (fn (k) (let ((o (js-this))) (js-object-has-own o k))) :toLocaleString (fn () "[object Object]")} :values js-object-values :hasOwn js-object-has-own :freeze js-object-freeze :assign js-object-assign :isFrozen js-object-is-frozen :getOwnPropertyDescriptor js-object-get-own-property-descriptor :fromEntries js-object-from-entries :defineProperty js-object-define-property :setPrototypeOf js-object-set-prototype-of :getOwnPropertyNames js-object-get-own-property-names :getOwnPropertyDescriptors js-object-get-own-property-descriptors :create js-object-create :isExtensible js-object-is-extensible :is js-object-is :keys js-object-keys :getPrototypeOf js-object-get-prototype-of :isSealed js-object-is-sealed :seal js-object-seal}) +(define Object {:keys js-object-keys :getPrototypeOf js-object-get-prototype-of :isSealed js-object-is-sealed :seal js-object-seal :create js-object-create :isExtensible js-object-is-extensible :is js-object-is :setPrototypeOf js-object-set-prototype-of :getOwnPropertyNames js-object-get-own-property-names :getOwnPropertyDescriptors js-object-get-own-property-descriptors :defineProperty js-object-define-property :fromEntries js-object-from-entries :getOwnPropertyDescriptor js-object-get-own-property-descriptor :assign js-object-assign :isFrozen js-object-is-frozen :freeze js-object-freeze :values js-object-values :hasOwn js-object-has-own :prototype {:hasOwnProperty (fn (k) (let ((o (js-this))) (js-object-has-own o k))) :toLocaleString (fn () "[object Object]") :isPrototypeOf (fn (o) (let ((this-val (js-this))) (cond ((not (dict? o)) false) (else (let ((proto (if (contains? (keys o) "__proto__") (get o "__proto__") nil))) (cond ((= proto this-val) true) ((= proto nil) false) (else ((get (get Object "prototype") "isPrototypeOf") proto)))))))) :toString (fn () "[object Object]") :propertyIsEnumerable (fn (k) (let ((o (js-this))) (js-object-has-own o k))) :valueOf (fn () (js-this))} :__callable__ (fn (&rest args) (cond ((= (len args) 0) (dict)) (else (nth args 0)))) :preventExtensions js-object-prevent-extensions :entries js-object-entries :defineProperties js-object-define-properties}) (dict-set! Object "length" 1) @@ -3337,7 +3471,8 @@ (else (let ((src (js-iterable-to-list (nth args 0))) - (map-fn (if (< (len args) 2) nil (nth args 1)))) + (map-fn + (if (< (len args) 2) nil (nth args 1)))) (if (= map-fn nil) (let @@ -3347,11 +3482,14 @@ (let ((result (list)) (i 0)) (for-each - (fn (x) (append! result (map-fn x)) (set! i (+ i 1))) + (fn + (x) + (append! result (map-fn x)) + (set! i (+ i 1))) src) result))))))) -(define Array {:__callable__ (fn (&rest args) (cond ((= (len args) 0) (list)) ((and (= (len args) 1) (number? (nth args 0))) (js-make-list-of-length (js-num-to-int (nth args 0)) :js-undefined)) (else args))) :prototype {:entries (js-array-proto-fn "entries") :concat (js-array-proto-fn "concat") :lastIndexOf (js-array-proto-fn "lastIndexOf") :splice (js-array-proto-fn "splice") :filter (js-array-proto-fn "filter") :findLast (js-array-proto-fn "findLast") :shift (js-array-proto-fn "shift") :join (js-array-proto-fn "join") :reduceRight (js-array-proto-fn "reduceRight") :values (js-array-proto-fn "values") :reduce (js-array-proto-fn "reduce") :slice (js-array-proto-fn "slice") :includes (js-array-proto-fn "includes") :findLastIndex (js-array-proto-fn "findLastIndex") :find (js-array-proto-fn "find") :toLocaleString (js-array-proto-fn "toLocaleString") :findIndex (js-array-proto-fn "findIndex") :sort (js-array-proto-fn "sort") :every (js-array-proto-fn "every") :indexOf (js-array-proto-fn "indexOf") :unshift (js-array-proto-fn "unshift") :push (js-array-proto-fn "push") :map (js-array-proto-fn "map") :some (js-array-proto-fn "some") :flat (js-array-proto-fn "flat") :toSorted (js-array-proto-fn "toSorted") :at (js-array-proto-fn "at") :pop (js-array-proto-fn "pop") :toReversed (js-array-proto-fn "toReversed") :copyWithin (js-array-proto-fn "copyWithin") :toString (js-array-proto-fn "toString") :forEach (js-array-proto-fn "forEach") :fill (js-array-proto-fn "fill") :flatMap (js-array-proto-fn "flatMap") :keys (js-array-proto-fn "keys") :reverse (js-array-proto-fn "reverse")} :isArray js-array-is-array :of js-array-of :from js-array-from}) +(define Array {:of js-array-of :from js-array-from :isArray js-array-is-array :prototype {:reverse (js-array-proto-fn "reverse") :fill (js-array-proto-fn "fill") :flatMap (js-array-proto-fn "flatMap") :keys (js-array-proto-fn "keys") :forEach (js-array-proto-fn "forEach") :toString (js-array-proto-fn "toString") :copyWithin (js-array-proto-fn "copyWithin") :toReversed (js-array-proto-fn "toReversed") :pop (js-array-proto-fn "pop") :at (js-array-proto-fn "at") :push (js-array-proto-fn "push") :map (js-array-proto-fn "map") :some (js-array-proto-fn "some") :flat (js-array-proto-fn "flat") :toSorted (js-array-proto-fn "toSorted") :indexOf (js-array-proto-fn "indexOf") :unshift (js-array-proto-fn "unshift") :every (js-array-proto-fn "every") :sort (js-array-proto-fn "sort") :findIndex (js-array-proto-fn "findIndex") :toLocaleString (js-array-proto-fn "toLocaleString") :find (js-array-proto-fn "find") :includes (js-array-proto-fn "includes") :findLastIndex (js-array-proto-fn "findLastIndex") :slice (js-array-proto-fn "slice") :reduce (js-array-proto-fn "reduce") :values (js-array-proto-fn "values") :join (js-array-proto-fn "join") :reduceRight (js-array-proto-fn "reduceRight") :shift (js-array-proto-fn "shift") :filter (js-array-proto-fn "filter") :findLast (js-array-proto-fn "findLast") :concat (js-array-proto-fn "concat") :lastIndexOf (js-array-proto-fn "lastIndexOf") :splice (js-array-proto-fn "splice") :entries (js-array-proto-fn "entries")} :__callable__ (fn (&rest args) (cond ((= (len args) 0) (list)) ((and (= (len args) 1) (number? (nth args 0))) (js-make-list-of-length (js-num-to-int (nth args 0)) :js-undefined)) (else args)))}) (dict-set! Array "length" 1) @@ -3431,7 +3569,7 @@ ((s (cond ((= (type-of this-val) "string") this-val) ((and (= (type-of this-val) "dict") (contains? (keys this-val) "__js_string_value__")) (get this-val "__js_string_value__")) (else "[object Object]")))) (js-invoke-method s name args)))))) -(define String {:fromCharCode js-string-from-char-code :__callable__ (fn (&rest args) (if (= (len args) 0) "" (js-to-string (nth args 0)))) :prototype {:toLowerCase (js-string-proto-fn "toLowerCase") :concat (js-string-proto-fn "concat") :startsWith (js-string-proto-fn "startsWith") :padEnd (js-string-proto-fn "padEnd") :codePointAt (js-string-proto-fn "codePointAt") :lastIndexOf (js-string-proto-fn "lastIndexOf") :indexOf (js-string-proto-fn "indexOf") :localeCompare (js-string-proto-fn "localeCompare") :split (js-string-proto-fn "split") :endsWith (js-string-proto-fn "endsWith") :trim (js-string-proto-fn "trim") :valueOf (js-string-proto-fn "valueOf") :at (js-string-proto-fn "at") :normalize (js-string-proto-fn "normalize") :substring (js-string-proto-fn "substring") :replaceAll (js-string-proto-fn "replaceAll") :repeat (js-string-proto-fn "repeat") :padStart (js-string-proto-fn "padStart") :search (js-string-proto-fn "search") :toUpperCase (js-string-proto-fn "toUpperCase") :trimEnd (js-string-proto-fn "trimEnd") :toString (js-string-proto-fn "toString") :toLocaleLowerCase (js-string-proto-fn "toLocaleLowerCase") :charCodeAt (js-string-proto-fn "charCodeAt") :slice (js-string-proto-fn "slice") :charAt (js-string-proto-fn "charAt") :match (js-string-proto-fn "match") :includes (js-string-proto-fn "includes") :trimStart (js-string-proto-fn "trimStart") :toLocaleUpperCase (js-string-proto-fn "toLocaleUpperCase") :replace (js-string-proto-fn "replace")} :raw (fn (&rest args) (if (empty? args) "" (js-to-string (nth args 0))))}) +(define String {:raw (fn (&rest args) (if (empty? args) "" (js-to-string (nth args 0)))) :prototype {:replace (js-string-proto-fn "replace") :toLocaleUpperCase (js-string-proto-fn "toLocaleUpperCase") :trimStart (js-string-proto-fn "trimStart") :includes (js-string-proto-fn "includes") :charAt (js-string-proto-fn "charAt") :match (js-string-proto-fn "match") :charCodeAt (js-string-proto-fn "charCodeAt") :slice (js-string-proto-fn "slice") :toString (js-string-proto-fn "toString") :toLocaleLowerCase (js-string-proto-fn "toLocaleLowerCase") :toUpperCase (js-string-proto-fn "toUpperCase") :trimEnd (js-string-proto-fn "trimEnd") :repeat (js-string-proto-fn "repeat") :padStart (js-string-proto-fn "padStart") :search (js-string-proto-fn "search") :substring (js-string-proto-fn "substring") :replaceAll (js-string-proto-fn "replaceAll") :trim (js-string-proto-fn "trim") :valueOf (js-string-proto-fn "valueOf") :at (js-string-proto-fn "at") :normalize (js-string-proto-fn "normalize") :split (js-string-proto-fn "split") :endsWith (js-string-proto-fn "endsWith") :indexOf (js-string-proto-fn "indexOf") :localeCompare (js-string-proto-fn "localeCompare") :toLowerCase (js-string-proto-fn "toLowerCase") :concat (js-string-proto-fn "concat") :startsWith (js-string-proto-fn "startsWith") :padEnd (js-string-proto-fn "padEnd") :codePointAt (js-string-proto-fn "codePointAt") :lastIndexOf (js-string-proto-fn "lastIndexOf")} :__callable__ (fn (&rest args) (if (= (len args) 0) "" (js-to-string (nth args 0)))) :fromCharCode js-string-from-char-code}) (dict-set! String "length" 1) @@ -3528,7 +3666,10 @@ (let ((s (js-to-string (nth args 0))) (radix-arg - (if (< (len args) 2) 10 (js-to-number (nth args 1))))) + (if + (< (len args) 2) + 10 + (js-to-number (nth args 1))))) (let ((radix (if (or (js-number-is-nan radix-arg) (= radix-arg 0)) 10 radix-arg))) (js-parse-int-str (js-trim s) (js-math-trunc radix)))))))) @@ -3634,13 +3775,20 @@ ((prev (char-at s (- i 1)))) (if (or (= prev "e") (= prev "E")) - (js-float-prefix-end s (+ i 1) sawdigit sawdot sawe) + (js-float-prefix-end + s + (+ i 1) + sawdigit + sawdot + sawe) i))) (else i))))))) (define encodeURIComponent - (fn (v) (let ((s (js-to-string v))) (js-uri-encode-loop s 0 "")))) + (fn + (v) + (let ((s (js-to-string v))) (js-uri-encode-loop s 0 "")))) (define decodeURIComponent (fn (v) (js-to-string v))) @@ -3660,7 +3808,8 @@ (let ((code (char-code c))) (cond - ((= c " ") (js-uri-encode-loop s (+ i 1) (str acc "%20"))) + ((= c " ") + (js-uri-encode-loop s (+ i 1) (str acc "%20"))) ((and (>= code 48) (<= code 57)) (js-uri-encode-loop s (+ i 1) (str acc c))) ((and (>= code 65) (<= code 90)) @@ -3670,7 +3819,10 @@ ((or (= c "-") (= c "_") (= c ".") (= c "~") (= c "!") (= c "*") (= c "'") (= c "(") (= c ")")) (js-uri-encode-loop s (+ i 1) (str acc c))) (else - (js-uri-encode-loop s (+ i 1) (str acc "%" (js-hex-2 code))))))))))) + (js-uri-encode-loop + s + (+ i 1) + (str acc "%" (js-hex-2 code))))))))))) (define js-hex-2 @@ -3684,7 +3836,9 @@ js-hex-digit (fn (d) - (cond ((< d 10) (js-to-string d)) (else (js-code-to-char (+ 55 d)))))) + (cond + ((< d 10) (js-to-string d)) + (else (js-code-to-char (+ 55 d)))))) (define js-json-stringify @@ -3750,11 +3904,16 @@ (let ((c (char-at s i))) (cond - ((= c "\"") (js-json-escape-loop s (+ i 1) (str acc "\\\""))) - ((= c "\\") (js-json-escape-loop s (+ i 1) (str acc "\\\\"))) - ((= c "\n") (js-json-escape-loop s (+ i 1) (str acc "\\n"))) - ((= c "\r") (js-json-escape-loop s (+ i 1) (str acc "\\r"))) - ((= c "\t") (js-json-escape-loop s (+ i 1) (str acc "\\t"))) + ((= c "\"") + (js-json-escape-loop s (+ i 1) (str acc "\\\""))) + ((= c "\\") + (js-json-escape-loop s (+ i 1) (str acc "\\\\"))) + ((= c "\n") + (js-json-escape-loop s (+ i 1) (str acc "\\n"))) + ((= c "\r") + (js-json-escape-loop s (+ i 1) (str acc "\\r"))) + ((= c "\t") + (js-json-escape-loop s (+ i 1) (str acc "\\t"))) (else (js-json-escape-loop s (+ i 1) (str acc c)))))))) (define @@ -3794,9 +3953,12 @@ ((= (char-at s i) "\"") (js-json-parse-string st)) ((= (char-at s i) "[") (js-json-parse-array st)) ((= (char-at s i) "{") (js-json-parse-object st)) - ((= (char-at s i) "t") (begin (dict-set! st "i" (+ i 4)) true)) - ((= (char-at s i) "f") (begin (dict-set! st "i" (+ i 5)) false)) - ((= (char-at s i) "n") (begin (dict-set! st "i" (+ i 4)) nil)) + ((= (char-at s i) "t") + (begin (dict-set! st "i" (+ i 4)) true)) + ((= (char-at s i) "f") + (begin (dict-set! st "i" (+ i 5)) false)) + ((= (char-at s i) "n") + (begin (dict-set! st "i" (+ i 4)) nil)) (else (js-json-parse-number st)))))) (define @@ -3935,7 +4097,7 @@ ((= c "}") (dict-set! st "i" (+ (get st "i") 1))) (else (error "JSON: expected , or }"))))))) -(define JSON {:parse js-json-parse :stringify js-json-stringify}) +(define JSON {:stringify js-json-stringify :parse js-json-parse}) (define js-promise-flush-callbacks! @@ -4038,7 +4200,11 @@ (p args) (let ((on-f (if (>= (len args) 1) (nth args 0) :js-undefined)) - (on-r (if (>= (len args) 2) (nth args 1) :js-undefined))) + (on-r + (if + (>= (len args) 2) + (nth args 1) + :js-undefined))) (js-promise-then-internal! p on-f on-r)))) (define @@ -4137,7 +4303,9 @@ (cond ((<= n 0) acc) (else - (begin (append! acc fill) (js-make-list-loop acc (- n 1) fill)))))) + (begin + (append! acc fill) + (js-make-list-loop acc (- n 1) fill)))))) (define js-promise-all-loop! @@ -4157,7 +4325,10 @@ (let ((results (get state "results"))) (set-nth! results i v) - (dict-set! state "remaining" (- (get state "remaining") 1)) + (dict-set! + state + "remaining" + (- (get state "remaining") 1)) (cond ((= (get state "remaining") 0) (js-promise-resolve! result-p results)) @@ -4173,7 +4344,8 @@ ((items (if (empty? args) (list) (first args))) (p (js-make-promise))) (cond - ((= (len items) 0) (begin (js-promise-resolve! p (list)) p)) + ((= (len items) 0) + (begin (js-promise-resolve! p (list)) p)) (else (let ((n (len items)) (state (dict))) @@ -4332,7 +4504,11 @@ ((= name "test") (let ((impl (get __js_regex_platform__ "test")) - (arg (if (= (len args) 0) "" (js-to-string (nth args 0))))) + (arg + (if + (= (len args) 0) + "" + (js-to-string (nth args 0))))) (if (js-undefined? impl) (js-regex-stub-test rx arg) @@ -4340,7 +4516,11 @@ ((= name "exec") (let ((impl (get __js_regex_platform__ "exec")) - (arg (if (= (len args) 0) "" (js-to-string (nth args 0))))) + (arg + (if + (= (len args) 0) + "" + (js-to-string (nth args 0))))) (if (js-undefined? impl) (js-regex-stub-exec rx arg) @@ -4349,4 +4529,11 @@ (str "/" (get rx "source") "/" (get rx "flags"))) (else js-undefined)))) -(define js-global {:isFinite js-global-is-finite :console console :Number Number :parseFloat parseFloat :Math Math :Array Array :Boolean Boolean :String String :NaN 0 :Infinity inf :isNaN js-global-is-nan :Object Object :parseInt parseInt :JSON JSON :undefined js-undefined}) +(begin + (dict-set! Object "__proto__" (get js-function-global "prototype")) + (dict-set! Array "__proto__" (get js-function-global "prototype")) + (dict-set! Number "__proto__" (get js-function-global "prototype")) + (dict-set! String "__proto__" (get js-function-global "prototype")) + (dict-set! Boolean "__proto__" (get js-function-global "prototype"))) + +(define js-global {:undefined js-undefined :JSON JSON :parseInt parseInt :Object Object :isNaN js-global-is-nan :Infinity inf :NaN 0 :String String :Boolean Boolean :Array Array :Math Math :parseFloat parseFloat :Number Number :console console :isFinite js-global-is-finite}) diff --git a/lib/js/test262-scoreboard.json b/lib/js/test262-scoreboard.json index e52d87a4..753f9b70 100644 --- a/lib/js/test262-scoreboard.json +++ b/lib/js/test262-scoreboard.json @@ -1,26 +1,26 @@ { "totals": { - "pass": 77, - "fail": 16, + "pass": 78, + "fail": 15, "skip": 1, "timeout": 6, "total": 100, "runnable": 99, - "pass_rate": 77.8 + "pass_rate": 78.8 }, "categories": [ { "category": "built-ins/String", "total": 100, - "pass": 77, - "fail": 16, + "pass": 78, + "fail": 15, "skip": 1, "timeout": 6, - "pass_rate": 77.8, + "pass_rate": 78.8, "top_failures": [ [ "Test262Error (assertion failed)", - 14 + 13 ], [ "Timeout", @@ -40,7 +40,7 @@ "top_failure_modes": [ [ "Test262Error (assertion failed)", - 14 + 13 ], [ "Timeout", @@ -56,6 +56,6 @@ ] ], "pinned_commit": "d5e73fc8d2c663554fb72e2380a8c2bc1a318a33", - "elapsed_seconds": 250.0, + "elapsed_seconds": 273.6, "workers": 1 } \ No newline at end of file diff --git a/lib/js/test262-scoreboard.md b/lib/js/test262-scoreboard.md index d9417b28..385613d2 100644 --- a/lib/js/test262-scoreboard.md +++ b/lib/js/test262-scoreboard.md @@ -1,13 +1,13 @@ # test262 scoreboard Pinned commit: `d5e73fc8d2c663554fb72e2380a8c2bc1a318a33` -Wall time: 250.0s +Wall time: 273.6s -**Total:** 77/99 runnable passed (77.8%). Raw: pass=77 fail=16 skip=1 timeout=6 total=100. +**Total:** 78/99 runnable passed (78.8%). Raw: pass=78 fail=15 skip=1 timeout=6 total=100. ## Top failure modes -- **14x** Test262Error (assertion failed) +- **13x** Test262Error (assertion failed) - **6x** Timeout - **1x** ReferenceError (undefined symbol) - **1x** SyntaxError (parse/unsupported syntax) @@ -16,13 +16,13 @@ Wall time: 250.0s | Category | Pass | Fail | Skip | Timeout | Total | Pass % | |---|---:|---:|---:|---:|---:|---:| -| built-ins/String | 77 | 16 | 1 | 6 | 100 | 77.8% | +| built-ins/String | 78 | 15 | 1 | 6 | 100 | 78.8% | ## Per-category top failures (min 10 runnable, worst first) -### built-ins/String (77/99 — 77.8%) +### built-ins/String (78/99 — 78.8%) -- **14x** Test262Error (assertion failed) +- **13x** Test262Error (assertion failed) - **6x** Timeout - **1x** ReferenceError (undefined symbol) - **1x** SyntaxError (parse/unsupported syntax) diff --git a/plans/js-on-sx.md b/plans/js-on-sx.md index eef5a9b6..cc919152 100644 --- a/plans/js-on-sx.md +++ b/plans/js-on-sx.md @@ -158,6 +158,8 @@ Each item: implement → tests → update progress. Mark `[x]` when tests green. Append-only record of completed iterations. Loop writes one line per iteration: date, what was done, test count delta. +- 2026-05-07 — **Constructors (`Object`/`Array`/`Number`/`String`/`Boolean`) carry `__proto__ = Function.prototype`.** Per spec, the constructors are functions and inherit from `Function.prototype`, so `Function.prototype.foo = 1; Array.foo === 1`. Previously the constructor dicts had no `__proto__`, so they only saw `Object.prototype` via the recent fallback — `Function.prototype` mutations were invisible. Added a `(begin (dict-set! ...))` post-init at the end of `runtime.sx` after the constructors are defined. Combined with the existing Object.prototype fallback, the proto chain now terminates correctly for the constructor → `Function.prototype` → `Object.prototype` walk. built-ins/Number: 41/50 → 42/50, built-ins/String: 75/99 → 78/99, built-ins/Array: 12/45 → 13/45. conformance.sh: 148/148. + - 2026-05-07 — **`js-neg` preserves IEEE-754 negative zero.** `-0` was returning `0` (rational integer) because `js-neg` did `(- 0 (js-to-number a))`, which loses sign-of-zero in any arithmetic implementation that follows IEEE 754. Per JS spec, `-0` and `1/-0 === -Infinity` must be observable. Switched to `(* -1 (exact->inexact (js-to-number a)))` so the result is always a float and `-0.0` is preserved. Fixes `Math.asinh(-0)` and other `-0`-sensitive tests; `1/(-0) === -Infinity` now works. built-ins/Math: 41/45 → 42/45. conformance.sh: 148/148. - 2026-05-07 — **`js-div` coerces divisor to inexact before dividing.** When both operands are SX rationals (e.g. `(js-div 1 0)` from JS-transpiled `1/0` reaching the harness's `_isSameValue` +0/-0 check), SX integer-rational division throws "rational: division by zero" instead of producing JS `Infinity`. Wrapped the divisor in `(exact->inexact ...)` so it's always a float; integer-by-zero now returns `inf` (positive numerator), `-inf` (negative), `nan` (zero numerator), matching JS semantics. Was hitting harness assertion failures even when the test value matched expected. built-ins/Number: 37/50 → 41/50. built-ins/String: 77/99. conformance.sh: 148/148.