js-on-sx: array-like receivers for Array.prototype.* methods

Array.prototype.slice.call({length:3, 0:41, 1:42, 2:43}) used to crash with
'Not callable: {dict}' because js-array-proto-fn passed the dict straight
into js-invoke-method, which then tried (append! dict x) etc.

Now js-array-proto-fn converts dict-with-length receivers to a list via
js-arraylike-to-list before dispatch. Mutation methods (push/pop/shift/
reverse/sort/fill) still require a real list — array-likes only work for
read-only methods.

Targets the 455x 'Not callable: {:length N :0 v1 :1 v2 ...}' scoreboard item.

6 new unit tests, 459/461 total.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-24 06:40:46 +00:00
parent 860549c1db
commit 5f3a8e43c0
2 changed files with 58 additions and 1 deletions

View File

@@ -1799,6 +1799,37 @@
result))
(else (list)))))
(define
js-arraylike-to-list
(fn
(v)
(cond
((list? v) v)
((= (type-of v) "string") (js-string-to-list v 0 (list)))
((dict? v)
(let
((n-val (get v "length")))
(if
(or (= n-val nil) (js-undefined? n-val))
(list)
(let
((n (js-to-number n-val)))
(js-arraylike-to-list-loop v 0 n (list))))))
(else (list)))))
(define
js-arraylike-to-list-loop
(fn
(v i n acc)
(if
(>= i n)
acc
(let
((val (get v (str i))))
(do
(append! acc (if (= val nil) :js-undefined val))
(js-arraylike-to-list-loop v (+ i 1) n acc))))))
(define
js-string-to-list
(fn
@@ -1933,7 +1964,11 @@
(name)
(fn
(&rest args)
(let ((this-val (js-this))) (js-invoke-method this-val name args)))))
(let
((this-val (js-this)))
(let
((recv (cond ((list? this-val) this-val) ((and (dict? this-val) (contains? (keys this-val) "length")) (js-arraylike-to-list this-val)) (else this-val))))
(js-invoke-method recv name args))))))
(define
js-array-from