Skip to content

Allow Hash#[] to be aliased again - #6675

Closed
pzskc383 wants to merge 3 commits into
mruby:masterfrom
pzskc383:master
Closed

Allow Hash#[] to be aliased again#6675
pzskc383 wants to merge 3 commits into
mruby:masterfrom
pzskc383:master

Conversation

@pzskc383

Copy link
Copy Markdown

This prevents mruby-hashie from working properly on current mruby, it uses similar pattern here and it was working on 3.0.0, so i was able to trace it to commit 09336c5 using testcases.

Now, i'm not sure about performance impact of this. An alternative would be checking if class is still Hash and allow redefining only in subclasses, i.e. if (mrb_class(mrb, va) != mrb->hash_class) {

Thank you in advance.

found as a bug in mruby-hashie and bisected to commit 09336c5 which adds
OP_(GET|SET)IDX. OP_SETIDX seeps to default to dispatch as of now.
if it's aliased, goto default dispatch, otherwise use mrb_hash_set
@pzskc383
pzskc383 requested a review from matz as a code owner November 29, 2025 17:39
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @pzskc383, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a critical fix to mruby by re-enabling the aliasing of the Hash#[] method. This change resolves a compatibility issue with libraries like mruby-hashie that depend on this functionality. The implementation ensures that if Hash#[] is aliased or overridden, the virtual machine correctly dispatches to the Ruby-defined method, while maintaining optimized behavior for unaliased Hash instances.

Highlights

  • Re-enables Hash#[] aliasing: The pull request restores the ability to alias the Hash#[] method, which was previously prevented by a specific commit, allowing for more flexible hash manipulation.
  • Fixes mruby-hashie compatibility: This change directly addresses an issue that prevented the mruby-hashie library from functioning correctly on current mruby versions, ensuring broader compatibility.
  • Conditional method dispatch for Hash#[]: The virtual machine now includes a check to determine if Hash#[] has been aliased or overridden by a Ruby method. If so, it uses standard method dispatch; otherwise, it continues to use the optimized mrb_hash_get.
  • New test coverage: Comprehensive test cases have been added to verify that Hash#[] can be successfully aliased for both the Hash class and its subclasses without causing crashes, confirming the intended behavior.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@github-actions github-actions Bot added the core label Nov 29, 2025

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request restores the ability to alias Hash#[], which was broken by a previous optimization. The change in src/vm.c adds a check to see if Hash#[] has been redefined before taking the optimized path. This fixes compatibility with libraries like mruby-hashie and also allows monkey-patching Hash directly, as demonstrated by the new tests in test/t/hash.rb.

My main concern is with the implementation of the check in src/vm.c. While it correctly handles redefinitions in Ruby, it is not robust against redefinitions with another C function, which could lead to incorrect behavior. I've added a detailed comment about this. The added tests are good and cover both subclassing and direct monkey-patching scenarios.

Comment thread src/vm.c
@matz

matz commented Dec 10, 2025

Copy link
Copy Markdown
Member

My concern is the performance. If we call mrb_method_search() internally here, it would be slower than normal method calls, thus this optimization become useless (at least for Hash). You have to choose between performance and flexibility here.

@leviongit

Copy link
Copy Markdown
Contributor

wouldn't it be possible to use some of the left-over flag bits of the struct RClass object to store that a class' operators (e.g. +, -, *, /, [], []=) were overriden? or have we ran out of those bits?

@matz matz closed this in 35af869 Dec 25, 2025
matz added a commit that referenced this pull request Dec 25, 2025
Apply the same pattern as the Hash fix: the OP_GETIDX optimization
now only applies to instances of the Array class itself. Subclasses
fall back to method dispatch, allowing them to override the [] method.

Co-authored-by: Claude <noreply@anthropic.com>
matz added a commit that referenced this pull request Dec 25, 2025
Apply the same pattern as the Array/Hash fix: the OP_GETIDX optimization
now only applies to instances of the String class itself. Subclasses
fall back to method dispatch, allowing them to override the [] method.

Co-authored-by: Claude <noreply@anthropic.com>
matz added a commit that referenced this pull request Dec 25, 2025
Add inline optimizations for Array#[]= and Hash#[]= in OP_SETIDX,
matching the pattern established for OP_GETIDX:

- Array class: use mrb_ary_set() directly (integer index only)
- Hash class: use mrb_hash_set() directly
- Subclasses: fall back to method dispatch (can override []=)
- String: unchanged (complex 2-3 argument signature)

Co-authored-by: Claude <noreply@anthropic.com>
@matz

matz commented Dec 26, 2025

Copy link
Copy Markdown
Member

Thank you for the report and the PR.

I've implemented an alternative fix in commit e28372df57 that takes a different approach to balance performance and flexibility
.

The Fix

Instead of calling mrb_method_search() on every hash access (which would negate the optimization benefit), the fix checks if
the object is exactly the Hash class:

case MRB_TT_HASH:
  /* optimize only for Hash class; subclasses may override [] */
  if (mrb_obj_class(mrb, va) != mrb->hash_class) goto getidx_fallback;
  va = mrb_hash_get(mrb, va, vb);
  ...

Behavior

  • Hash subclasses: Full method dispatch - can override [] freely
  • Hash class itself: Uses optimized mrb_hash_get() directly

Why This Works for mruby-hashie

The mruby-hashie library uses a Mash subclass of Hash:

class Mash < Hash
  alias_method :regular_reader, :[]
  def [](key)
    # custom logic
    regular_reader(convert_key(key))
  end
end

Since Mash is a subclass, the fix ensures this pattern works correctly.

Trade-off

Overriding Hash#[] directly on the Hash class itself will not take effect:

class Hash
  def [](key); "custom"; end  # Won't be called for Hash instances
end

This is an acceptable trade-off for mruby because:

  1. Subclassing is the proper OOP pattern for customization
  2. Libraries like mruby-hashie already use subclasses
  3. The optimization remains effective for the common case (no per-call method search overhead)

This semantic is similar to having "final" method behavior on core classes - if you need custom [] behavior, create a subclas
s.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants