Skip to content

Commit d93c435

Browse files
anmonteirodnolen
authored andcommitted
CLJS-2034: Sequence and Eduction produce infinite loop in transducer that appends to the reduction
The implementation of transducers in ClojureScript tracked an old counterpart in the Clojure codebase. This patch addresses the change introduced in the following commit to Clojure, which replaced `LazyTransformer` with `TransformerIterator`, effectively making the transducer behavior akin to the one in Clojure. clojure/clojure@c47e1bb
1 parent 1d8e6fb commit d93c435

File tree

2 files changed

+134
-153
lines changed

2 files changed

+134
-153
lines changed

src/main/cljs/cljs/core.cljs

Lines changed: 127 additions & 152 deletions
Original file line numberDiff line numberDiff line change
@@ -3409,7 +3409,7 @@ reduces them without incurring seq initialization"
34093409
(-lastIndexOf coll x (count coll)))
34103410
(lastIndexOf [coll x start]
34113411
(-lastIndexOf coll x start))
3412-
3412+
34133413
IWithMeta
34143414
(-with-meta [coll m]
34153415
(ChunkedCons. chunk more m __hash))
@@ -3868,41 +3868,53 @@ reduces them without incurring seq initialization"
38683868
(seqable? coll) (seq-iter coll)
38693869
:else (throw (js/Error. (str "Cannot create iterator from " coll)))))
38703870

3871-
(declare LazyTransformer)
3871+
(deftype Many [vals]
3872+
Object
3873+
(add [this o]
3874+
(.push vals o)
3875+
this)
3876+
(remove [this]
3877+
(.shift vals))
3878+
(isEmpty [this]
3879+
(zero? (.-length vals)))
3880+
(toString [this]
3881+
(str "Many: " vals)))
38723882

3873-
(defn lazy-transformer [stepper]
3874-
(LazyTransformer. stepper nil nil nil))
3883+
(def ^:private NONE #js {})
38753884

3876-
(deftype Stepper [xform iter]
3885+
(deftype Single [^:mutable val]
38773886
Object
3878-
(step [this lt]
3879-
(loop []
3880-
(if (and (not (nil? (.-stepper lt)))
3881-
(.hasNext iter))
3882-
(if (reduced? (xform lt (.next iter)))
3883-
(when-not (nil? (.-rest lt))
3884-
(set! (.. lt -rest -stepper) nil))
3885-
(recur))))
3886-
(when-not (nil? (.-stepper lt))
3887-
(xform lt))))
3888-
3889-
(defn stepper [xform iter]
3890-
(letfn [(stepfn
3891-
([result]
3892-
(let [lt (if (reduced? result)
3893-
@result
3894-
result)]
3895-
(set! (.-stepper lt) nil)
3896-
result))
3897-
([result input]
3898-
(let [lt result]
3899-
(set! (.-first lt) input)
3900-
(set! (.-rest lt) (lazy-transformer (.-stepper lt)))
3901-
(set! (.-stepper lt) nil)
3902-
(.-rest lt))))]
3903-
(Stepper. (xform stepfn) iter)))
3904-
3905-
(deftype MultiStepper [xform iters nexts]
3887+
(add [this o]
3888+
(if (identical? val NONE)
3889+
(do
3890+
(set! val o)
3891+
this)
3892+
(Many. #js [val o])))
3893+
(remove [this]
3894+
(if (identical? val NONE)
3895+
(throw (js/Error. (str "Removing object from empty buffer")))
3896+
(let [ret val]
3897+
(set! val NONE)
3898+
ret)))
3899+
(isEmpty [this]
3900+
(identical? val NONE))
3901+
(toString [this]
3902+
(str "Single: " val)))
3903+
3904+
(deftype Empty []
3905+
Object
3906+
(add [this o]
3907+
(Single. o))
3908+
(remove [this]
3909+
(throw (js/Error. (str "Removing object from empty buffer"))))
3910+
(isEmpty [this]
3911+
true)
3912+
(toString [this]
3913+
"Empty"))
3914+
3915+
(def ^:private EMPTY (Empty.))
3916+
3917+
(deftype MultiIterator [iters]
39063918
Object
39073919
(hasNext [_]
39083920
(loop [iters (seq iters)]
@@ -3913,128 +3925,83 @@ reduces them without incurring seq initialization"
39133925
(recur (next iters))))
39143926
true)))
39153927
(next [_]
3916-
(dotimes [i (alength iters)]
3917-
(aset nexts i (.next (aget iters i))))
3918-
(prim-seq nexts 0))
3919-
(step [this lt]
3920-
(loop []
3921-
(if (and (not (nil? (.-stepper lt)))
3922-
(.hasNext this))
3923-
(if (reduced? (apply xform (cons lt (.next this))))
3924-
(when-not (nil? (.-rest lt))
3925-
(set! (.. lt -rest -stepper) nil))
3926-
(recur))))
3927-
(when-not (nil? (.-stepper lt))
3928-
(xform lt))))
3929-
3930-
(defn multi-stepper
3931-
([xform iters]
3932-
(multi-stepper xform iters
3933-
(make-array (alength iters))))
3934-
([xform iters nexts]
3935-
(letfn [(stepfn
3936-
([result]
3937-
(let [lt (if (reduced? result)
3938-
@result
3939-
result)]
3940-
(set! (.-stepper lt) nil)
3941-
lt))
3942-
([result input]
3943-
(let [lt result]
3944-
(set! (.-first lt) input)
3945-
(set! (.-rest lt) (lazy-transformer (.-stepper lt)))
3946-
(set! (.-stepper lt) nil)
3947-
(.-rest lt))))]
3948-
(MultiStepper. (xform stepfn) iters nexts))))
3949-
3950-
(deftype LazyTransformer [^:mutable stepper ^:mutable first ^:mutable rest meta]
3928+
(let [nexts (array)]
3929+
(dotimes [i (alength iters)]
3930+
(aset nexts i (.next (aget iters i))))
3931+
(prim-seq nexts 0))))
3932+
3933+
(defn- chunkIteratorSeq [iter]
3934+
(lazy-seq
3935+
(when ^boolean (.hasNext iter)
3936+
(let [arr (array)]
3937+
(loop [n 0]
3938+
(if (and (.hasNext iter) (< n 32))
3939+
(do
3940+
(aset arr n (.next iter))
3941+
(recur (inc n)))
3942+
(chunk-cons (array-chunk arr 0 n) (chunkIteratorSeq iter))))))))
3943+
3944+
(deftype TransformerIterator [^:mutable buffer ^:mutable _next ^:mutable completed ^:mutable xf sourceIter multi]
39513945
Object
3952-
(indexOf [coll x]
3953-
(-indexOf coll x 0))
3954-
(indexOf [coll x start]
3955-
(-indexOf coll x start))
3956-
(lastIndexOf [coll x]
3957-
(-lastIndexOf coll x (count coll)))
3958-
(lastIndexOf [coll x start]
3959-
(-lastIndexOf coll x start))
3960-
3961-
IWithMeta
3962-
(-with-meta [this new-meta]
3963-
(LazyTransformer. stepper first rest new-meta))
3964-
3965-
IMeta
3966-
(-meta [this] meta)
3967-
3968-
ICollection
3969-
(-conj [this o]
3970-
(cons o (-seq this)))
3971-
3972-
IEmptyableCollection
3973-
(-empty [this]
3974-
())
3975-
3976-
ISequential
3977-
IEquiv
3978-
(-equiv [this other]
3979-
(let [s (-seq this)]
3980-
(if-not (nil? s)
3981-
(equiv-sequential this other)
3982-
(and (sequential? other)
3983-
(nil? (seq other))))))
3984-
3985-
IHash
3986-
(-hash [this]
3987-
(hash-ordered-coll this))
3988-
3989-
ISeqable
3990-
(-seq [this]
3991-
(when-not (nil? stepper)
3992-
(.step stepper this))
3993-
(if (nil? rest)
3994-
nil
3995-
this))
3996-
3997-
ISeq
3998-
(-first [this]
3999-
(when-not (nil? stepper)
4000-
(-seq this))
4001-
(if (nil? rest)
4002-
nil
4003-
first))
4004-
4005-
(-rest [this]
4006-
(when-not (nil? stepper)
4007-
(-seq this))
4008-
(if (nil? rest)
4009-
()
4010-
rest))
4011-
4012-
INext
4013-
(-next [this]
4014-
(when-not (nil? stepper)
4015-
(-seq this))
4016-
(if (nil? rest)
4017-
nil
4018-
(-seq rest)))
4019-
4020-
IPending
4021-
(-realized? [_]
4022-
(nil? stepper)))
4023-
4024-
(es6-iterable LazyTransformer)
4025-
4026-
(set! (.-create LazyTransformer)
3946+
(step [this]
3947+
(if-not (identical? _next NONE)
3948+
true
3949+
(loop []
3950+
(if (identical? _next NONE)
3951+
(if ^boolean (.isEmpty buffer)
3952+
(if ^boolean completed
3953+
false
3954+
(if ^boolean (.hasNext sourceIter)
3955+
(let [iter (if ^boolean multi
3956+
(apply xf (cons nil (.next sourceIter)))
3957+
(xf nil (.next sourceIter)))]
3958+
(when (reduced? iter)
3959+
(xf nil)
3960+
(set! completed true))
3961+
(recur))
3962+
(do
3963+
(xf nil)
3964+
(set! completed true)
3965+
(recur))))
3966+
(do
3967+
(set! _next (.remove buffer))
3968+
(recur)))
3969+
true))))
3970+
(hasNext [this]
3971+
(.step this))
3972+
(next [this]
3973+
(if ^boolean (.hasNext this)
3974+
(let [ret _next]
3975+
(set! _next NONE)
3976+
ret)
3977+
(throw (js/Error. "No such element"))))
3978+
(remove [_]
3979+
(js/Error. "Unsupported operation")))
3980+
3981+
(es6-iterable TransformerIterator)
3982+
3983+
(defn transformer-iterator
3984+
[xform sourceIter multi]
3985+
(let [iterator (TransformerIterator. EMPTY NONE false nil sourceIter multi)]
3986+
(set! (.-xf iterator)
3987+
(xform (fn
3988+
([] nil)
3989+
([acc] acc)
3990+
([acc o]
3991+
(set! (.-buffer iterator) (.add (.-buffer iterator) o))
3992+
acc))))
3993+
iterator))
3994+
3995+
(set! (.-create TransformerIterator)
40273996
(fn [xform coll]
4028-
(LazyTransformer. (stepper xform (iter coll)) nil nil nil)))
3997+
(transformer-iterator xform (iter coll) false)))
40293998

4030-
(set! (.-createMulti LazyTransformer)
3999+
(set! (.-createMulti TransformerIterator)
40314000
(fn [xform colls]
40324001
(let [iters (array)]
40334002
(doseq [coll colls]
40344003
(.push iters (iter coll)))
4035-
(LazyTransformer.
4036-
(multi-stepper xform iters (make-array (alength iters)))
4037-
nil nil nil))))
4004+
(transformer-iterator xform (MultiIterator. iters) true))))
40384005

40394006
(defn sequence
40404007
"Coerces coll to a (possibly empty) sequence, if it is not already
@@ -4050,9 +4017,13 @@ reduces them without incurring seq initialization"
40504017
coll
40514018
(or (seq coll) ())))
40524019
([xform coll]
4053-
(.create LazyTransformer xform coll))
4020+
(or (chunkIteratorSeq
4021+
(.create TransformerIterator xform coll))
4022+
()))
40544023
([xform coll & colls]
4055-
(.createMulti LazyTransformer xform (to-array (cons coll colls)))))
4024+
(or (chunkIteratorSeq
4025+
(.createMulti TransformerIterator xform (to-array (cons coll colls))))
4026+
())))
40564027

40574028
(defn ^boolean every?
40584029
"Returns true if (pred x) is logical true for every x in coll, else
@@ -4257,7 +4228,7 @@ reduces them without incurring seq initialization"
42574228
(-equiv this other))
42584229

42594230
IAtom
4260-
4231+
42614232
IEquiv
42624233
(-equiv [o other] (identical? o other))
42634234

@@ -9645,7 +9616,7 @@ reduces them without incurring seq initialization"
96459616
LazySeq
96469617
(-pr-writer [coll writer opts] (pr-sequential-writer writer pr-writer "(" " " ")" opts coll))
96479618

9648-
LazyTransformer
9619+
TransformerIterator
96499620
(-pr-writer [coll writer opts] (pr-sequential-writer writer pr-writer "(" " " ")" opts coll))
96509621

96519622
IndexedSeq
@@ -9955,6 +9926,10 @@ reduces them without incurring seq initialization"
99559926

99569927
ISequential
99579928

9929+
IIterable
9930+
(-iterator [coll]
9931+
(.create TransformerIterator xform coll))
9932+
99589933
ISeqable
99599934
(-seq [_] (seq (sequence xform coll)))
99609935

src/test/cljs/cljs/core_test.cljs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,13 @@
348348
(is (= (sequence xf [0 0] [1 2]) [1 2])))
349349
(is (= (-> (sequence (map inc) [1 2 3])
350350
(with-meta {:a 1})
351-
meta) {:a 1}))))
351+
meta) {:a 1}))
352+
(let [xf (fn [rf]
353+
(fn
354+
([] (rf))
355+
([result] (rf result :foo))
356+
([result input] (rf result input))))]
357+
(is (= (sequence xf [1 2 3]) [1 2 3 :foo])))))
352358

353359
(deftest test-obj-equiv
354360
(testing "Object equiv method"

0 commit comments

Comments
 (0)