Skip to content

Commit 9c33aba

Browse files
jgarzikclaude
andcommitted
Implement range repr, fix cascade crash in test_constructors
The \"GC crash\" during heavy __getitem__ iteration was actually caused by range objects having no tp_repr/tp_str. str(range(1000)) returned SmallInt 0 instead of a string, which then crashed when BINARY_SLICE tried to slice it (NULL ob_type dereference). Fix: implement range_obj_repr that produces \"range(start, stop)\" or \"range(start, stop, step)\" format strings. Set both tp_repr and tp_str on range_obj_type. This unblocks test_constructors and ~10 other tests that were cascade-failing from this crash. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 5292a85 commit 9c33aba

1 file changed

Lines changed: 140 additions & 2 deletions

File tree

src/pyo/iter.asm

Lines changed: 140 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,144 @@ DEF_FUNC range_obj_reversed
479479
ret
480480
END_FUNC range_obj_reversed
481481

482+
;; ============================================================================
483+
;; range_obj_repr(PyRangeObject *self) -> PyStrObject*
484+
;; Returns "range(start, stop)" or "range(start, stop, step)" if step != 1
485+
;; ============================================================================
486+
extern str_from_cstr_heap
487+
extern obj_repr
488+
ROR_BUF equ 8
489+
ROR_POS equ 16
490+
ROR_FRAME equ 16
491+
DEF_FUNC range_obj_repr, ROR_FRAME
492+
push rbx
493+
push r12
494+
push r13
495+
push r14
496+
497+
mov rbx, rdi ; self (range object)
498+
mov r12, [rbx + PyRangeObject.start]
499+
mov r13, [rbx + PyRangeObject.stop]
500+
mov r14, [rbx + PyRangeObject.step]
501+
502+
; Allocate buffer (128 bytes is plenty for "range(i64, i64, i64)")
503+
mov edi, 128
504+
call ap_malloc
505+
mov [rbp - ROR_BUF], rax
506+
mov qword [rbp - ROR_POS], 0
507+
508+
; Write "range("
509+
mov rdi, rax
510+
mov byte [rdi], 'r'
511+
mov byte [rdi+1], 'a'
512+
mov byte [rdi+2], 'n'
513+
mov byte [rdi+3], 'g'
514+
mov byte [rdi+4], 'e'
515+
mov byte [rdi+5], '('
516+
mov qword [rbp - ROR_POS], 6
517+
518+
; Format start
519+
mov rdi, r12 ; start value (i64)
520+
call .ror_format_i64
521+
522+
; Write ", "
523+
mov rdi, [rbp - ROR_BUF]
524+
mov rcx, [rbp - ROR_POS]
525+
mov byte [rdi + rcx], ','
526+
mov byte [rdi + rcx + 1], ' '
527+
add qword [rbp - ROR_POS], 2
528+
529+
; Format stop
530+
mov rdi, r13 ; stop value (i64)
531+
call .ror_format_i64
532+
533+
; If step != 1, add ", step"
534+
cmp r14, 1
535+
je .ror_close
536+
mov rdi, [rbp - ROR_BUF]
537+
mov rcx, [rbp - ROR_POS]
538+
mov byte [rdi + rcx], ','
539+
mov byte [rdi + rcx + 1], ' '
540+
add qword [rbp - ROR_POS], 2
541+
mov rdi, r14 ; step value (i64)
542+
call .ror_format_i64
543+
544+
.ror_close:
545+
; Write ")"
546+
mov rdi, [rbp - ROR_BUF]
547+
mov rcx, [rbp - ROR_POS]
548+
mov byte [rdi + rcx], ')'
549+
mov byte [rdi + rcx + 1], 0 ; NUL terminate
550+
inc qword [rbp - ROR_POS]
551+
552+
; Create string from buffer
553+
mov rdi, [rbp - ROR_BUF]
554+
call str_from_cstr_heap
555+
push rax ; save string
556+
557+
; Free buffer
558+
mov rdi, [rbp - ROR_BUF]
559+
call ap_free
560+
561+
pop rax ; return string
562+
pop r14
563+
pop r13
564+
pop r12
565+
pop rbx
566+
leave
567+
ret
568+
569+
; Helper: format i64 in rdi to buffer, appending at current position
570+
.ror_format_i64:
571+
push rbx
572+
push r12
573+
mov rbx, rdi ; value
574+
mov r12, [rbp - ROR_BUF]
575+
mov rcx, [rbp - ROR_POS]
576+
577+
; Handle negative
578+
test rbx, rbx
579+
jns .ror_fi_pos
580+
mov byte [r12 + rcx], '-'
581+
inc rcx
582+
mov [rbp - ROR_POS], rcx
583+
neg rbx
584+
585+
.ror_fi_pos:
586+
; Convert to decimal digits (reversed)
587+
sub rsp, 24 ; temp digit buffer on stack
588+
mov rdi, rsp
589+
xor r8d, r8d ; digit count
590+
mov rax, rbx
591+
.ror_fi_loop:
592+
xor edx, edx
593+
mov rcx, 10
594+
div rcx ; rax = quotient, rdx = remainder
595+
add dl, '0'
596+
mov [rdi + r8], dl
597+
inc r8
598+
test rax, rax
599+
jnz .ror_fi_loop
600+
601+
; Copy reversed digits to buffer
602+
mov rcx, [rbp - ROR_POS]
603+
mov rdi, [rbp - ROR_BUF]
604+
.ror_fi_copy:
605+
dec r8
606+
movzx eax, byte [rsp + r8]
607+
mov [rdi + rcx], al
608+
inc rcx
609+
test r8, r8
610+
jnz .ror_fi_copy
611+
612+
add rsp, 24
613+
mov [rbp - ROR_POS], rcx
614+
pop r12
615+
pop rbx
616+
ret
617+
618+
END_FUNC range_obj_repr
619+
482620
;; ============================================================================
483621
;; init_iter_types
484622
;; Patches list_type.tp_iter and tuple_type.tp_iter at startup
@@ -610,8 +748,8 @@ range_obj_type:
610748
dq range_obj_name ; tp_name
611749
dq PyRangeObject_size ; tp_basicsize
612750
dq range_obj_dealloc ; tp_dealloc
613-
dq 0 ; tp_repr
614-
dq 0 ; tp_str
751+
dq range_obj_repr ; tp_repr
752+
dq range_obj_repr ; tp_str
615753
dq 0 ; tp_hash
616754
dq 0 ; tp_call
617755
dq 0 ; tp_getattr

0 commit comments

Comments
 (0)