The current implementation of the visitor pattern in Prism uses
a single method (`visit_child_nodes`) to handle all node types. This can lead to performance issues since the `node` argument will end up being polymorphic, and will prevent effective use of inline caches, which in CRuby are monomorphic.
This commit generates an inlined version of the previous code for each node type, thus making the calls inside visitor methods monomorphic. This should improve performance, especially in cases where the visitor is called frequently.
https://github.com/ruby/prism/commit/60d324a701
Instead of requiring the consumer to provide a list of all events which
they wish to handle, we can give them to option of dynamically detecting
them, by scanning the listener's public methods.
This approach is similar to that used by Minitest (scanning for `test_`
methods) and Rails generators (running all public methods in the order
they are defined).
While this is slower than specifying a hard coded list, the penalty is
only during registration. There is no change the the behaviour of
dispatching the events.
https://github.com/ruby/prism/commit/781ebed743
This can get triggered even if the list of statements only contains
a single statement. This is necessary to properly support compiling
```ruby
defined? (;a)
defined? (a;)
```
as "expression". Previously these were parsed as statements lists
with single statements in them.
https://github.com/ruby/prism/commit/b63b5d67a9
[Bug #21161]
The `tolower` function provided by the libc is locale dependent
and can behave in ways you wouldn't expect for some value
of `LC_CTYPE`.
https://github.com/ruby/prism/commit/e3488256b4
Co-Authored-By: Nobuyoshi Nakada <nobu@ruby-lang.org>
According to the calloc(3) man page, when nmemb or size is 0, `calloc()` can either return NULL or a unique pointer that can be passed to `free()`.
While gcc and clang typically return a unique pointer, mruby's `mrb_calloc()` returns NULL in this case.
Since `pm_constant_pool_init()` is commonly called with capacity=0 during normal operation of Prism, explicitly handle this case by setting `list->ids` to NULL when capacity is 0.
This approach is portable across different calloc implementations and avoids potential issues with mruby's allocation behavior.
This maintains compatibility with `free()` and `realloc()`, as passing NULL pointers to these functions is explicitly allowed by their specifications.
https://github.com/ruby/prism/commit/1c32252df7
Expressions joined with `&&` are better asserted separately, so that
it is clear from the failure message which expression is false.
Also, `unsigned long` may not be enough for `ptrdiff_t`, e.g., Windows.
Saying that `ptrdiff_t` can be larger than `SIZE_MAX` means that
`PTRDIFF_MAX` is larger than `SIZE_MAX` and `ptrdiff_t` is sufficient
to represent `SIZE_MAX`, otherwise if `PTRDIFF_MAX` is smaller than
`SIZE_MAX`, `diff` will always be smaller than `SIZE_MAX` as well.
`diff` could be equal to `SIZE_MAX` only if `PTRDIFF_MAX` is equal to
`SIZE_MAX` and these assertions would pass, but I don't think there is
any platform where that is the case.
https://github.com/ruby/prism/commit/1fc6dfcada
When parent scopes around an eval are forwarding parameters (like
*, **, &, or ...) we need to know that information when we are in
the parser. As such, we need to support passing that information
into the scopes option. In order to do this, unfortunately we need
a bunch of changes.
The scopes option was previously an array of array of strings.
These corresponded to the names of the locals in the parent scopes.
We still support this, but now additionally support passing in a
Prism::Scope instance at each index in the array. This Prism::Scope
class holds both the names of the locals as well as an array of
forwarding parameter names (symbols corresponding to the forwarding
parameters). There is convenience function on the Prism module that
creates a Prism::Scope object using Prism.scope.
In JavaScript, we now additionally support an object much the same
as the Ruby side. In Java, we now have a ParsingOptions.Scope class
that holds that information. In the dump APIs, these objects in all
3 languages will add an additional byte for the forwarding flags in
the middle of the scopes serialization.
All of this is in service of properly parsing the following code:
```ruby
def foo(*) = eval("bar(*)")
```
https://github.com/ruby/prism/commit/21abb6b7c4