Skip to content

Conversation

@fglock
Copy link
Owner

@fglock fglock commented Oct 30, 2025

Summary

This PR completes the fix for t/op/filehandle.t by properly implementing typeglob CODE slot access.

Root Cause

The previous PR #66 only added the missing DESTROY method, but the real issue was that *{name}{CODE} wasn't working properly. When FileHandle.pm checked for method existence using *{"IO::Handle::method"}{CODE}, it was calling RuntimeScalar.hashDerefGetNonStrict() instead of accessing the typeglob's CODE slot.

Changes

  1. Override hashDerefGetNonStrict() in RuntimeGlob to properly handle typeglob slot access (CODE, SCALAR, ARRAY, HASH, IO, FORMAT)
  2. Check RuntimeCode.defined() before returning CODE slot to match Perl behavior where undefined subs return undef
  3. Add missing IO module methods:
    • IO::Handle: gets(), _open_mode_string()
    • IO::Seekable: getpos(), setpos()
    • IO::File: new_tmpfile() (stub for now)
  4. Fix vivify() crash in NonStrict methods for read-only scalars (from PR Fix tr.t and multideref.t: Unicode support + strict-refs #65)
  5. Fix hash/array dereference in scalar context to prevent VerifyError

Test Results

t/op/filehandle.t: 4/4 tests pass (was 0/4 before PR #66)

Bonus Improvements

This fix also improved other tests:

  • t/op/undef.t: +23 tests (15 → 38 tests passing, previously crashed)
  • t/uni/select.t: +3 tests (0 → 3/5 tests passing)

Technical Details

Used --disassemble to confirm that *{name}{CODE} compiles to hashDerefGetNonStrict, which was missing the override in RuntimeGlob.

The real issue was that *{name}{CODE} wasn't calling RuntimeGlob's
hashDerefGet() method - it was using the parent RuntimeScalar's version
which treats the glob as a hash dereference.

Fixes:
1. Override hashDerefGetNonStrict() in RuntimeGlob to properly handle
   typeglob slot access (CODE, SCALAR, ARRAY, HASH, IO, FORMAT)
2. Check RuntimeCode.defined() before returning CODE slot to match
   standard Perl behavior where undefined subs don't appear in CODE slot
3. Add missing IO::Handle methods: gets(), _open_mode_string()
4. Add missing IO::Seekable methods: getpos(), setpos()
5. Add missing IO::File method: new_tmpfile() (stub for now)
6. Keep DESTROY method in IO::Handle for compatibility

Test Results: t/op/filehandle.t now passes 4/4 tests
- Add RuntimeIO.selectedGlob to track the selected filehandle glob
- Update IOOperator.select() to return the glob name instead of GLOB(0xHASHCODE)
- Fix RuntimeGlob.toString() to return the glob name without '*' prefix
- Fix RuntimeScalar.undefine() to handle CODE type properly without crashing

This fixes t/uni/select.t (3/5 tests pass, 2 TODO) and t/op/undef.t.
Add parseUndef() method that sets parsingTakeReference flag, similar to
parseDefined(). This prevents &foo from being auto-called when used with
undef, allowing undef &foo to properly undefine the subroutine instead of
trying to undefine the return value (which may be read-only).

This is the proper fix for the 'Modification of a read-only value attempted'
error when using undef &sub.
@fglock fglock merged commit fdb9a4c into master Oct 30, 2025
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants