js-on-sx: wrapper constructor-detection, Array.prototype.toString, >>> operator
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 11s

Number.__callable__ and String.__callable__ now check this.__proto__ ===
Number/String.prototype before writing wrapper slots, preventing false-positive
mutation when called as plain function. js-to-number extended to unwrap
wrapper dicts and call valueOf/toString for plain objects. Array.prototype.toString
replaced with a direct js-list-join implementation (eliminates infinite recursion
via js-invoke-method on dict-based arrays). >>> added to transpiler + runtime.

String test262 subset: 62→66/100. 529/530 unit, 147/148 slice.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-25 19:22:53 +00:00
parent ea63b6d9bb
commit 97180b4aa3
5 changed files with 232 additions and 54 deletions

View File

@@ -904,6 +904,36 @@
((= v false) 0)
((= (type-of v) "number") v)
((= (type-of v) "string") (js-string-to-number v))
((= (type-of v) "dict")
(cond
((contains? (keys v) "__js_number_value__")
(get v "__js_number_value__"))
((contains? (keys v) "__js_boolean_value__")
(if (get v "__js_boolean_value__") 1 0))
((contains? (keys v) "__js_string_value__")
(js-string-to-number (get v "__js_string_value__")))
(else
(let
((valueof-fn (js-get-prop v "valueOf")))
(if
(= (type-of valueof-fn) "lambda")
(let
((result (js-call-with-this v valueof-fn ())))
(if
(not (= (type-of result) "dict"))
(js-to-number result)
(let
((tostr-fn (js-get-prop v "toString")))
(if
(= (type-of tostr-fn) "lambda")
(let
((result2 (js-call-with-this v tostr-fn ())))
(if
(not (= (type-of result2) "dict"))
(js-to-number result2)
(js-nan-value)))
(js-nan-value)))))
(js-nan-value))))))
(else 0))))
(define
@@ -1172,10 +1202,35 @@
(else
(if
(= (type-of v) "dict")
(if
(contains? (keys v) "__js_string_value__")
(get v "__js_string_value__")
"[object Object]")
(cond
((contains? (keys v) "__js_string_value__")
(get v "__js_string_value__"))
((contains? (keys v) "__js_number_value__")
(js-number-to-string (get v "__js_number_value__")))
((contains? (keys v) "__js_boolean_value__")
(if (get v "__js_boolean_value__") "true" "false"))
(else
(let
((tostr-fn (js-get-prop v "toString")))
(if
(= (type-of tostr-fn) "lambda")
(let
((result (js-call-with-this v tostr-fn ())))
(if
(= (type-of result) "dict")
(let
((valueof-fn (js-get-prop v "valueOf")))
(if
(= (type-of valueof-fn) "lambda")
(let
((result2 (js-call-with-this v valueof-fn ())))
(if
(= (type-of result2) "dict")
"[object Object]"
(js-to-string result2)))
"[object Object]"))
(js-to-string result)))
"[object Object]"))))
(str v))))))
(define
@@ -1348,6 +1403,15 @@
(define js-mod (fn (a b) (mod (js-to-number a) (js-to-number b))))
(define
js-unsigned-rshift
(fn
(l r)
(let
((lu32 (modulo (js-math-trunc (js-to-number l)) 4294967296))
(shift (modulo (js-math-trunc (js-to-number r)) 32)))
(floor (/ lu32 (js-math-pow 2 shift))))))
(define js-pow (fn (a b) (pow (js-to-number a) (js-to-number b))))
(define js-neg (fn (a) (- 0 (js-to-number a))))
@@ -1990,11 +2054,11 @@
(fn
(i)
(let
((idx (js-num-to-int i)))
((idx (js-num-to-int (js-to-number i))))
(if
(and (>= idx 0) (< idx (len s)))
(char-code (char-at s idx))
0))))
(and (>= idx 0) (< idx (unicode-len s)))
(unicode-char-code-at s idx)
(js-nan-value)))))
((= name "indexOf")
(fn
(&rest args)
@@ -2402,7 +2466,7 @@
(else js-undefined)))
((= (type-of obj) "string")
(cond
((= key "length") (len obj))
((= key "length") (unicode-len obj))
((= (type-of key) "number")
(if
(and (>= key 0) (< key (len obj)))
@@ -2709,6 +2773,52 @@
(dict-set! (get Number "prototype") "constructor" Number)
(dict-set!
Number
"__callable__"
(fn
(&rest args)
(let
((raw (if (= (len args) 0) 0 (js-to-number (nth args 0)))))
(let
((this-val (js-this)))
(if
(and
(dict? this-val)
(contains? (keys this-val) "__proto__")
(= (get this-val "__proto__") (get Number "prototype")))
(begin (dict-set! this-val "__js_number_value__" raw) this-val)
raw)))))
(dict-set!
(get Number "prototype")
"valueOf"
(fn
()
(let
((this-val (js-this)))
(if
(and
(dict? this-val)
(contains? (keys this-val) "__js_number_value__"))
(get this-val "__js_number_value__")
this-val))))
(dict-set!
(get Number "prototype")
"toString"
(fn
(&rest args)
(let
((this-raw (js-this)))
(let
((this-val (if (and (dict? this-raw) (contains? (keys this-raw) "__js_number_value__")) (get this-raw "__js_number_value__") this-raw)))
(let
((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)))))))
(define isFinite js-global-is-finite)
(define isNaN js-global-is-nan)
@@ -3167,6 +3277,17 @@
(dict-set! Array "name" "Array")
(dict-set!
(get Array "prototype")
"toString"
(fn
(&rest args)
(let
((this-val (js-this)))
(let
((items (cond ((list? this-val) this-val) ((and (dict? this-val) (contains? (keys this-val) "length")) (js-arraylike-to-list this-val)) (else (list)))))
(js-list-join items ",")))))
(define
js-string-from-char-code
(fn (&rest args) (js-string-from-char-code-loop args 0 "")))
@@ -3207,10 +3328,14 @@
(if
(>= i (len args))
acc
(js-string-from-char-code-loop
args
(+ i 1)
(str acc (js-code-to-char (js-num-to-int (nth args i))))))))
(let
((n (js-to-number (nth args i))))
(let
((code (if (js-global-is-nan n) 0 (modulo (js-math-trunc n) 65536))))
(js-string-from-char-code-loop
args
(+ i 1)
(str acc (char-from-code code))))))))
(define
js-string-proto-fn
@@ -3220,7 +3345,9 @@
(&rest args)
(let
((this-val (js-this)))
(js-invoke-method (js-to-string this-val) name args)))))
(let
((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))))})
@@ -3232,6 +3359,8 @@
(dict-set! String "fromCodePoint" js-string-from-code-point)
(dict-set! String "fromCharCode" js-string-from-char-code)
(dict-set!
String
"__callable__"
@@ -3242,7 +3371,10 @@
(let
((this-val (js-this)))
(if
(dict? this-val)
(and
(dict? this-val)
(contains? (keys this-val) "__proto__")
(= (get this-val "__proto__") (get String "prototype")))
(begin
(dict-set! this-val "__js_string_value__" raw)
(dict-set! this-val "length" (len raw))
@@ -3260,6 +3392,50 @@
(dict-set! Boolean "name" "Boolean")
(dict-set! Boolean "prototype" {:constructor Boolean})
(dict-set!
Boolean
"__callable__"
(fn
(&rest args)
(let
((val (if (> (len args) 0) (js-to-boolean (nth args 0)) false)))
(let
((this-val (js-this)))
(if
(dict? this-val)
(begin
(dict-set! this-val "__js_boolean_value__" val)
(dict-set! this-val "__proto__" (get Boolean "prototype"))
this-val)
(if val true false))))))
(dict-set!
(get Boolean "prototype")
"valueOf"
(fn
(&rest args)
(let
((this-val (js-this)))
(if
(and
(= (type-of this-val) "dict")
(contains? (keys this-val) "__js_boolean_value__"))
(get this-val "__js_boolean_value__")
this-val))))
(dict-set!
(get Boolean "prototype")
"toString"
(fn
(&rest args)
(let
((this-val (js-this)))
(let
((b (if (and (= (type-of this-val) "dict") (contains? (keys this-val) "__js_boolean_value__")) (get this-val "__js_boolean_value__") this-val)))
(if b "true" "false")))))
(define
parseInt
(fn