diff --git a/CHANGELOG.md b/CHANGELOG.md index bf7d53559f71..103fcdb83821 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,7 +37,7 @@ Bug fixes: * Fix `Dir.glob` returning blank string entry with leading `**/` in glob and `base:` argument (@rwstauner). * Fix class lookup after an object's class has been replaced by `IO#reopen` (@itarato, @nirvdrum, @eregon). * Fix `Marshal.load` and raise `ArgumentError` when dump is broken and is too short (#3108, @andrykonchin). -* Fix `super` method lookup for unbounded attached methods (#3131, @itarato). +* Fix `super` method lookup for unbounded attached methods (#3131, @itarato). * Fix `Module#define_method(name, Method)` to respect `module_function` visibility (#3181, @andrykonchin). * Fix stack overflow with `Kernel.require` and `zeitwerk` (#3224, @eregon). * Reimplement `IO.select` with `poll(2)` to support file descriptors >= 1024 (#3201, @eregon). @@ -47,7 +47,7 @@ Compatibility: * Fix `Hash#shift` when Hash is empty but has initial default value or initial default proc (#3039, @itarato). * Make `Array#shuffle` produce the same results as CRuby (@rwstauner). * Add `Process.argv0` method (@andrykonchin). -* Add support for array pattern matching. This is opt-in via `--pattern-matching` since pattern matching is not fully supported yet. (#2683, @razetime). +* Add support for array pattern matching. This is opt-in via `--pattern-matching` since pattern matching is not fully supported yet (#2683, @razetime). * Fix `Array#[]` with `ArithmeticSequence` argument when step is negative (#3039, @itarato). * Fix `Range#size` and return `nil` for beginningless Range when end isn't Numeric (#3039, @rwstauner). * Alias `String#-@` to `String#dedup` (#3039, @itarato). @@ -67,7 +67,7 @@ Compatibility: * Add `timeout` argument to `Thread::SizedQueue#push` (#3039, @itarato). * Add `rb_syserr_new` function (@rwstauner). * Add `Enumerator#product` (#3039, @itarato). -* Add `Module#const_added` (#3039, @itarato). +* Add `Module#const_added` (#3039, @itarato). * Show the pointer size information (if available) in `FFI::Pointer#inspect` (@nirvdrum). * Implement performance warnings (`Warning[:performance]`) like in CRuby 3.3 (@eregon). * The output of `Marshal.dump` is now compatible with CRuby for `Rational` and `Complex` instances (#3228, @eregon). @@ -116,7 +116,7 @@ Bug fixes: * Fix constants lookup when `BasicObject#instance_eval` method is called with a String (#2810, @andrykonchin). * Don't trigger the `method_added` event when changing a method's visibility or calling `module_function` (@paracycle, @nirvdrum). * Fix `rb_time_timespec_new` function to not call `Time.at` method directly (@andrykonchin). -* Fix `StringIO#write` to transcode strings with encodings that don't match the `StringIO`'s `external_encoding`. (#2839, @flavorjones) +* Fix `StringIO#write` to transcode strings with encodings that don't match the `StringIO`'s `external_encoding` (#2839, @flavorjones). * Fix processing of proc rest arguments located at the beginning if there are no actual arguments (#2921, @andrykonchin). * Fix `Monitor#exit` to raise `ThreadError` when monitor not owned by the current thread (#2922, @andrykonchin). * Fix `MatchData#[]` to support negative `length` argument (#2929, @andrykonchin). @@ -149,7 +149,7 @@ Compatibility: * Support `offset` keyword argument for `String#unpack` and `String#unpack1` (@andrykonchin). * Fix `Process.detach` and cast `pid` argument to `Integer` (#2782, @andrykonchin). * `rb_to_id()` should create a static `ID`, used by RMagick (@eregon). -* Resolve the current user home even when `$HOME` is not set (#2784, @eregon) +* Resolve the current user home even when `$HOME` is not set (#2784, @eregon). * Fix `IO#lineno=` and convert argument to `Integer` more strictly (#2786, @andrykonchin). * Fix argument implicit convertion in `IO#pos=` and `IO#seek` methods (#2787, @andrykonchin). * Warn about unknown directive passed to `Array#pack` in verbose mode (#2791, @andrykonchin). @@ -176,8 +176,8 @@ Compatibility: * Raise `EncodingError` at parse time when Hash literal contains a Symbol key with invalid encoding (#2848, @andrykonchin). * Fix `Array` methods `reject`, `reject!`, `inject`, `map`, `select`, `each_index` and handle a case when array is modified by a passed block like CRuby does (#2822, @andrykonchin, @eregon). * Fix `Array` methods `select!` and `keep_if` and handle a case when exception is raised in a passed block properly (@andrykonchin). -* Fix `Enumerable` methods `each_cons` and `each_slice` to return receiver (#2733, @horakivo) -* `Module` methods `#private`, `#public`, `#protected`, `#module_function` now returns their arguments like in CRuby 3.1 (#2733, @horakivo) +* Fix `Enumerable` methods `each_cons` and `each_slice` to return receiver (#2733, @horakivo). +* `Module` methods `#private`, `#public`, `#protected`, `#module_function` now returns their arguments like in CRuby 3.1 (#2733, @horakivo). * `Kernel#exit!`, killing Fibers and internal errors do not run code in `ensure` clauses anymore, the same as CRuby (@eregon). * Implement `UnboundMethod#original_name` (@paracycle, @nirvdrum). * Implement `Thread#native_thread_id` method (#2733, @horakivo). @@ -207,7 +207,7 @@ Performance: * Marking of native structures wrapped in objects is now done on C call exit to reduce memory overhead (@aardvark179). * Splitting (copying) of call targets has been optimized by implementing `cloneUninitialized()` (@andrykonchin, @eregon). -* `Process.pid` is now cached per process like `$$` (#2882, @horakivo) +* `Process.pid` is now cached per process like `$$` (#2882, @horakivo). * Use the system `libyaml` for `psych` to improve warmup when parsing YAML (#2089, @eregon). * Fixed repeated deoptimizations for methods building an `Array` which is growing over multiple calls at a given call site (@eregon). @@ -306,7 +306,7 @@ Bug fixes: * Fix issue with feature loading not detecting a previously loaded feature (#2677, @bjfish). * Fix `/#{...}/o` to evaluate only once per context when splitting happens (@eregon). * Fix `Kernel#sprintf` formatting of floats to be like CRuby (@aardvark179). -* Fix `Process.egid=` to accept `String`s (#2615, @ngtban) +* Fix `Process.egid=` to accept `String`s (#2615, @ngtban). * Fix optional assignment to only evaluate index arguments once (#2658, @aardvark179). Compatibility: @@ -376,21 +376,21 @@ Compatibility: * Implement full Ruby 3 keyword arguments semantics (#2453, @eregon, @chrisseaton). * Implement `ruby_native_thread_p` for compatibility (#2556, @aardvark179). -* Add `rb_argv0` for the `tk` gem. (#2556, @aardvark179). +* Add `rb_argv0` for the `tk` gem (#2556, @aardvark179). * Implement more correct conversion of array elements by `Array#pack`(#2503, #2504, @aardvark179). -* Implement `Pathname#{empty?, glob}` (#2559, @bjfish) +* Implement `Pathname#{empty?, glob}` (#2559, @bjfish). * Fixed `Rational('')` to raise error like MRI (#2566, @aardvark179). * Freeze instances of `Range` but not subclasses, like CRuby (#2570, @MattAlp). -* When writing to STDOUT redirected to a closed pipe, no broken pipe error message will be shown now. (#2532, @gogainda). +* When writing to STDOUT redirected to a closed pipe, no broken pipe error message will be shown now (#2532, @gogainda). * Use `#to_a` for converting `list` in `rescue *list` (#2572, @eregon). * Implement 'rb_str_buf_append' (@bjfish). * Add patch for `digest` so that TruffleRuby implementation is not overridden (@bjfish). * Handle encoding conversion errors when reading directory entries (@aardvark179). -* Follow symlinks when processing `*/` directory glob patterns. (#2589, @aardvark179). -* Set `@gem_prelude_index` variable on the default load paths (#2586 , @bjfish) +* Follow symlinks when processing `*/` directory glob patterns (#2589, @aardvark179). +* Set `@gem_prelude_index` variable on the default load paths (#2586 , @bjfish). * Do not call `IO#flush` dynamically from `IO#close` (#2594, @gogainda). * Implement `rb_str_new_static` for C extensions that use it (@aardvark179). -* Rewrote `ArrayEachIteratorNode` and re-introduced `each` specs for MRI parity when mutating arrays whilst iterating, rather than crashing (#2587, @MattAlp) +* Rewrote `ArrayEachIteratorNode` and re-introduced `each` specs for MRI parity when mutating arrays whilst iterating, rather than crashing (#2587, @MattAlp). * Update `String#rindex` to only accept `Regexp` or objects convertable to `String` as the first parameter (#2608, @bjfish). * Update `String#<<` to require one argument (#2609, @bjfish). * Update `String#split` to raise `TypeError` when false is given (#2606, @bjfish). @@ -403,12 +403,12 @@ Compatibility: Performance: -* Increase dispatch limit for string library to handle mutable, immutable and non-strings (@aardvark179) +* Increase dispatch limit for string library to handle mutable, immutable and non-strings (@aardvark179). * Switch to `Arrays.mismatch()` in string comparison for better performance (@aardvark179). * Removed extra array allocations for method calls in the interpreter to improve warmup performance (@aardvark179). * Optimize `Dir[]` by sorting entries as they are found and grouping syscalls (#2092, @aardvark179). * Reduce memory footprint by tracking `VALUE`s created during C extension init separately (@aardvark179). -* Rewrote `ArrayEachIteratorNode` to optimize performance for a constant-sized array and reduce specializations to 1 general case (#2587, @MattAlp) +* Rewrote `ArrayEachIteratorNode` to optimize performance for a constant-sized array and reduce specializations to 1 general case (#2587, @MattAlp). * Reduce conversion of `VALUE`s to native handle during common operations in C extensions (@aardvark179). * Improved performance of regex boolean matches (e.g., `Regexp#match?`) by avoiding match data allocation in TRegex (#2588, @nirvdrum). * Remove overhead when getting using `RDATA_PTR` (@aardvark179). @@ -588,7 +588,7 @@ Compatibility: * Support precision when formatting strings (#2281, @kirs). * Make rpartition compatible with Ruby 2.7 (#2320, @gogainda). * Include the type name in exception messages from `rb_check_type` (#2307). -* Fix `Hash#rehash` to remove duplicate keys after modifications (#2266, @MattAlp) +* Fix `Hash#rehash` to remove duplicate keys after modifications (#2266, @MattAlp). * Only fail `rb_check_type` for typed data, not wrapped untyped structs (#2331). * Decide the visibility in `Module#define_method` based on `self` and the default definee (#2334). * Configure `mandir` value in `RbConfig::CONFIG` and `RbConfig::MAKEFILE_CONFIG` (#2315). @@ -613,7 +613,7 @@ Performance: * Fiber-local variables are much faster now by using less synchronization. * Improved the performance of the exceptional case of `String#chr` (#2318, @chrisseaton). * Improved the performance of `IO#read_nonblock` when no data is available to be read. -* `TruffleSafepoint` is now used instead of custom logic, which no longer invalidates JITed code for guest safepoints (e.g., `Thread#{backtrace,raise,kill}`, `ObjectSpace`, etc) +* `TruffleSafepoint` is now used instead of custom logic, which no longer invalidates JITed code for guest safepoints (e.g., `Thread#{backtrace,raise,kill}`, `ObjectSpace`, etc). * Significantly improved performance of `Time#strftime` for common formats (#2361, @wildmaples, @chrisseaton). * Faster solution for lazy integer length (#2365, @lemire, @chrisseaton). * Speedup `rb_funcallv*()` by directly unwrapping the C arguments array instead of going through a Ruby `Array` (#2089). @@ -680,7 +680,7 @@ Compatibility: * Added beginless range support for `Array#{[], []=, slice, slice!, to_a, fill, values_at}` (#2155, @LillianZ). * Added beginless range support for `String#{byteslice, slice, slice!}` and `Symbol#slice` (#2211, @LillianZ). * Added beginless range support for `Kernel#{caller, caller_locations}` and `Thread#backtrace_locations` (#2211, @LillianZ). -* Make rand work with exclusive range with Float (#1506, @gogainda) +* Make rand work with exclusive range with Float (#1506, @gogainda). * Fixed `String#dump`'s formatting of escaped unicode characters (#2217, @meganniu). * Switched to the io-console C extension from C ruby for better performance and compatibility in `irb`. * Coerce the message to a `String` for `BasicSocket#send` (#2209, @HoneyryderChuck). @@ -732,7 +732,7 @@ New features: Bug fixes: -* Fix error message when the method name is not a Symbol or String for `Kernel#respond_to?` (#2132, @ssnickolay) +* Fix error message when the method name is not a Symbol or String for `Kernel#respond_to?` (#2132, @ssnickolay). * Fixed setting of special variables in enumerators and enumerables (#1484). * Fixed return value of `Enumerable#count` and `Enumerable#uniq` with multiple yielded arguments (#2145, @LillianZ). * Fixed `String#unpack` for `w*` format (#2143). @@ -743,12 +743,12 @@ Bug fixes: Compatibility: -* Implement `String#undump` (#2131, @kustosz) +* Implement `String#undump` (#2131, @kustosz). * `Errno` constants with the same `errno` number are now the same class. * Implement `Enumerable#tally` and `Enumerable#filter_map` (#2144 and #2152, @LillianZ). * Implement `Range#minmax`. * Pass more `Enumerator::Lazy#uniq` and `Enumerator::Lazy#chunk` specs (#2146, @LillianZ). -* Implement `Enumerator#produce` (#2160, @zverok) +* Implement `Enumerator#produce` (#2160, @zverok). * Implement `Complex#<=>` (#2004, @ssnickolay). * Add warning for `proc` without block (#2004, @ssnickolay). * Implemented `FrozenError#receiver`. @@ -781,13 +781,13 @@ Changes: Bug fixes: -* Handle foreign null object as falsy value (#1902, @ssnickolay) +* Handle foreign null object as falsy value (#1902, @ssnickolay). * Fixed return value of `Enumerable#first` with multiple yielded arguments (#2056, @LillianZ). * Improve reliability of the post install hook by disabling RubyGems (#2075). * Fixed top level exception handler to print exception cause (#2013). * Fixed issue when extending FFI from File (#2094). * Fixed issue with `Kernel#freeze` not freezing singleton class (#2093). -* Fixed `String#encode` with options issue (#2091, #2095, @LillianZ) +* Fixed `String#encode` with options issue (#2091, #2095, @LillianZ). * Fixed issue with `spawn` when `:close` redirect is used (#2097). * Fixed `coverage` issue when `*eval` is used (#2078). * Use expanded load paths for feature matching (#1501). @@ -796,14 +796,14 @@ Bug fixes: * Fixed constant/identifier detection in lexer for non-ASCII encodings (#2079, #2102, @ivoanjo). * Fixed parsing of `--jvm` as an application argument (#2108). * Fix `rb_rescue2` to ignore the end marker `(VALUE)0` (#2127, #2130). -* Fix status and output when SystemExit is subclassed and raised (#2128) +* Fix status and output when SystemExit is subclassed and raised (#2128). * Fix `String#{chomp, chomp!}` issue with invalid encoded strings (#2133). Compatibility: * Run `at_exit` handlers even if parsing the main script fails (#2047). * Load required libraries (`-r`) before parsing the main script (#2047). -* `String#split` supports block (#2052, @ssnickolay) +* `String#split` supports block (#2052, @ssnickolay). * Implemented `String#{grapheme_clusters, each_grapheme_cluster}`. * Fix the caller location for `#method_added` (#2059). * Fix issue with `Float#round` when `self` is `-0.0`. @@ -812,24 +812,24 @@ Compatibility: * Update `Range#cover?` to handle `Range` parameter. * Fix `String#{casecmp, casecmp?}` parameter conversion. * Fix `Regexp` issue which raised syntax error instead of `RegexpError` (#2066). -* Handle `Object#autoload` when autoload itself (#1616, @ssnickolay) +* Handle `Object#autoload` when autoload itself (#1616, @ssnickolay). * Skip upgraded default gems while loading RubyGems (#2075). * Verify that gem paths are correct before loading RubyGems (#2075). * Implement `rb_ivar_count`. * Implemented `rb_yield_values2`. * Implemented `Digest::Base#{update, <<}` (#2100). * Pass the final `super` specs (#2104, @chrisseaton). -* Fix arity for arguments with optional kwargs (#1669, @ssnickolay) -* Fix arity for `Proc` (#2098, @ssnickolay) +* Fix arity for arguments with optional kwargs (#1669, @ssnickolay). +* Fix arity for `Proc` (#2098, @ssnickolay). * Check bounds for `FFI::Pointer` accesses when the size of the memory behind is known. * Implement negative line numbers for eval (#1482). -* Support refinements for `#to_s` called by string interpolation (#2110, @ssnickolay) -* Module#using raises error in method scope (#2112, @ssnickolay) +* Support refinements for `#to_s` called by string interpolation (#2110, @ssnickolay). +* Module#using raises error in method scope (#2112, @ssnickolay). * `File#path` now returns a new mutable String on every call like MRI (#2115). * Avoid infinite recursion when redefining `Warning#warn` and calling `Kernel#warn` (#2109). * Convert objects with `#to_path` in `$LOAD_PATH` (#2119). * Handle the functions being native for `rb_thread_call_without_gvl()` (#2090). -* Support refinements for Kernel#respond_to? (#2120, @ssnickolay) +* Support refinements for Kernel#respond_to? (#2120, @ssnickolay). * JCodings has been updated from 1.0.45 to 1.0.55. * Joni has been updated from 2.1.30 to 2.1.40. @@ -908,11 +908,11 @@ Compatibility: * Implemented `rb_enc_isalnum` and `rb_enc_isspace`. * `RUBY_REVISION` is now the full commit hash used to build TruffleRuby, similar to MRI 2.7+. * Implemented `rb_enc_mbc_to_codepoint`. -* Changed the lookup methods to achieve Refinements specification (#2033, @ssnickolay) +* Changed the lookup methods to achieve Refinements specification (#2033, @ssnickolay). * Implemented `Digest::Instance#new` (#2040). * Implemented `ONIGENC_MBC_CASE_FOLD`. * Fixed `Thread#raise` to call the exception class' constructor with no arguments when given no message (#2045). -* Fixed `refine + super` compatibility (#2039, #2048, @ssnickolay) +* Fixed `refine + super` compatibility (#2039, #2048, @ssnickolay). * Make the top-level exception handler more compatible with MRI (#2047). * Implemented `rb_enc_codelen`. * Implemented `Ripper` by using the C extension (#1585). @@ -925,7 +925,7 @@ Performance: * Enable lazy translation from the parser AST to the Truffle AST for user code by default. This should improve application startup time (#1992). * `instance variable ... not initialized` and similar warnings are now optimized to have no peak performance impact if they are not printed (depends on `$VERBOSE`). -* Implement integer modular exponentiation using `BigInteger#mod_pow` (#1999, @skateman) +* Implement integer modular exponentiation using `BigInteger#mod_pow` (#1999, @skateman). * Fixed a performance issue when computing many substrings of a given non-leaf `String` with non-US-ASCII characters. * Speedup native handle to Ruby object lookup for C extensions. @@ -962,7 +962,7 @@ Bug fixes: * Updated `Kernel.Float()` to handle the `exception: false` parameter. * Fixed `String#unpack` `M` format (#1901). * Fixed error when `SystemCallError` message contained non-ASCII characters. -* Fixed `rb_rescue` to allow null rescue methods. (#1909, @kipply). +* Fixed `rb_rescue` to allow null rescue methods (#1909, @kipply). * Fixed incorrect comparisons between bignums and doubles. * Prevented some internal uses of `Kernel#caller_locations` to be overridden by user code (#1934). * Fixed an issue caused by recursing inlining within `Regexp#quote` (#1927). @@ -979,7 +979,7 @@ Bug fixes: * `alias_method` and `instance_methods` should now work correctly inside a refinement (#1942). * Fixed `Regexp.union` parameter conversion (#1963). * `IO#read(n)` no longer buffers more than needed, which could cause hanging if detecting readability via a native call such as `select(2)` (#1951). -* Fixed `Random::DEFAULT.seed` to be different on boot (#1965, @kipply) +* Fixed `Random::DEFAULT.seed` to be different on boot (#1965, @kipply). * `rb_encoding->name` can now be read even if the `rb_encoding` is stored in native memory. * Detect and cut off recursion when inspecting a foreign object, substituting an ellipsis instead. * Fixed feature lookup order to check every `$LOAD_PATH` path entry for `.rb`, then every entry for native extension when `require` is called with no extension. @@ -989,7 +989,7 @@ Bug fixes: * Actually unset environment variables with a `nil` value for `Process.spawn` instead of setting them to an empty String. * Core library methods part of the Native Image heap are no longer added in the compilation queue on the first call, but after they reach the thresholds like other methods. * Fix `RbConfig::CONFIG['LIBRUBY_SO']` file extension. -* Fix `char`, `short`, `unsigned char`, `unsigned int`, and `unsigned short` types in `Fiddle` (#1971). +* Fix `char`, `short`, `unsigned char`, `unsigned int`, and `unsigned short` types in `Fiddle` (#1971). * Fix `IO#select` to reallocate its buffer if it is interrupted by a signal. * Fix issue where interpolated string matched `#` within string as being a variable (#1495). * Fix `File.join` to raise error on strings with null bytes. @@ -1052,7 +1052,7 @@ Compatibility: Changes: * `TRUFFLERUBY_RESILIENT_GEM_HOME` has been removed. Unset `GEM_HOME` and `GEM_PATH` instead if you need to. -* The deprecated `Truffle::System.full_memory_barrier`, `Truffle::Primitive.logical_processors`, and `Truffle::AtomicReference` have been removed. +* The deprecated `Truffle::System.full_memory_barrier`, `Truffle::Primitive.logical_processors`, and `Truffle::AtomicReference` have been removed. * The implicit interface for allowing Ruby objects to behave as polyglot arrays with `#size`, `#[]` methods has been removed and replaced with an explicit interface where each method starts with `polyglot_*`. * Hash keys are no longer reported as polyglot members. * All remaining implicit polyglot behaviour for `#[]` method was replaced with `polyglot_*` methods. @@ -1904,7 +1904,7 @@ Performance: Bug fixes: -* The launcher accepts `--native` and similar options in the `TRUFFLERUBYOPT` environment variable. +* The launcher accepts `--native` and similar options in the `TRUFFLERUBYOPT` environment variable. Internal changes: diff --git a/spec/ruby/language/case_spec.rb b/spec/ruby/language/case_spec.rb index 58e1aaed9679..1a3925c9c6f9 100644 --- a/spec/ruby/language/case_spec.rb +++ b/spec/ruby/language/case_spec.rb @@ -329,49 +329,6 @@ def bar; @calls << :bar; end 100 end.should == 100 end -end - -describe "The 'case'-construct with no target expression" do - it "evaluates the body of the first clause when at least one of its condition expressions is true" do - case - when true, false; 'foo' - end.should == 'foo' - end - - it "evaluates the body of the first when clause that is not false/nil" do - case - when false; 'foo' - when 2; 'bar' - when 1 == 1; 'baz' - end.should == 'bar' - - case - when false; 'foo' - when nil; 'foo' - when 1 == 1; 'bar' - end.should == 'bar' - end - - it "evaluates the body of the else clause if all when clauses are false/nil" do - case - when false; 'foo' - when nil; 'foo' - when 1 == 2; 'bar' - else 'baz' - end.should == 'baz' - end - - it "evaluates multiple conditional expressions as a boolean disjunction" do - case - when true, false; 'foo' - else 'bar' - end.should == 'foo' - - case - when false, true; 'foo' - else 'bar' - end.should == 'foo' - end it "evaluates true as only 'true' when true is the first clause" do case 1 @@ -442,6 +399,49 @@ def ===(o) :called end.should == :called end +end + +describe "The 'case'-construct with no target expression" do + it "evaluates the body of the first clause when at least one of its condition expressions is true" do + case + when true, false; 'foo' + end.should == 'foo' + end + + it "evaluates the body of the first when clause that is not false/nil" do + case + when false; 'foo' + when 2; 'bar' + when 1 == 1; 'baz' + end.should == 'bar' + + case + when false; 'foo' + when nil; 'foo' + when 1 == 1; 'bar' + end.should == 'bar' + end + + it "evaluates the body of the else clause if all when clauses are false/nil" do + case + when false; 'foo' + when nil; 'foo' + when 1 == 2; 'bar' + else 'baz' + end.should == 'baz' + end + + it "evaluates multiple conditional expressions as a boolean disjunction" do + case + when true, false; 'foo' + else 'bar' + end.should == 'foo' + + case + when false, true; 'foo' + else 'bar' + end.should == 'foo' + end # Homogeneous cases are often optimized to avoid === using a jump table, and should be tested separately. # See https://github.com/jruby/jruby/issues/6440 @@ -451,4 +451,13 @@ def ===(o) when 2; 'bar' end.should == 'foo' end + + it "expands arrays to lists of values" do + case + when *[false] + "foo" + when *[true] + "bar" + end.should == "bar" + end end diff --git a/spec/tags/truffle/parsing/parsing_tags.txt b/spec/tags/truffle/parsing/parsing_tags.txt index b0b29e54bde9..ca4701df19df 100644 --- a/spec/tags/truffle/parsing/parsing_tags.txt +++ b/spec/tags/truffle/parsing/parsing_tags.txt @@ -47,13 +47,7 @@ fails:Parsing a Block (Tail expression / with explicit return inside then branch fails:Parsing a Block (a block with empty body) case is parsed correctly fails:Parsing a Block (a block with not empty body) case is parsed correctly fails:Parsing a Block (a block without parameters) case is parsed correctly -fails:Parsing a Break (break operator within a block) case is parsed correctly -fails:Parsing a case expression (case expression with expression/value to match (case a when ... end)) case is parsed correctly -fails:Parsing a case expression (case expression with expression to match and `else` branch (case a when ... else ... end)) case is parsed correctly -fails:Parsing a case expression (case expression with expression to match and multiple values in a `when` branch (case a when a, b ... end)) case is parsed correctly -fails:Parsing a case expression (case expression without expression to match (case when expr ... end)) case is parsed correctly -fails:Parsing a case expression (case expression without expression to match and with `else` branch (case when expr ... else ... end)) case is parsed correctly -fails:Parsing a case expression (case expression without expression to match and with multiple conditions in a `when` branch (case when a, b ... end)) case is parsed correctly +fails:Parsing a Break (within a block) case is parsed correctly fails:Parsing a class << (reopen an object singleton class) case is parsed correctly fails:Parsing a Complex number (Complex literal `bri` (without real part) where b is Float is represented as `Complext.convert(0, Rational.convert(b*100, 100))` where 100 is some power of 10 to convert b to Integer) case is parsed correctly fails:Parsing a Complex number (Complex literal `bri` (without real part) where b is Integer is represented as `Complext.convert(0, Rational.convert(b, 1))`) case is parsed correctly @@ -117,39 +111,12 @@ fails:Parsing a Method call (Arguments/with keyword arguments) case is parsed co fails:Parsing a Method call (Arguments/with positional argument and splat operator (a, *args)) case is parsed correctly fails:Parsing a Method call (Arguments/with splat operator (*args)) case is parsed correctly fails:Parsing a Method call (Arguments/with splat operator and positional arguments (*args, a)) case is parsed correctly -fails:Parsing a Method call (Primitive is replaced with a node implementing this method) case is parsed correctly -fails:Parsing a Method call (Special cases/method #!) case is parsed correctly -fails:Parsing a Method call (Special cases/method #%) case is parsed correctly -fails:Parsing a Method call (Special cases/method #&) case is parsed correctly -fails:Parsing a Method call (Special cases/method #*) case is parsed correctly -fails:Parsing a Method call (Special cases/method #+) case is parsed correctly -fails:Parsing a Method call (Special cases/method #-) case is parsed correctly -fails:Parsing a Method call (Special cases/method #-@) case is parsed correctly -fails:Parsing a Method call (Special cases/method #<) case is parsed correctly -fails:Parsing a Method call (Special cases/method #<<) case is parsed correctly -fails:Parsing a Method call (Special cases/method #<=) case is parsed correctly -fails:Parsing a Method call (Special cases/method #==) case is parsed correctly -fails:Parsing a Method call (Special cases/method #===) case is parsed correctly -fails:Parsing a Method call (Special cases/method #>) case is parsed correctly -fails:Parsing a Method call (Special cases/method #>=) case is parsed correctly -fails:Parsing a Method call (Special cases/method #>>) case is parsed correctly -fails:Parsing a Method call (Special cases/method #[]) case is parsed correctly -fails:Parsing a Method call (Special cases/method #[]=) case is parsed correctly -fails:Parsing a Method call (Special cases/method #at (Array#at)) case is parsed correctly -fails:Parsing a Method call (Special cases/method #bytesize) case is parsed correctly -fails:Parsing a Method call (Special cases/method #dedup called on a String literal) case is parsed correctly -fails:Parsing a Method call (Special cases/method #freeze) case is parsed correctly -fails:Parsing a Method call (Special cases/method #is_a?) case is parsed correctly -fails:Parsing a Method call (Special cases/method #kind_of?) case is parsed correctly fails:Parsing a Method call (Special cases/method #lambda (Kernel#lambda)) case is parsed correctly fails:Parsing a Method call (Special cases/method #lambda (not Kernel#lambda)) case is parsed correctly -fails:Parsing a Method call (Special cases/method #nil?) case is parsed correctly -fails:Parsing a Method call (Special cases/method #/) case is parsed correctly -fails:Parsing a Method call (Special cases/method #|) case is parsed correctly -fails:Parsing a Method call (Special cases/method #attr= (property assignment)) case is parsed correctly -fails:Parsing a Method call (Method call with implicit receiver (`foo`)) case is parsed correctly -fails:Parsing a Method call (With safe navigation operator (&.)) case is parsed correctly -fails:Parsing a Method call (Without arguments and parentheses) case is parsed correctly +fails:Parsing a Method call (super / in a method body with explicit arguments) case is parsed correctly +fails:Parsing a Method call (super / in a method body without explicit arguments) case is parsed correctly +fails:Parsing a Method call (super / outside a method body with explicit arguments) case is parsed correctly +fails:Parsing a Method call (super / outside a method body without explicit arguments) case is parsed correctly fails:Parsing a &&= (Assign an attribute local variable (a.b &&= c)) case is parsed correctly fails:Parsing a &&= (Assign an referenced element (a[b] &&= c)) case is parsed correctly fails:Parsing a &&= (Assign an element referenced with multiple indexes (a[b, c, d] &&= e)) case is parsed correctly @@ -160,11 +127,8 @@ fails:Parsing a &&= (Variable assignment/global variable ($a &&= b)) case is par fails:Parsing a &&= (Variable assignment/instance variable (@a &&= b)) case is parsed correctly fails:Parsing a &&= (Variable assignment/local variable (a &&= b)) case is parsed correctly fails:Parsing a Flip-flop operator (in a block) case is parsed correctly -fails:Parsing a Flip-flop operator (in a class body) case is parsed correctly fails:Parsing a Flip-flop operator (in a lambda) case is parsed correctly fails:Parsing a Flip-flop operator (in a method) case is parsed correctly -fails:Parsing a Flip-flop operator (in a module body) case is parsed correctly -fails:Parsing a Flip-flop operator (Flop-flop operator - a range literal in boolean context) case is parsed correctly fails:Parsing a Match (=~ operator) case is parsed correctly fails:Parsing a Match (=~ operator/with Regexp literal as a RHS) case is parsed correctly fails:Parsing a Match (=~ operator/with Regexp literal as a LHS (without named capture groups)) case is parsed correctly diff --git a/spec/truffle/parsing/fixtures/END.yaml b/spec/truffle/parsing/fixtures/END.yaml index bed3afd9f0ee..e2a8afc3b4c4 100644 --- a/spec/truffle/parsing/fixtures/END.yaml +++ b/spec/truffle/parsing/fixtures/END.yaml @@ -42,7 +42,6 @@ ast: | methodName = "at_exit" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: arguments = [ BooleanLiteralNode diff --git a/spec/truffle/parsing/fixtures/block/name/in_block.yaml b/spec/truffle/parsing/fixtures/block/name/in_block.yaml index df5c405b60d7..f250b5c0bb17 100644 --- a/spec/truffle/parsing/fixtures/block/name/in_block.yaml +++ b/spec/truffle/parsing/fixtures/block/name/in_block.yaml @@ -84,7 +84,6 @@ ast: | methodName = "proc" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: block = BlockDefinitionNode diff --git a/spec/truffle/parsing/fixtures/block/name/in_method.yaml b/spec/truffle/parsing/fixtures/block/name/in_method.yaml index d7b0f0c844d4..97e6d20be781 100644 --- a/spec/truffle/parsing/fixtures/block/name/in_method.yaml +++ b/spec/truffle/parsing/fixtures/block/name/in_method.yaml @@ -88,7 +88,6 @@ ast: | methodName = "proc" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: block = BlockDefinitionNode diff --git a/spec/truffle/parsing/fixtures/break/with_multiple_arguments.yaml b/spec/truffle/parsing/fixtures/break/with_multiple_arguments.yaml index 69120e05fc22..c3eb79c5fa0d 100644 --- a/spec/truffle/parsing/fixtures/break/with_multiple_arguments.yaml +++ b/spec/truffle/parsing/fixtures/break/with_multiple_arguments.yaml @@ -1,5 +1,5 @@ subject: "Break" -description: "break operator with multiple arguments" +description: "with multiple arguments" focused_on_node: "org.truffleruby.language.control.BreakNode" ruby: | while true diff --git a/spec/truffle/parsing/fixtures/break/with_argument.yaml b/spec/truffle/parsing/fixtures/break/with_single_argument.yaml similarity index 90% rename from spec/truffle/parsing/fixtures/break/with_argument.yaml rename to spec/truffle/parsing/fixtures/break/with_single_argument.yaml index 3017d4ecc9c0..a84f9d981b07 100644 --- a/spec/truffle/parsing/fixtures/break/with_argument.yaml +++ b/spec/truffle/parsing/fixtures/break/with_single_argument.yaml @@ -1,5 +1,5 @@ subject: "Break" -description: "break operator with argument" +description: "with single argument" focused_on_node: "org.truffleruby.language.control.BreakNode" ruby: | while true diff --git a/spec/truffle/parsing/fixtures/break/with_splat_operator.yaml b/spec/truffle/parsing/fixtures/break/with_splat_operator.yaml index 3c89d723e8b2..697a99fe088b 100644 --- a/spec/truffle/parsing/fixtures/break/with_splat_operator.yaml +++ b/spec/truffle/parsing/fixtures/break/with_splat_operator.yaml @@ -1,5 +1,5 @@ subject: "Break" -description: "break operator with splat operator (break *a)" +description: "with splat operator (break *a)" focused_on_node: "org.truffleruby.language.control.BreakNode" ruby: | while true diff --git a/spec/truffle/parsing/fixtures/break/with_splat_operator_and_following_argument.yaml b/spec/truffle/parsing/fixtures/break/with_splat_operator_and_following_argument.yaml new file mode 100644 index 000000000000..7f98dc7415e6 --- /dev/null +++ b/spec/truffle/parsing/fixtures/break/with_splat_operator_and_following_argument.yaml @@ -0,0 +1,66 @@ +subject: "Break" +description: "with splat operator and following argument (break *a, b)" +notes: > + Arguments are represented with combination of: + - ArrayConcatNode + - SplatCastNodeGen + - ArrayLiteralNode$UninitialisedArrayLiteralNode + nodes +yarp_specific: true # Simplified AST and replaced ArrayAppendOneNodeGen by concatenating with array of one element +focused_on_node: "org.truffleruby.language.control.BreakNode" +ruby: | + while true + break *foo, 42 + end +ast: | + BreakNode + attributes: + breakID = org.truffleruby.language.control.BreakID@... + flags = 1 + ignoreMarker = true + children: + child = + ArrayConcatNode + attributes: + flags = 0 + children: + children = [ + SplatCastNodeGen + attributes: + conversionMethod = :to_a + copy = true + flags = 0 + nilBehavior = CONVERT + children: + childNode_ = + RubyCallNode + attributes: + descriptor = org.truffleruby.language.arguments.EmptyArgumentsDescriptor@... + dispatchConfig = PRIVATE + emptyKeywordsProfile = false + flags = 0 + isAttrAssign = false + isSafeNavigation = false + isSplatted = false + isVCall = true + lastArgIsNotHashProfile = false + methodName = "foo" + notEmptyKeywordsProfile = false + notRuby2KeywordsHashProfile = false + children: + receiver = + SelfNode + attributes: + flags = 0 + ArrayLiteralNode$UninitialisedArrayLiteralNode + attributes: + flags = 0 + language = org.truffleruby.RubyLanguage@... + children: + values = [ + IntegerFixnumLiteralNode + attributes: + flags = 0 + value = 42 + ] + ] \ No newline at end of file diff --git a/spec/truffle/parsing/fixtures/break/with_splat_operator_and_following_arguments.yaml b/spec/truffle/parsing/fixtures/break/with_splat_operator_and_following_arguments.yaml new file mode 100644 index 000000000000..8d1a3162edf7 --- /dev/null +++ b/spec/truffle/parsing/fixtures/break/with_splat_operator_and_following_arguments.yaml @@ -0,0 +1,71 @@ +subject: "Break" +description: "with splat operator and following arguments (break *a, b, ...)" +notes: > + Arguments are represented with combination of: + - ArrayConcatNode + - SplatCastNodeGen + - ArrayLiteralNode$UninitialisedArrayLiteralNode + nodes +yarp_specific: true # Removed excessive SplatCastNodeGen: + # SplatCastNodeGen(UninitialisedArrayLiteralNode) -> UninitialisedArrayLiteralNode +focused_on_node: "org.truffleruby.language.control.BreakNode" +ruby: | + while true + break *foo, 42, 100500 + end +ast: | + BreakNode + attributes: + breakID = org.truffleruby.language.control.BreakID@... + flags = 1 + ignoreMarker = true + children: + child = + ArrayConcatNode + attributes: + flags = 0 + children: + children = [ + SplatCastNodeGen + attributes: + conversionMethod = :to_a + copy = true + flags = 0 + nilBehavior = CONVERT + children: + childNode_ = + RubyCallNode + attributes: + descriptor = org.truffleruby.language.arguments.EmptyArgumentsDescriptor@... + dispatchConfig = PRIVATE + emptyKeywordsProfile = false + flags = 0 + isAttrAssign = false + isSafeNavigation = false + isSplatted = false + isVCall = true + lastArgIsNotHashProfile = false + methodName = "foo" + notEmptyKeywordsProfile = false + notRuby2KeywordsHashProfile = false + children: + receiver = + SelfNode + attributes: + flags = 0 + ArrayLiteralNode$UninitialisedArrayLiteralNode + attributes: + flags = 0 + language = org.truffleruby.RubyLanguage@... + children: + values = [ + IntegerFixnumLiteralNode + attributes: + flags = 0 + value = 42 + IntegerFixnumLiteralNode + attributes: + flags = 0 + value = 100500 + ] + ] \ No newline at end of file diff --git a/spec/truffle/parsing/fixtures/break/with_splat_operator_and_preceding_and_following_arguments.yaml b/spec/truffle/parsing/fixtures/break/with_splat_operator_and_preceding_and_following_arguments.yaml new file mode 100644 index 000000000000..4a321b874d73 --- /dev/null +++ b/spec/truffle/parsing/fixtures/break/with_splat_operator_and_preceding_and_following_arguments.yaml @@ -0,0 +1,88 @@ +subject: "Break" +description: "with splat operator and preceding and following arguments (break a, ..., *b, c, ...)" +notes: > + Arguments are represented with combination of: + - ArrayConcatNode + - SplatCastNodeGen + - ArrayLiteralNode$UninitialisedArrayLiteralNode + nodes +yarp_specific: true # Removed excessive SplatCastNodeGen: + # SplatCastNodeGen(UninitialisedArrayLiteralNode) -> UninitialisedArrayLiteralNode +focused_on_node: "org.truffleruby.language.control.BreakNode" +ruby: | + while true + break 42, 100500, *foo, "bar", "baz" + end +ast: | + BreakNode + attributes: + breakID = org.truffleruby.language.control.BreakID@... + flags = 1 + ignoreMarker = true + children: + child = + ArrayConcatNode + attributes: + flags = 0 + children: + children = [ + ArrayLiteralNode$UninitialisedArrayLiteralNode + attributes: + flags = 0 + language = org.truffleruby.RubyLanguage@... + children: + values = [ + IntegerFixnumLiteralNode + attributes: + flags = 0 + value = 42 + IntegerFixnumLiteralNode + attributes: + flags = 0 + value = 100500 + ] + SplatCastNodeGen + attributes: + conversionMethod = :to_a + copy = true + flags = 0 + nilBehavior = CONVERT + children: + childNode_ = + RubyCallNode + attributes: + descriptor = org.truffleruby.language.arguments.EmptyArgumentsDescriptor@... + dispatchConfig = PRIVATE + emptyKeywordsProfile = false + flags = 0 + isAttrAssign = false + isSafeNavigation = false + isSplatted = false + isVCall = true + lastArgIsNotHashProfile = false + methodName = "foo" + notEmptyKeywordsProfile = false + notRuby2KeywordsHashProfile = false + children: + receiver = + SelfNode + attributes: + flags = 0 + ArrayLiteralNode$UninitialisedArrayLiteralNode + attributes: + flags = 0 + language = org.truffleruby.RubyLanguage@... + children: + values = [ + StringLiteralNode + attributes: + encoding = UTF-8 + flags = 0 + tstring = bar + StringLiteralNode + attributes: + encoding = UTF-8 + flags = 0 + tstring = baz + ] + ] \ No newline at end of file diff --git a/spec/truffle/parsing/fixtures/break/with_splat_operator_and_preceding_argument.yaml b/spec/truffle/parsing/fixtures/break/with_splat_operator_and_preceding_argument.yaml new file mode 100644 index 000000000000..8d33267bb996 --- /dev/null +++ b/spec/truffle/parsing/fixtures/break/with_splat_operator_and_preceding_argument.yaml @@ -0,0 +1,65 @@ +subject: "Break" +description: "with splat operator and preceding argument (break a, *b)" +notes: > + Arguments are represented with combination of: + - ArrayConcatNode + - SplatCastNodeGen + - ArrayLiteralNode$UninitialisedArrayLiteralNode + nodes +focused_on_node: "org.truffleruby.language.control.BreakNode" +ruby: | + while true + break 42, *foo + end +ast: | + BreakNode + attributes: + breakID = org.truffleruby.language.control.BreakID@... + flags = 1 + ignoreMarker = true + children: + child = + ArrayConcatNode + attributes: + flags = 0 + children: + children = [ + ArrayLiteralNode$UninitialisedArrayLiteralNode + attributes: + flags = 0 + language = org.truffleruby.RubyLanguage@... + children: + values = [ + IntegerFixnumLiteralNode + attributes: + flags = 0 + value = 42 + ] + SplatCastNodeGen + attributes: + conversionMethod = :to_a + copy = true + flags = 0 + nilBehavior = CONVERT + children: + childNode_ = + RubyCallNode + attributes: + descriptor = org.truffleruby.language.arguments.EmptyArgumentsDescriptor@... + dispatchConfig = PRIVATE + emptyKeywordsProfile = false + flags = 0 + isAttrAssign = false + isSafeNavigation = false + isSplatted = false + isVCall = true + lastArgIsNotHashProfile = false + methodName = "foo" + notEmptyKeywordsProfile = false + notRuby2KeywordsHashProfile = false + children: + receiver = + SelfNode + attributes: + flags = 0 + ] \ No newline at end of file diff --git a/spec/truffle/parsing/fixtures/break/with_splat_operator_and_preceding_arguments.yaml b/spec/truffle/parsing/fixtures/break/with_splat_operator_and_preceding_arguments.yaml new file mode 100644 index 000000000000..d5a8af38d53c --- /dev/null +++ b/spec/truffle/parsing/fixtures/break/with_splat_operator_and_preceding_arguments.yaml @@ -0,0 +1,69 @@ +subject: "Break" +description: "with splat operator and preceding arguments (break a, ..., *b)" +notes: > + Arguments are represented with combination of: + - ArrayConcatNode + - SplatCastNodeGen + - ArrayLiteralNode$UninitialisedArrayLiteralNode + nodes +focused_on_node: "org.truffleruby.language.control.BreakNode" +ruby: | + while true + break 42, 100500, *foo + end +ast: | + BreakNode + attributes: + breakID = org.truffleruby.language.control.BreakID@... + flags = 1 + ignoreMarker = true + children: + child = + ArrayConcatNode + attributes: + flags = 0 + children: + children = [ + ArrayLiteralNode$UninitialisedArrayLiteralNode + attributes: + flags = 0 + language = org.truffleruby.RubyLanguage@... + children: + values = [ + IntegerFixnumLiteralNode + attributes: + flags = 0 + value = 42 + IntegerFixnumLiteralNode + attributes: + flags = 0 + value = 100500 + ] + SplatCastNodeGen + attributes: + conversionMethod = :to_a + copy = true + flags = 0 + nilBehavior = CONVERT + children: + childNode_ = + RubyCallNode + attributes: + descriptor = org.truffleruby.language.arguments.EmptyArgumentsDescriptor@... + dispatchConfig = PRIVATE + emptyKeywordsProfile = false + flags = 0 + isAttrAssign = false + isSafeNavigation = false + isSplatted = false + isVCall = true + lastArgIsNotHashProfile = false + methodName = "foo" + notEmptyKeywordsProfile = false + notRuby2KeywordsHashProfile = false + children: + receiver = + SelfNode + attributes: + flags = 0 + ] \ No newline at end of file diff --git a/spec/truffle/parsing/fixtures/break/within_block.yaml b/spec/truffle/parsing/fixtures/break/within_block.yaml index 020b173e71ee..bb6cf76ea332 100644 --- a/spec/truffle/parsing/fixtures/break/within_block.yaml +++ b/spec/truffle/parsing/fixtures/break/within_block.yaml @@ -1,5 +1,5 @@ subject: "Break" -description: "break operator within a block" +description: "within a block" notes: > Calling not within a while loop leads to `ignoreMarker = false`. diff --git a/spec/truffle/parsing/fixtures/break/without_argument.yaml b/spec/truffle/parsing/fixtures/break/without_argument.yaml index 8ad27f24dfea..5c3a2fdf5ccb 100644 --- a/spec/truffle/parsing/fixtures/break/without_argument.yaml +++ b/spec/truffle/parsing/fixtures/break/without_argument.yaml @@ -1,5 +1,5 @@ subject: "Break" -description: "break operator without argument" +description: "without argument" notes: > Returning value is represented by NilLiteralNode focused_on_node: "org.truffleruby.language.control.BreakNode" diff --git a/spec/truffle/parsing/fixtures/case/with_expression_to_match_and_else_branch.yaml b/spec/truffle/parsing/fixtures/case/with_expression_and_when/with_else_branch.yaml similarity index 98% rename from spec/truffle/parsing/fixtures/case/with_expression_to_match_and_else_branch.yaml rename to spec/truffle/parsing/fixtures/case/with_expression_and_when/with_else_branch.yaml index 6b1316001f4a..797ac6f34544 100644 --- a/spec/truffle/parsing/fixtures/case/with_expression_to_match_and_else_branch.yaml +++ b/spec/truffle/parsing/fixtures/case/with_expression_and_when/with_else_branch.yaml @@ -1,5 +1,5 @@ subject: "case expression" -description: "case expression with expression to match and `else` branch (case a when ... else ... end)" +description: "with expression to match / and `else` branch (case exp when ... else ... end)" notes: > The case expression is represented with multiple nested `if`s. Matching in every branch is a `#===` method call (represented with InlinedCaseEqualNodeGen node). diff --git a/spec/truffle/parsing/fixtures/case/with_expression_to_match_and_with_multiple_values.yaml b/spec/truffle/parsing/fixtures/case/with_expression_and_when/with_multiple_values.yaml similarity index 64% rename from spec/truffle/parsing/fixtures/case/with_expression_to_match_and_with_multiple_values.yaml rename to spec/truffle/parsing/fixtures/case/with_expression_and_when/with_multiple_values.yaml index f2f06c445eb2..84b7b47c2e63 100644 --- a/spec/truffle/parsing/fixtures/case/with_expression_to_match_and_with_multiple_values.yaml +++ b/spec/truffle/parsing/fixtures/case/with_expression_and_when/with_multiple_values.yaml @@ -1,19 +1,18 @@ subject: "case expression" -description: "case expression with expression to match and multiple values in a `when` branch (case a when a, b ... end)" +description: "with expression to match / and multiple values in a `when` branch (case exp when a, b ... end)" notes: > - A list of values in a `when` expression is implemented as multiple nested `if` statements + A list of values in a `when` expression is implemented as `if` operator to compare each value in a list. So the example below is translated to the following expression: %case_1 = 42 - if 42 === %case_1 - "forty two" - if 42.0 === %case_1 + if 42 === %case_1 || 42.0 === %case_1 "forty two" else nil end +yarp_specific: true # Simplified AST and removed duplication of `when`'s body for each `when`'s condition focused_on_node: "org.truffleruby.language.control.SequenceNode" ruby: | case 42 @@ -55,30 +54,30 @@ ast: | flags = 0 children: condition = - InlinedCaseEqualNodeGen + OrNodeGen attributes: - assumptions = [Assumption(valid, name=set_trace_func is not used)] flags = 0 - integerCaseEqualAssumption = Assumption(valid, name=inlined Integer#===) - parameters = org.truffleruby.language.dispatch.RubyCallNodeParameters@... children: - leftNode_ = - IntegerFixnumLiteralNode - attributes: - flags = 0 - value = 42 - rightNode_ = - ReadLocalVariableNode + left = + InlinedCaseEqualNodeGen attributes: + assumptions = [Assumption(valid, name=set_trace_func is not used)] flags = 0 - frameSlot = 2 - type = FRAME_LOCAL - elseBody = - IfElseNodeGen - attributes: - flags = 0 - children: - condition = + integerCaseEqualAssumption = Assumption(valid, name=inlined Integer#===) + parameters = org.truffleruby.language.dispatch.RubyCallNodeParameters@... + children: + leftNode_ = + IntegerFixnumLiteralNode + attributes: + flags = 0 + value = 42 + rightNode_ = + ReadLocalVariableNode + attributes: + flags = 0 + frameSlot = 2 + type = FRAME_LOCAL + right = InlinedCaseEqualNodeGen attributes: assumptions = [Assumption(valid, name=set_trace_func is not used)] @@ -97,17 +96,11 @@ ast: | flags = 0 frameSlot = 2 type = FRAME_LOCAL - elseBody = - NilLiteralNode - attributes: - flags = 0 - isImplicit = false - thenBody = - StringLiteralNode - attributes: - encoding = UTF-8 - flags = 1 - tstring = forty two + elseBody = + NilLiteralNode + attributes: + flags = 0 + isImplicit = false thenBody = StringLiteralNode attributes: diff --git a/spec/truffle/parsing/fixtures/case/with_expression_to_match.yaml b/spec/truffle/parsing/fixtures/case/with_expression_and_when/with_single_value.yaml similarity index 98% rename from spec/truffle/parsing/fixtures/case/with_expression_to_match.yaml rename to spec/truffle/parsing/fixtures/case/with_expression_and_when/with_single_value.yaml index 0da59a2ca19b..af6f7ad9adfc 100644 --- a/spec/truffle/parsing/fixtures/case/with_expression_to_match.yaml +++ b/spec/truffle/parsing/fixtures/case/with_expression_and_when/with_single_value.yaml @@ -1,5 +1,5 @@ subject: "case expression" -description: "case expression with expression/value to match (case a when ... end)" +description: "with expression to match / and single value (case exp when a ... end)" notes: > The case expression is represented with multiple nested `if`s. Matching in every branch is a `#===` method call (represented with InlinedCaseEqualNodeGen node). diff --git a/spec/truffle/parsing/fixtures/case/with_expression_and_when/with_splat_operator.yaml b/spec/truffle/parsing/fixtures/case/with_expression_and_when/with_splat_operator.yaml new file mode 100644 index 000000000000..72c680935bea --- /dev/null +++ b/spec/truffle/parsing/fixtures/case/with_expression_and_when/with_splat_operator.yaml @@ -0,0 +1,79 @@ +subject: "case expression" +description: "with expression to match / and splat operator (case exp when *b ... end)" +notes: > + Splat operator is handled with `Truffle::Internal.when_splat` method implemented in Ruby +focused_on_node: "org.truffleruby.language.control.IfElseNode" +ruby: | + case 42 + when *foo + true + end +ast: | + IfElseNodeGen + attributes: + flags = 0 + children: + condition = + RubyCallNode + attributes: + descriptor = org.truffleruby.language.arguments.EmptyArgumentsDescriptor@... + dispatchConfig = PRIVATE + emptyKeywordsProfile = false + flags = 0 + isAttrAssign = false + isSafeNavigation = false + isSplatted = false + isVCall = false + lastArgIsNotHashProfile = false + methodName = "when_splat" + notEmptyKeywordsProfile = false + notRuby2KeywordsHashProfile = false + children: + arguments = [ + SplatCastNodeGen + attributes: + conversionMethod = :to_a + copy = true + flags = 0 + nilBehavior = CONVERT + children: + childNode_ = + RubyCallNode + attributes: + descriptor = org.truffleruby.language.arguments.EmptyArgumentsDescriptor@... + dispatchConfig = PRIVATE + emptyKeywordsProfile = false + flags = 0 + isAttrAssign = false + isSafeNavigation = false + isSplatted = false + isVCall = true + lastArgIsNotHashProfile = false + methodName = "foo" + notEmptyKeywordsProfile = false + notRuby2KeywordsHashProfile = false + children: + receiver = + SelfNode + attributes: + flags = 0 + ReadLocalVariableNode + attributes: + flags = 0 + frameSlot = 2 + type = FRAME_LOCAL + ] + receiver = + TruffleInternalModuleLiteralNode + attributes: + flags = 0 + elseBody = + NilLiteralNode + attributes: + flags = 0 + isImplicit = false + thenBody = + BooleanLiteralNode + attributes: + flags = 1 + value = true \ No newline at end of file diff --git a/spec/truffle/parsing/fixtures/case/with_expression_and_when/with_splat_operator_and_following_element.yaml b/spec/truffle/parsing/fixtures/case/with_expression_and_when/with_splat_operator_and_following_element.yaml new file mode 100644 index 000000000000..213e4e314fbb --- /dev/null +++ b/spec/truffle/parsing/fixtures/case/with_expression_and_when/with_splat_operator_and_following_element.yaml @@ -0,0 +1,97 @@ +subject: "case expression" +description: "with expression to match / and splat operator with following element (case exp when *a, b ... end)" +notes: > + Splat operator is handled with `Truffle::Internal.when_splat` method implemented in Ruby +yarp_specific: true # Simplified AST and replaced ArrayAppendOneNodeGen by concatenating with array of one element +focused_on_node: "org.truffleruby.language.control.IfElseNode" +ruby: | + case 42 + when *foo, 42 + true + end +ast: | + IfElseNodeGen + attributes: + flags = 0 + children: + condition = + RubyCallNode + attributes: + descriptor = org.truffleruby.language.arguments.EmptyArgumentsDescriptor@... + dispatchConfig = PRIVATE + emptyKeywordsProfile = false + flags = 0 + isAttrAssign = false + isSafeNavigation = false + isSplatted = false + isVCall = false + lastArgIsNotHashProfile = false + methodName = "when_splat" + notEmptyKeywordsProfile = false + notRuby2KeywordsHashProfile = false + children: + arguments = [ + ArrayConcatNode + attributes: + flags = 0 + children: + children = [ + SplatCastNodeGen + attributes: + conversionMethod = :to_a + copy = true + flags = 0 + nilBehavior = CONVERT + children: + childNode_ = + RubyCallNode + attributes: + descriptor = org.truffleruby.language.arguments.EmptyArgumentsDescriptor@... + dispatchConfig = PRIVATE + emptyKeywordsProfile = false + flags = 0 + isAttrAssign = false + isSafeNavigation = false + isSplatted = false + isVCall = true + lastArgIsNotHashProfile = false + methodName = "foo" + notEmptyKeywordsProfile = false + notRuby2KeywordsHashProfile = false + children: + receiver = + SelfNode + attributes: + flags = 0 + ArrayLiteralNode$UninitialisedArrayLiteralNode + attributes: + flags = 0 + language = org.truffleruby.RubyLanguage@... + children: + values = [ + IntegerFixnumLiteralNode + attributes: + flags = 0 + value = 42 + ] + ] + ReadLocalVariableNode + attributes: + flags = 0 + frameSlot = 2 + type = FRAME_LOCAL + ] + receiver = + TruffleInternalModuleLiteralNode + attributes: + flags = 0 + elseBody = + NilLiteralNode + attributes: + flags = 0 + isImplicit = false + thenBody = + BooleanLiteralNode + attributes: + flags = 1 + value = true \ No newline at end of file diff --git a/spec/truffle/parsing/fixtures/case/with_expression_and_when/with_splat_operator_and_following_elements.yaml b/spec/truffle/parsing/fixtures/case/with_expression_and_when/with_splat_operator_and_following_elements.yaml new file mode 100644 index 000000000000..b31d2c388889 --- /dev/null +++ b/spec/truffle/parsing/fixtures/case/with_expression_and_when/with_splat_operator_and_following_elements.yaml @@ -0,0 +1,102 @@ +subject: "case expression" +description: "with expression to match / and splat operator with multiple following elements (case exp when *a, b, c ... end)" +notes: > + Splat operator is handled with `Truffle::Internal.when_splat` method implemented in Ruby +yarp_specific: true # Removed excessive SplatCastNodeGen: + # SplatCastNodeGen(UninitialisedArrayLiteralNode) -> UninitialisedArrayLiteralNode +focused_on_node: "org.truffleruby.language.control.IfElseNode" +ruby: | + case 42 + when *foo, 42, 100500 + true + end +ast: | + IfElseNodeGen + attributes: + flags = 0 + children: + condition = + RubyCallNode + attributes: + descriptor = org.truffleruby.language.arguments.EmptyArgumentsDescriptor@... + dispatchConfig = PRIVATE + emptyKeywordsProfile = false + flags = 0 + isAttrAssign = false + isSafeNavigation = false + isSplatted = false + isVCall = false + lastArgIsNotHashProfile = false + methodName = "when_splat" + notEmptyKeywordsProfile = false + notRuby2KeywordsHashProfile = false + children: + arguments = [ + ArrayConcatNode + attributes: + flags = 0 + children: + children = [ + SplatCastNodeGen + attributes: + conversionMethod = :to_a + copy = true + flags = 0 + nilBehavior = CONVERT + children: + childNode_ = + RubyCallNode + attributes: + descriptor = org.truffleruby.language.arguments.EmptyArgumentsDescriptor@... + dispatchConfig = PRIVATE + emptyKeywordsProfile = false + flags = 0 + isAttrAssign = false + isSafeNavigation = false + isSplatted = false + isVCall = true + lastArgIsNotHashProfile = false + methodName = "foo" + notEmptyKeywordsProfile = false + notRuby2KeywordsHashProfile = false + children: + receiver = + SelfNode + attributes: + flags = 0 + ArrayLiteralNode$UninitialisedArrayLiteralNode + attributes: + flags = 0 + language = org.truffleruby.RubyLanguage@... + children: + values = [ + IntegerFixnumLiteralNode + attributes: + flags = 0 + value = 42 + IntegerFixnumLiteralNode + attributes: + flags = 0 + value = 100500 + ] + ] + ReadLocalVariableNode + attributes: + flags = 0 + frameSlot = 2 + type = FRAME_LOCAL + ] + receiver = + TruffleInternalModuleLiteralNode + attributes: + flags = 0 + elseBody = + NilLiteralNode + attributes: + flags = 0 + isImplicit = false + thenBody = + BooleanLiteralNode + attributes: + flags = 1 + value = true \ No newline at end of file diff --git a/spec/truffle/parsing/fixtures/case/with_expression_and_when/with_splat_operator_and_preceding_and_following_element.yaml b/spec/truffle/parsing/fixtures/case/with_expression_and_when/with_splat_operator_and_preceding_and_following_element.yaml new file mode 100644 index 000000000000..94a703c22bf2 --- /dev/null +++ b/spec/truffle/parsing/fixtures/case/with_expression_and_when/with_splat_operator_and_preceding_and_following_element.yaml @@ -0,0 +1,108 @@ +subject: "case expression" +description: "with expression to match / and splat operator with preceding and following element (case exp when a, *b, c ... end)" +notes: > + Splat operator is handled with `Truffle::Internal.when_splat` method implemented in Ruby +yarp_specific: true # Simplified AST and replaced ArrayAppendOneNodeGen by concatenating with array of one element +focused_on_node: "org.truffleruby.language.control.IfElseNode" +ruby: | + case 42 + when 42, *foo, 100500 + true + end +ast: | + IfElseNodeGen + attributes: + flags = 0 + children: + condition = + RubyCallNode + attributes: + descriptor = org.truffleruby.language.arguments.EmptyArgumentsDescriptor@... + dispatchConfig = PRIVATE + emptyKeywordsProfile = false + flags = 0 + isAttrAssign = false + isSafeNavigation = false + isSplatted = false + isVCall = false + lastArgIsNotHashProfile = false + methodName = "when_splat" + notEmptyKeywordsProfile = false + notRuby2KeywordsHashProfile = false + children: + arguments = [ + ArrayConcatNode + attributes: + flags = 0 + children: + children = [ + ArrayLiteralNode$UninitialisedArrayLiteralNode + attributes: + flags = 0 + language = org.truffleruby.RubyLanguage@... + children: + values = [ + IntegerFixnumLiteralNode + attributes: + flags = 0 + value = 42 + ] + SplatCastNodeGen + attributes: + conversionMethod = :to_a + copy = true + flags = 0 + nilBehavior = CONVERT + children: + childNode_ = + RubyCallNode + attributes: + descriptor = org.truffleruby.language.arguments.EmptyArgumentsDescriptor@... + dispatchConfig = PRIVATE + emptyKeywordsProfile = false + flags = 0 + isAttrAssign = false + isSafeNavigation = false + isSplatted = false + isVCall = true + lastArgIsNotHashProfile = false + methodName = "foo" + notEmptyKeywordsProfile = false + notRuby2KeywordsHashProfile = false + children: + receiver = + SelfNode + attributes: + flags = 0 + ArrayLiteralNode$UninitialisedArrayLiteralNode + attributes: + flags = 0 + language = org.truffleruby.RubyLanguage@... + children: + values = [ + IntegerFixnumLiteralNode + attributes: + flags = 0 + value = 100500 + ] + ] + ReadLocalVariableNode + attributes: + flags = 0 + frameSlot = 2 + type = FRAME_LOCAL + ] + receiver = + TruffleInternalModuleLiteralNode + attributes: + flags = 0 + elseBody = + NilLiteralNode + attributes: + flags = 0 + isImplicit = false + thenBody = + BooleanLiteralNode + attributes: + flags = 1 + value = true \ No newline at end of file diff --git a/spec/truffle/parsing/fixtures/case/with_expression_and_when/with_splat_operator_and_preceding_and_following_elements.yaml b/spec/truffle/parsing/fixtures/case/with_expression_and_when/with_splat_operator_and_preceding_and_following_elements.yaml new file mode 100644 index 000000000000..5362230bc6aa --- /dev/null +++ b/spec/truffle/parsing/fixtures/case/with_expression_and_when/with_splat_operator_and_preceding_and_following_elements.yaml @@ -0,0 +1,119 @@ +subject: "case expression" +description: "with expression to match / and splat operator with preceding and following elements (case exp when a, b, *c, d, e ... end)" +notes: > + Splat operator is handled with `Truffle::Internal.when_splat` method implemented in Ruby +yarp_specific: true # Removed excessive SplatCastNodeGen: + # SplatCastNodeGen(UninitialisedArrayLiteralNode) -> UninitialisedArrayLiteralNode +focused_on_node: "org.truffleruby.language.control.IfElseNode" +ruby: | + case 42 + when 42, 100500, *foo, "bar", "baz" + true + end +ast: | + IfElseNodeGen + attributes: + flags = 0 + children: + condition = + RubyCallNode + attributes: + descriptor = org.truffleruby.language.arguments.EmptyArgumentsDescriptor@... + dispatchConfig = PRIVATE + emptyKeywordsProfile = false + flags = 0 + isAttrAssign = false + isSafeNavigation = false + isSplatted = false + isVCall = false + lastArgIsNotHashProfile = false + methodName = "when_splat" + notEmptyKeywordsProfile = false + notRuby2KeywordsHashProfile = false + children: + arguments = [ + ArrayConcatNode + attributes: + flags = 0 + children: + children = [ + ArrayLiteralNode$UninitialisedArrayLiteralNode + attributes: + flags = 0 + language = org.truffleruby.RubyLanguage@... + children: + values = [ + IntegerFixnumLiteralNode + attributes: + flags = 0 + value = 42 + IntegerFixnumLiteralNode + attributes: + flags = 0 + value = 100500 + ] + SplatCastNodeGen + attributes: + conversionMethod = :to_a + copy = true + flags = 0 + nilBehavior = CONVERT + children: + childNode_ = + RubyCallNode + attributes: + descriptor = org.truffleruby.language.arguments.EmptyArgumentsDescriptor@... + dispatchConfig = PRIVATE + emptyKeywordsProfile = false + flags = 0 + isAttrAssign = false + isSafeNavigation = false + isSplatted = false + isVCall = true + lastArgIsNotHashProfile = false + methodName = "foo" + notEmptyKeywordsProfile = false + notRuby2KeywordsHashProfile = false + children: + receiver = + SelfNode + attributes: + flags = 0 + ArrayLiteralNode$UninitialisedArrayLiteralNode + attributes: + flags = 0 + language = org.truffleruby.RubyLanguage@... + children: + values = [ + StringLiteralNode + attributes: + encoding = UTF-8 + flags = 0 + tstring = bar + StringLiteralNode + attributes: + encoding = UTF-8 + flags = 0 + tstring = baz + ] + ] + ReadLocalVariableNode + attributes: + flags = 0 + frameSlot = 2 + type = FRAME_LOCAL + ] + receiver = + TruffleInternalModuleLiteralNode + attributes: + flags = 0 + elseBody = + NilLiteralNode + attributes: + flags = 0 + isImplicit = false + thenBody = + BooleanLiteralNode + attributes: + flags = 1 + value = true \ No newline at end of file diff --git a/spec/truffle/parsing/fixtures/case/with_expression_and_when/with_splat_operator_and_preceding_element.yaml b/spec/truffle/parsing/fixtures/case/with_expression_and_when/with_splat_operator_and_preceding_element.yaml new file mode 100644 index 000000000000..791c05e0138a --- /dev/null +++ b/spec/truffle/parsing/fixtures/case/with_expression_and_when/with_splat_operator_and_preceding_element.yaml @@ -0,0 +1,96 @@ +subject: "case expression" +description: "with expression to match / and splat operator with preceding element (case exp when a, *b ... end)" +notes: > + Splat operator is handled with `Truffle::Internal.when_splat` method implemented in Ruby +focused_on_node: "org.truffleruby.language.control.IfElseNode" +ruby: | + case 42 + when 42, *foo + true + end +ast: | + IfElseNodeGen + attributes: + flags = 0 + children: + condition = + RubyCallNode + attributes: + descriptor = org.truffleruby.language.arguments.EmptyArgumentsDescriptor@... + dispatchConfig = PRIVATE + emptyKeywordsProfile = false + flags = 0 + isAttrAssign = false + isSafeNavigation = false + isSplatted = false + isVCall = false + lastArgIsNotHashProfile = false + methodName = "when_splat" + notEmptyKeywordsProfile = false + notRuby2KeywordsHashProfile = false + children: + arguments = [ + ArrayConcatNode + attributes: + flags = 0 + children: + children = [ + ArrayLiteralNode$UninitialisedArrayLiteralNode + attributes: + flags = 0 + language = org.truffleruby.RubyLanguage@... + children: + values = [ + IntegerFixnumLiteralNode + attributes: + flags = 0 + value = 42 + ] + SplatCastNodeGen + attributes: + conversionMethod = :to_a + copy = true + flags = 0 + nilBehavior = CONVERT + children: + childNode_ = + RubyCallNode + attributes: + descriptor = org.truffleruby.language.arguments.EmptyArgumentsDescriptor@... + dispatchConfig = PRIVATE + emptyKeywordsProfile = false + flags = 0 + isAttrAssign = false + isSafeNavigation = false + isSplatted = false + isVCall = true + lastArgIsNotHashProfile = false + methodName = "foo" + notEmptyKeywordsProfile = false + notRuby2KeywordsHashProfile = false + children: + receiver = + SelfNode + attributes: + flags = 0 + ] + ReadLocalVariableNode + attributes: + flags = 0 + frameSlot = 2 + type = FRAME_LOCAL + ] + receiver = + TruffleInternalModuleLiteralNode + attributes: + flags = 0 + elseBody = + NilLiteralNode + attributes: + flags = 0 + isImplicit = false + thenBody = + BooleanLiteralNode + attributes: + flags = 1 + value = true \ No newline at end of file diff --git a/spec/truffle/parsing/fixtures/case/with_expression_and_when/with_splat_operator_and_preceding_elements.yaml b/spec/truffle/parsing/fixtures/case/with_expression_and_when/with_splat_operator_and_preceding_elements.yaml new file mode 100644 index 000000000000..aeab537be9fb --- /dev/null +++ b/spec/truffle/parsing/fixtures/case/with_expression_and_when/with_splat_operator_and_preceding_elements.yaml @@ -0,0 +1,100 @@ +subject: "case expression" +description: "with expression to match / and splat operator with preceding multiple elements (case exp when a, b, *c ... end)" +notes: > + Splat operator is handled with `Truffle::Internal.when_splat` method implemented in Ruby +focused_on_node: "org.truffleruby.language.control.IfElseNode" +ruby: | + case 42 + when 42, 100500, *foo + true + end +ast: | + IfElseNodeGen + attributes: + flags = 0 + children: + condition = + RubyCallNode + attributes: + descriptor = org.truffleruby.language.arguments.EmptyArgumentsDescriptor@... + dispatchConfig = PRIVATE + emptyKeywordsProfile = false + flags = 0 + isAttrAssign = false + isSafeNavigation = false + isSplatted = false + isVCall = false + lastArgIsNotHashProfile = false + methodName = "when_splat" + notEmptyKeywordsProfile = false + notRuby2KeywordsHashProfile = false + children: + arguments = [ + ArrayConcatNode + attributes: + flags = 0 + children: + children = [ + ArrayLiteralNode$UninitialisedArrayLiteralNode + attributes: + flags = 0 + language = org.truffleruby.RubyLanguage@... + children: + values = [ + IntegerFixnumLiteralNode + attributes: + flags = 0 + value = 42 + IntegerFixnumLiteralNode + attributes: + flags = 0 + value = 100500 + ] + SplatCastNodeGen + attributes: + conversionMethod = :to_a + copy = true + flags = 0 + nilBehavior = CONVERT + children: + childNode_ = + RubyCallNode + attributes: + descriptor = org.truffleruby.language.arguments.EmptyArgumentsDescriptor@... + dispatchConfig = PRIVATE + emptyKeywordsProfile = false + flags = 0 + isAttrAssign = false + isSafeNavigation = false + isSplatted = false + isVCall = true + lastArgIsNotHashProfile = false + methodName = "foo" + notEmptyKeywordsProfile = false + notRuby2KeywordsHashProfile = false + children: + receiver = + SelfNode + attributes: + flags = 0 + ] + ReadLocalVariableNode + attributes: + flags = 0 + frameSlot = 2 + type = FRAME_LOCAL + ] + receiver = + TruffleInternalModuleLiteralNode + attributes: + flags = 0 + elseBody = + NilLiteralNode + attributes: + flags = 0 + isImplicit = false + thenBody = + BooleanLiteralNode + attributes: + flags = 1 + value = true \ No newline at end of file diff --git a/spec/truffle/parsing/fixtures/case/without_expression_to_match_and_with_else_branch.yaml b/spec/truffle/parsing/fixtures/case/without_expression_and_when/with_else_branch.yaml similarity index 93% rename from spec/truffle/parsing/fixtures/case/without_expression_to_match_and_with_else_branch.yaml rename to spec/truffle/parsing/fixtures/case/without_expression_and_when/with_else_branch.yaml index efb819f7c0ac..d1d7b19313ff 100644 --- a/spec/truffle/parsing/fixtures/case/without_expression_to_match_and_with_else_branch.yaml +++ b/spec/truffle/parsing/fixtures/case/without_expression_and_when/with_else_branch.yaml @@ -1,5 +1,5 @@ subject: "case expression" -description: "case expression without expression to match and with `else` branch (case when expr ... else ... end)" +description: "without expression to match / with `else` branch (case when expr ... else ... end)" notes: > The case expression is represented with multiple nested `if`s. Each `when`'s condition is casted to boolean with BooleanCastNodeGen node. @@ -27,7 +27,7 @@ ruby: | ast: | IfElseNodeGen attributes: - flags = 0 + flags = 1 children: condition = IntegerFixnumLiteralNode diff --git a/spec/truffle/parsing/fixtures/case/without_expression_to_match_and_with_multiple_conditions.yaml b/spec/truffle/parsing/fixtures/case/without_expression_and_when/with_multiple_values.yaml similarity index 58% rename from spec/truffle/parsing/fixtures/case/without_expression_to_match_and_with_multiple_conditions.yaml rename to spec/truffle/parsing/fixtures/case/without_expression_and_when/with_multiple_values.yaml index 9a82e26c1fdf..bb33cbfa497a 100644 --- a/spec/truffle/parsing/fixtures/case/without_expression_to_match_and_with_multiple_conditions.yaml +++ b/spec/truffle/parsing/fixtures/case/without_expression_and_when/with_multiple_values.yaml @@ -1,18 +1,17 @@ subject: "case expression" -description: "case expression without expression to match and with multiple conditions in a `when` branch (case when a, b ... end)" +description: "without expression to match / with multiple values in a `when` branch (case when a, b ... end)" notes: > - A list of conditions in a `when` expression is represented as multiple nested `if` statements + A list of conditions in a `when` expression is represented as a single `if` operator to evaluate each of conditions. So the example below is translated to the following expression: - if (true) - "1st or 2d condition" - else (false) + if true || false "1st or 2d condition" else nil end +yarp_specific: true # Simplified AST and removed duplication of `when`'s body for each `when`'s condition focused_on_node: "org.truffleruby.language.control.IfElseNode" ruby: | case @@ -22,34 +21,28 @@ ruby: | ast: | IfElseNodeGen attributes: - flags = 0 + flags = 1 children: condition = - BooleanLiteralNode - attributes: - flags = 0 - value = true - elseBody = - IfElseNodeGen + OrNodeGen attributes: flags = 0 children: - condition = + left = BooleanLiteralNode attributes: flags = 0 - value = false - elseBody = - NilLiteralNode + value = true + right = + BooleanLiteralNode attributes: flags = 0 - isImplicit = false - thenBody = - StringLiteralNode - attributes: - encoding = UTF-8 - flags = 1 - tstring = 1st or 2d condition + value = false + elseBody = + NilLiteralNode + attributes: + flags = 0 + isImplicit = false thenBody = StringLiteralNode attributes: diff --git a/spec/truffle/parsing/fixtures/case/without_expression_to_match.yaml b/spec/truffle/parsing/fixtures/case/without_expression_and_when/with_single_value.yaml similarity index 93% rename from spec/truffle/parsing/fixtures/case/without_expression_to_match.yaml rename to spec/truffle/parsing/fixtures/case/without_expression_and_when/with_single_value.yaml index 087d13d1b5b9..ff4ccae78c57 100644 --- a/spec/truffle/parsing/fixtures/case/without_expression_to_match.yaml +++ b/spec/truffle/parsing/fixtures/case/without_expression_and_when/with_single_value.yaml @@ -1,5 +1,5 @@ subject: "case expression" -description: "case expression without expression to match (case when expr ... end)" +description: "without expression to match / with single value in a when branch (case when a ... end)" notes: > The case expression is represented with multiple nested `if`s. Each `when`'s condition is casted to boolean with BooleanCastNodeGen node. @@ -25,7 +25,7 @@ ruby: | ast: | IfElseNodeGen attributes: - flags = 0 + flags = 1 children: condition = IntegerFixnumLiteralNode diff --git a/spec/truffle/parsing/fixtures/case/without_expression_and_when/with_splat_operator.yaml b/spec/truffle/parsing/fixtures/case/without_expression_and_when/with_splat_operator.yaml new file mode 100644 index 000000000000..a8352e919988 --- /dev/null +++ b/spec/truffle/parsing/fixtures/case/without_expression_and_when/with_splat_operator.yaml @@ -0,0 +1,98 @@ +subject: "case expression" +description: "without expression to match / and splat operator (case when *a ... end)" +notes: > + Splat operator is handled with `Array#any?` method to check that there is at least one truthy `when`'s operand +yarp_specific: true # Simplified AST and replaced ArrayAppendOneNodeGen by concatenating with array of one element +focused_on_node: "org.truffleruby.language.control.IfElseNode" +ruby: | + case + when 42, *foo, 100500 + true + end +ast: | + IfElseNodeGen + attributes: + flags = 1 + children: + condition = + RubyCallNode + attributes: + descriptor = org.truffleruby.language.arguments.EmptyArgumentsDescriptor@... + dispatchConfig = PRIVATE + emptyKeywordsProfile = false + flags = 0 + isAttrAssign = false + isSafeNavigation = false + isSplatted = false + isVCall = false + lastArgIsNotHashProfile = false + methodName = "any?" + notEmptyKeywordsProfile = false + notRuby2KeywordsHashProfile = false + children: + receiver = + ArrayConcatNode + attributes: + flags = 0 + children: + children = [ + ArrayLiteralNode$UninitialisedArrayLiteralNode + attributes: + flags = 0 + language = org.truffleruby.RubyLanguage@... + children: + values = [ + IntegerFixnumLiteralNode + attributes: + flags = 0 + value = 42 + ] + SplatCastNodeGen + attributes: + conversionMethod = :to_a + copy = true + flags = 0 + nilBehavior = CONVERT + children: + childNode_ = + RubyCallNode + attributes: + descriptor = org.truffleruby.language.arguments.EmptyArgumentsDescriptor@... + dispatchConfig = PRIVATE + emptyKeywordsProfile = false + flags = 0 + isAttrAssign = false + isSafeNavigation = false + isSplatted = false + isVCall = true + lastArgIsNotHashProfile = false + methodName = "foo" + notEmptyKeywordsProfile = false + notRuby2KeywordsHashProfile = false + children: + receiver = + SelfNode + attributes: + flags = 0 + ArrayLiteralNode$UninitialisedArrayLiteralNode + attributes: + flags = 0 + language = org.truffleruby.RubyLanguage@... + children: + values = [ + IntegerFixnumLiteralNode + attributes: + flags = 0 + value = 100500 + ] + ] + elseBody = + NilLiteralNode + attributes: + flags = 0 + isImplicit = false + thenBody = + BooleanLiteralNode + attributes: + flags = 1 + value = true \ No newline at end of file diff --git a/spec/truffle/parsing/fixtures/complex_numbers/with_float_rational_imaginary_part.yaml b/spec/truffle/parsing/fixtures/complex_numbers/with_float_rational_imaginary_part.yaml index 51a147dc2133..44e470ee1532 100644 --- a/spec/truffle/parsing/fixtures/complex_numbers/with_float_rational_imaginary_part.yaml +++ b/spec/truffle/parsing/fixtures/complex_numbers/with_float_rational_imaginary_part.yaml @@ -21,7 +21,6 @@ ast: | methodName = "convert" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: arguments = [ IntegerFixnumLiteralNode @@ -42,7 +41,6 @@ ast: | methodName = "convert" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: arguments = [ IntegerFixnumLiteralNode diff --git a/spec/truffle/parsing/fixtures/complex_numbers/with_integer_rational_imaginary_part.yaml b/spec/truffle/parsing/fixtures/complex_numbers/with_integer_rational_imaginary_part.yaml index 15b85fb4f37a..398fd4abd757 100644 --- a/spec/truffle/parsing/fixtures/complex_numbers/with_integer_rational_imaginary_part.yaml +++ b/spec/truffle/parsing/fixtures/complex_numbers/with_integer_rational_imaginary_part.yaml @@ -20,7 +20,6 @@ ast: | methodName = "convert" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: arguments = [ IntegerFixnumLiteralNode @@ -41,7 +40,6 @@ ast: | methodName = "convert" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: arguments = [ IntegerFixnumLiteralNode diff --git a/spec/truffle/parsing/fixtures/complex_numbers/with_rational_and_imaginary_parts.yaml b/spec/truffle/parsing/fixtures/complex_numbers/with_rational_and_imaginary_parts.yaml index be68cc9667fc..e92473808f34 100644 --- a/spec/truffle/parsing/fixtures/complex_numbers/with_rational_and_imaginary_parts.yaml +++ b/spec/truffle/parsing/fixtures/complex_numbers/with_rational_and_imaginary_parts.yaml @@ -32,7 +32,6 @@ ast: | methodName = "convert" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: arguments = [ IntegerFixnumLiteralNode diff --git a/spec/truffle/parsing/fixtures/defined/with_yield_in_method.yaml b/spec/truffle/parsing/fixtures/defined/with_yield_in_method.yaml index f4c8fd9fef9a..e7f7178e4812 100644 --- a/spec/truffle/parsing/fixtures/defined/with_yield_in_method.yaml +++ b/spec/truffle/parsing/fixtures/defined/with_yield_in_method.yaml @@ -76,7 +76,6 @@ ast: | lastArgIsNotHashProfile = false notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: readBlockNode = ReadLocalVariableNode diff --git a/spec/truffle/parsing/fixtures/ensure/in_do_end_block.yaml b/spec/truffle/parsing/fixtures/ensure/in_do_end_block.yaml index 82abf86ce68a..ba043788eea6 100644 --- a/spec/truffle/parsing/fixtures/ensure/in_do_end_block.yaml +++ b/spec/truffle/parsing/fixtures/ensure/in_do_end_block.yaml @@ -78,7 +78,6 @@ ast: | methodName = "bar" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: receiver = SelfNode diff --git a/spec/truffle/parsing/fixtures/ensure/in_method.yaml b/spec/truffle/parsing/fixtures/ensure/in_method.yaml index d409221cfe63..27ff1b73ca27 100644 --- a/spec/truffle/parsing/fixtures/ensure/in_method.yaml +++ b/spec/truffle/parsing/fixtures/ensure/in_method.yaml @@ -82,7 +82,6 @@ ast: | methodName = "bar" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: receiver = SelfNode diff --git a/spec/truffle/parsing/fixtures/floats/negative.yaml b/spec/truffle/parsing/fixtures/floats/negative.yaml new file mode 100644 index 000000000000..fc5cf29bc6a0 --- /dev/null +++ b/spec/truffle/parsing/fixtures/floats/negative.yaml @@ -0,0 +1,10 @@ +subject: "Float" +description: "negative" +focused_on_node: "org.truffleruby.language.literal.FloatLiteralNode" +ruby: | + -3.14 +ast: | + FloatLiteralNode + attributes: + flags = 1 + value = -3.14 \ No newline at end of file diff --git a/spec/truffle/parsing/fixtures/for_operator.yaml b/spec/truffle/parsing/fixtures/for_operator.yaml index de3411f19424..5ac19c7139c7 100644 --- a/spec/truffle/parsing/fixtures/for_operator.yaml +++ b/spec/truffle/parsing/fixtures/for_operator.yaml @@ -54,7 +54,6 @@ ast: | methodName = "each" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: block = BlockDefinitionNode diff --git a/spec/truffle/parsing/fixtures/integers/negative.yaml b/spec/truffle/parsing/fixtures/integers/negative.yaml new file mode 100644 index 000000000000..9dc7fcef4273 --- /dev/null +++ b/spec/truffle/parsing/fixtures/integers/negative.yaml @@ -0,0 +1,10 @@ +subject: "Integer" +description: "negative literal" +focused_on_node: "org.truffleruby.language.literal.IntegerFixnumLiteralNode" +ruby: | + -42 +ast: | + IntegerFixnumLiteralNode + attributes: + flags = 1 + value = -42 \ No newline at end of file diff --git a/spec/truffle/parsing/fixtures/integers/literal.yaml b/spec/truffle/parsing/fixtures/integers/positive.yaml similarity index 83% rename from spec/truffle/parsing/fixtures/integers/literal.yaml rename to spec/truffle/parsing/fixtures/integers/positive.yaml index b6735ab63fd3..2e81fdd93be2 100644 --- a/spec/truffle/parsing/fixtures/integers/literal.yaml +++ b/spec/truffle/parsing/fixtures/integers/positive.yaml @@ -1,5 +1,5 @@ subject: "Integer" -description: "Integer decimal literal" +description: "positive literal" focused_on_node: "org.truffleruby.language.literal.IntegerFixnumLiteralNode" ruby: | 42 diff --git a/spec/truffle/parsing/fixtures/integers/zero.yaml b/spec/truffle/parsing/fixtures/integers/zero.yaml new file mode 100644 index 000000000000..60f71354071b --- /dev/null +++ b/spec/truffle/parsing/fixtures/integers/zero.yaml @@ -0,0 +1,12 @@ +subject: "Integer" +description: "Zero" +notes: > + It should be parsed correctly and not mixed with octal Integer literals +focused_on_node: "org.truffleruby.language.literal.IntegerFixnumLiteralNode" +ruby: | + 0 +ast: | + IntegerFixnumLiteralNode + attributes: + flags = 1 + value = 0 \ No newline at end of file diff --git a/spec/truffle/parsing/fixtures/method_calls/arguments/with_&_decontruction_to_block.yaml b/spec/truffle/parsing/fixtures/method_calls/arguments/with_&_decontruction_to_block.yaml index d7f99b378ccc..92baf0488a64 100644 --- a/spec/truffle/parsing/fixtures/method_calls/arguments/with_&_decontruction_to_block.yaml +++ b/spec/truffle/parsing/fixtures/method_calls/arguments/with_&_decontruction_to_block.yaml @@ -21,7 +21,6 @@ ast: | methodName = "foo" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: block = ToProcNodeGen diff --git a/spec/truffle/parsing/fixtures/method_calls/arguments/with_block_literal_argument.yaml b/spec/truffle/parsing/fixtures/method_calls/arguments/with_block_literal_argument.yaml index 90dc6b632b11..17ef14be6a9e 100644 --- a/spec/truffle/parsing/fixtures/method_calls/arguments/with_block_literal_argument.yaml +++ b/spec/truffle/parsing/fixtures/method_calls/arguments/with_block_literal_argument.yaml @@ -34,7 +34,6 @@ ast: | methodName = "foo" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: block = BlockDefinitionNode diff --git a/spec/truffle/parsing/fixtures/method_calls/arguments/with_double_splat_operator.yaml b/spec/truffle/parsing/fixtures/method_calls/arguments/with_double_splat_operator.yaml index 744b9a056f08..267df741f32e 100644 --- a/spec/truffle/parsing/fixtures/method_calls/arguments/with_double_splat_operator.yaml +++ b/spec/truffle/parsing/fixtures/method_calls/arguments/with_double_splat_operator.yaml @@ -20,7 +20,6 @@ ast: | methodName = "foo" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: arguments = [ HashCastNodeGen$HashCastASTNodeGen @@ -42,7 +41,6 @@ ast: | methodName = "bar" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: receiver = SelfNode diff --git a/spec/truffle/parsing/fixtures/method_calls/arguments/with_keyword_arguments.yaml b/spec/truffle/parsing/fixtures/method_calls/arguments/with_keyword_arguments.yaml index 9b1b75759072..9b843331c1f0 100644 --- a/spec/truffle/parsing/fixtures/method_calls/arguments/with_keyword_arguments.yaml +++ b/spec/truffle/parsing/fixtures/method_calls/arguments/with_keyword_arguments.yaml @@ -23,7 +23,6 @@ ast: | methodName = "foo" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: arguments = [ PackedHashStoreLibraryFactory$SmallHashLiteralNodeGen diff --git a/spec/truffle/parsing/fixtures/method_calls/arguments/with_positional_argument_and_splat_operator.yaml b/spec/truffle/parsing/fixtures/method_calls/arguments/with_positional_argument_and_splat_operator.yaml index 46fecbd64a0f..e742eb38ee99 100644 --- a/spec/truffle/parsing/fixtures/method_calls/arguments/with_positional_argument_and_splat_operator.yaml +++ b/spec/truffle/parsing/fixtures/method_calls/arguments/with_positional_argument_and_splat_operator.yaml @@ -20,7 +20,6 @@ ast: | methodName = "foo" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: arguments = [ ArrayConcatNode @@ -48,7 +47,6 @@ ast: | methodName = "bar" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: receiver = SelfNode @@ -77,7 +75,6 @@ ast: | methodName = "baz" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: receiver = SelfNode diff --git a/spec/truffle/parsing/fixtures/method_calls/arguments/with_splat_operator.yaml b/spec/truffle/parsing/fixtures/method_calls/arguments/with_splat_operator.yaml index 26fc5703d0cf..8b2c1e4d2356 100644 --- a/spec/truffle/parsing/fixtures/method_calls/arguments/with_splat_operator.yaml +++ b/spec/truffle/parsing/fixtures/method_calls/arguments/with_splat_operator.yaml @@ -22,7 +22,6 @@ ast: | methodName = "foo" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: arguments = [ SplatCastNodeGen @@ -47,7 +46,6 @@ ast: | methodName = "bar" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: receiver = SelfNode diff --git a/spec/truffle/parsing/fixtures/method_calls/arguments/with_splat_operator_and_positional_arguments.yaml b/spec/truffle/parsing/fixtures/method_calls/arguments/with_splat_operator_and_positional_arguments.yaml index daa5e916ef05..865d06fa29f6 100644 --- a/spec/truffle/parsing/fixtures/method_calls/arguments/with_splat_operator_and_positional_arguments.yaml +++ b/spec/truffle/parsing/fixtures/method_calls/arguments/with_splat_operator_and_positional_arguments.yaml @@ -23,7 +23,6 @@ ast: | methodName = "foo" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: arguments = [ ArrayAppendOneNodeGen @@ -58,7 +57,6 @@ ast: | methodName = "bar" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: receiver = SelfNode @@ -79,7 +77,6 @@ ast: | methodName = "baz" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: receiver = SelfNode diff --git a/spec/truffle/parsing/fixtures/method_calls/primitive.yaml b/spec/truffle/parsing/fixtures/method_calls/primitive.yaml index c5d170a56364..c6f13a382388 100644 --- a/spec/truffle/parsing/fixtures/method_calls/primitive.yaml +++ b/spec/truffle/parsing/fixtures/method_calls/primitive.yaml @@ -1,6 +1,8 @@ subject: "Method call" -description: "Primitive is replaced with a node implementing this method" +description: "Primitive method" notes: > + Primitive is replaced with a node implementing this method. + Focus on the top node instead of a particular Primitive method implementation. focused_on_node: "org.truffleruby.language.RubyTopLevelRootNode" ruby: | @@ -50,7 +52,7 @@ ast: | flags = 0 TypeNodesFactory$IsNilNodeFactory$IsNilNodeGen attributes: - flags = 1 + flags = 0 children: argumentNodes0_ = IntegerFixnumLiteralNode diff --git a/spec/truffle/parsing/fixtures/method_calls/special_cases/method_!.yaml b/spec/truffle/parsing/fixtures/method_calls/special_cases/method_!.yaml index a9fc1e37807a..99cad7459b23 100644 --- a/spec/truffle/parsing/fixtures/method_calls/special_cases/method_!.yaml +++ b/spec/truffle/parsing/fixtures/method_calls/special_cases/method_!.yaml @@ -14,6 +14,6 @@ ast: | selfNode_ = ReadLocalVariableNode attributes: - flags = 1 + flags = 0 frameSlot = 2 type = FRAME_LOCAL \ No newline at end of file diff --git a/spec/truffle/parsing/fixtures/method_calls/special_cases/method_lambda_not_kernel.yaml b/spec/truffle/parsing/fixtures/method_calls/special_cases/method_lambda_not_kernel.yaml index 0d27ee4109b1..44f96f2b69c3 100644 --- a/spec/truffle/parsing/fixtures/method_calls/special_cases/method_lambda_not_kernel.yaml +++ b/spec/truffle/parsing/fixtures/method_calls/special_cases/method_lambda_not_kernel.yaml @@ -26,7 +26,6 @@ ast: | methodName = "lambda" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: arguments = [ IntegerFixnumLiteralNode diff --git a/spec/truffle/parsing/fixtures/method_calls/special_cases/property_assignment.yaml b/spec/truffle/parsing/fixtures/method_calls/special_cases/property_assignment.yaml index 6260308dc60f..008cb1471f3e 100644 --- a/spec/truffle/parsing/fixtures/method_calls/special_cases/property_assignment.yaml +++ b/spec/truffle/parsing/fixtures/method_calls/special_cases/property_assignment.yaml @@ -20,7 +20,6 @@ ast: | methodName = "foobar=" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: arguments = [ IntegerFixnumLiteralNode @@ -43,7 +42,6 @@ ast: | methodName = "a" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: receiver = SelfNode diff --git a/spec/truffle/parsing/fixtures/method_calls/super/in_method_body_with_explicit_arguments.yaml b/spec/truffle/parsing/fixtures/method_calls/super/in_method_body_with_explicit_arguments.yaml new file mode 100644 index 000000000000..93f9b44b3ccc --- /dev/null +++ b/spec/truffle/parsing/fixtures/method_calls/super/in_method_body_with_explicit_arguments.yaml @@ -0,0 +1,90 @@ +subject: "Method call" +description: super / in a method body with explicit arguments +notes: > + Is represented by SuperCallNode and ReadSuperArgumentsNode nodes +focused_on_node: "org.truffleruby.language.methods.LiteralMethodDefinitionNode" +ruby: | + def foo + super(42) + end +ast: | + LiteralMethodDefinitionNode + attributes: + callTargetSupplier = org.truffleruby.language.methods.CachedLazyCallTargetSupplier@... + flags = 1 + isDefSingleton = false + name = "foo" + sharedMethodInfo = SharedMethodInfo(sourceSection = SourceSection(source= [1 - 3], index=0, length=23, characters=def foo\n super(42)\nend), staticLexicalScope = :: Object, arity = Arity{preRequired = 0, optional = 0, hasRest = false, postRequired = 0, keywordArguments = [], requiredKeywordArgumentsCount = 0, hasKeywordsRest = false}, originName = foo, blockDepth = 0, parseName = Object#foo, notes = null, argumentDescriptors = []) + call targets: + RubyMethodRootNode + attributes: + arityForCheck = Arity{preRequired = 0, optional = 0, hasRest = false, postRequired = 0, keywordArguments = [], requiredKeywordArgumentsCount = 0, hasKeywordsRest = false} + callTarget = Object#foo + checkArityProfile = false + frameDescriptor = FrameDescriptor@...{#0:(self), #1:%$~_, #2:%method_block_arg} + instrumentationBits = 0 + keywordArguments = false + localReturnProfile = false + lock = java.util.concurrent.locks.ReentrantLock@...[Unlocked] + matchingReturnProfile = false + nonMatchingReturnProfile = false + polyglotRef = org.truffleruby.RubyLanguage@... + retryProfile = false + returnID = org.truffleruby.language.control.ReturnID@... + sharedMethodInfo = SharedMethodInfo(sourceSection = SourceSection(source= [1 - 3], index=0, length=23, characters=def foo\n super(42)\nend), staticLexicalScope = :: Object, arity = Arity{preRequired = 0, optional = 0, hasRest = false, postRequired = 0, keywordArguments = [], requiredKeywordArgumentsCount = 0, hasKeywordsRest = false}, originName = foo, blockDepth = 0, parseName = Object#foo, notes = null, argumentDescriptors = []) + sourceSection = SourceSection(source= [1 - 3], index=0, length=23, characters=def foo\n super(42)\nend) + split = HEURISTIC + children: + body = + SequenceNode + attributes: + flags = 12 + children: + body = [ + WriteLocalVariableNode + attributes: + flags = 0 + frameSlot = 0 + children: + valueNode = + ProfileArgumentNodeGen + attributes: + flags = 0 + children: + childNode_ = + ReadSelfNode + attributes: + flags = 0 + SaveMethodBlockNode + attributes: + flags = 0 + slot = 2 + SuperCallNode + attributes: + descriptor = org.truffleruby.language.arguments.EmptyArgumentsDescriptor@... + emptyKeywordsProfile = false + flags = 0 + isSplatted = false + lastArgIsNotHashProfile = false + notEmptyKeywordsProfile = false + notRuby2KeywordsHashProfile = false + children: + arguments = + ReadSuperArgumentsNode + attributes: + flags = 0 + isSplatted = false + children: + arguments = [ + IntegerFixnumLiteralNode + attributes: + flags = 0 + value = 42 + ] + block = + ReadLocalVariableNode + attributes: + flags = 0 + frameSlot = 2 + type = FRAME_LOCAL + ] \ No newline at end of file diff --git a/spec/truffle/parsing/fixtures/method_calls/super/in_method_body_without_explicit_arguments.yaml b/spec/truffle/parsing/fixtures/method_calls/super/in_method_body_without_explicit_arguments.yaml new file mode 100644 index 000000000000..ce9a6d79ed69 --- /dev/null +++ b/spec/truffle/parsing/fixtures/method_calls/super/in_method_body_without_explicit_arguments.yaml @@ -0,0 +1,83 @@ +subject: "Method call" +description: super / in a method body without explicit arguments +notes: > + Is represented by SuperCallNode and ReadZSuperArgumentsNode nodes +focused_on_node: "org.truffleruby.language.methods.LiteralMethodDefinitionNode" +ruby: | + def foo + super + end +ast: | + LiteralMethodDefinitionNode + attributes: + callTargetSupplier = org.truffleruby.language.methods.CachedLazyCallTargetSupplier@... + flags = 1 + isDefSingleton = false + name = "foo" + sharedMethodInfo = SharedMethodInfo(sourceSection = SourceSection(source= [1 - 3], index=0, length=19, characters=def foo\n super\nend), staticLexicalScope = :: Object, arity = Arity{preRequired = 0, optional = 0, hasRest = false, postRequired = 0, keywordArguments = [], requiredKeywordArgumentsCount = 0, hasKeywordsRest = false}, originName = foo, blockDepth = 0, parseName = Object#foo, notes = null, argumentDescriptors = []) + call targets: + RubyMethodRootNode + attributes: + arityForCheck = Arity{preRequired = 0, optional = 0, hasRest = false, postRequired = 0, keywordArguments = [], requiredKeywordArgumentsCount = 0, hasKeywordsRest = false} + callTarget = Object#foo + checkArityProfile = false + frameDescriptor = FrameDescriptor@...{#0:(self), #1:%$~_, #2:%method_block_arg} + instrumentationBits = 0 + keywordArguments = false + localReturnProfile = false + lock = java.util.concurrent.locks.ReentrantLock@...[Unlocked] + matchingReturnProfile = false + nonMatchingReturnProfile = false + polyglotRef = org.truffleruby.RubyLanguage@... + retryProfile = false + returnID = org.truffleruby.language.control.ReturnID@... + sharedMethodInfo = SharedMethodInfo(sourceSection = SourceSection(source= [1 - 3], index=0, length=19, characters=def foo\n super\nend), staticLexicalScope = :: Object, arity = Arity{preRequired = 0, optional = 0, hasRest = false, postRequired = 0, keywordArguments = [], requiredKeywordArgumentsCount = 0, hasKeywordsRest = false}, originName = foo, blockDepth = 0, parseName = Object#foo, notes = null, argumentDescriptors = []) + sourceSection = SourceSection(source= [1 - 3], index=0, length=19, characters=def foo\n super\nend) + split = HEURISTIC + children: + body = + SequenceNode + attributes: + flags = 12 + children: + body = [ + WriteLocalVariableNode + attributes: + flags = 0 + frameSlot = 0 + children: + valueNode = + ProfileArgumentNodeGen + attributes: + flags = 0 + children: + childNode_ = + ReadSelfNode + attributes: + flags = 0 + SaveMethodBlockNode + attributes: + flags = 0 + slot = 2 + SuperCallNode + attributes: + descriptor = org.truffleruby.language.arguments.EmptyArgumentsDescriptor@... + emptyKeywordsProfile = false + flags = 0 + isSplatted = false + lastArgIsNotHashProfile = false + notEmptyKeywordsProfile = false + notRuby2KeywordsHashProfile = false + children: + arguments = + ReadZSuperArgumentsNode + attributes: + flags = 0 + restArgIndex = -1 + block = + ReadLocalVariableNode + attributes: + flags = 0 + frameSlot = 2 + type = FRAME_LOCAL + ] \ No newline at end of file diff --git a/spec/truffle/parsing/fixtures/method_calls/super/outside_method_body_with_explicit_arguments.yaml b/spec/truffle/parsing/fixtures/method_calls/super/outside_method_body_with_explicit_arguments.yaml new file mode 100644 index 000000000000..488603ed92d1 --- /dev/null +++ b/spec/truffle/parsing/fixtures/method_calls/super/outside_method_body_with_explicit_arguments.yaml @@ -0,0 +1,92 @@ +subject: "Method call" +description: super / outside a method body with explicit arguments +notes: > + Is represented by SuperCallNode and ReadSuperArgumentsNode nodes +focused_on_node: "org.truffleruby.language.methods.BlockDefinitionNode" +ruby: | + -> { super(42) } +ast: | + BlockDefinitionNode + attributes: + breakID = org.truffleruby.language.control.BreakID@... + callTargets = ProcCallTargets(callTargetForProc = null, callTargetForLambda = block in , altCallTargetCompiler = null) + flags = 1 + frameOnStackMarkerSlot = -1 + sharedMethodInfo = SharedMethodInfo(sourceSection = SourceSection(source= [1 - 1], index=0, length=16, characters=-> { super(42) }), staticLexicalScope = :: Object, arity = Arity{preRequired = 0, optional = 0, hasRest = false, postRequired = 0, keywordArguments = [], requiredKeywordArgumentsCount = 0, hasKeywordsRest = false}, originName = block in , blockDepth = 1, parseName = block in , notes = , argumentDescriptors = []) + type = LAMBDA + children: + readSpecialVariableStorageNode = + TruffleKernelNodesFactory$GetSpecialVariableStorageNodeGen + call targets: + RubyLambdaRootNode + attributes: + arityForCheck = Arity{preRequired = 0, optional = 0, hasRest = false, postRequired = 0, keywordArguments = [], requiredKeywordArgumentsCount = 0, hasKeywordsRest = false} + breakID = org.truffleruby.language.control.BreakID@... + callTarget = block in + checkArityProfile = false + frameDescriptor = FrameDescriptor@...{#0:(self)} + instrumentationBits = 0 + keywordArguments = false + localReturnProfile = false + lock = java.util.concurrent.locks.ReentrantLock@...[Unlocked] + matchingBreakProfile = false + matchingReturnProfile = false + nextProfile = false + nonMatchingBreakProfile = false + nonMatchingReturnProfile = false + polyglotRef = org.truffleruby.RubyLanguage@... + redoProfile = false + retryProfile = false + returnID = org.truffleruby.language.control.ReturnID@... + sharedMethodInfo = SharedMethodInfo(sourceSection = SourceSection(source= [1 - 1], index=0, length=16, characters=-> { super(42) }), staticLexicalScope = :: Object, arity = Arity{preRequired = 0, optional = 0, hasRest = false, postRequired = 0, keywordArguments = [], requiredKeywordArgumentsCount = 0, hasKeywordsRest = false}, originName = block in , blockDepth = 1, parseName = block in , notes = , argumentDescriptors = []) + sourceSection = SourceSection(source= [1 - 1], index=0, length=16, characters=-> { super(42) }) + split = HEURISTIC + children: + body = + SequenceNode + attributes: + flags = 12 + children: + body = [ + WriteLocalVariableNode + attributes: + flags = 0 + frameSlot = 0 + children: + valueNode = + ProfileArgumentNodeGen + attributes: + flags = 0 + children: + childNode_ = + ReadSelfNode + attributes: + flags = 0 + SuperCallNode + attributes: + descriptor = org.truffleruby.language.arguments.EmptyArgumentsDescriptor@... + emptyKeywordsProfile = false + flags = 0 + isSplatted = false + lastArgIsNotHashProfile = false + notEmptyKeywordsProfile = false + notRuby2KeywordsHashProfile = false + children: + arguments = + ReadSuperArgumentsNode + attributes: + flags = 0 + isSplatted = false + children: + arguments = [ + IntegerFixnumLiteralNode + attributes: + flags = 0 + value = 42 + ] + block = + NilLiteralNode + attributes: + flags = 0 + isImplicit = true + ] \ No newline at end of file diff --git a/spec/truffle/parsing/fixtures/method_calls/super/outside_method_body_without_explicit_arguments.yaml b/spec/truffle/parsing/fixtures/method_calls/super/outside_method_body_without_explicit_arguments.yaml new file mode 100644 index 000000000000..08410be03d8b --- /dev/null +++ b/spec/truffle/parsing/fixtures/method_calls/super/outside_method_body_without_explicit_arguments.yaml @@ -0,0 +1,72 @@ +subject: "Method call" +description: super / outside a method body without explicit arguments +notes: > + Is represented by a ZSuperOutsideMethodNode node +focused_on_node: "org.truffleruby.language.methods.BlockDefinitionNode" +ruby: | + -> { super } +ast: | + BlockDefinitionNode + attributes: + breakID = org.truffleruby.language.control.BreakID@... + callTargets = ProcCallTargets(callTargetForProc = null, callTargetForLambda = block in , altCallTargetCompiler = null) + flags = 1 + frameOnStackMarkerSlot = -1 + sharedMethodInfo = SharedMethodInfo(sourceSection = SourceSection(source= [1 - 1], index=0, length=12, characters=-> { super }), staticLexicalScope = :: Object, arity = Arity{preRequired = 0, optional = 0, hasRest = false, postRequired = 0, keywordArguments = [], requiredKeywordArgumentsCount = 0, hasKeywordsRest = false}, originName = block in , blockDepth = 1, parseName = block in , notes = , argumentDescriptors = []) + type = LAMBDA + children: + readSpecialVariableStorageNode = + TruffleKernelNodesFactory$GetSpecialVariableStorageNodeGen + call targets: + RubyLambdaRootNode + attributes: + arityForCheck = Arity{preRequired = 0, optional = 0, hasRest = false, postRequired = 0, keywordArguments = [], requiredKeywordArgumentsCount = 0, hasKeywordsRest = false} + breakID = org.truffleruby.language.control.BreakID@... + callTarget = block in + checkArityProfile = false + frameDescriptor = FrameDescriptor@...{#0:(self)} + instrumentationBits = 0 + keywordArguments = false + localReturnProfile = false + lock = java.util.concurrent.locks.ReentrantLock@...[Unlocked] + matchingBreakProfile = false + matchingReturnProfile = false + nextProfile = false + nonMatchingBreakProfile = false + nonMatchingReturnProfile = false + polyglotRef = org.truffleruby.RubyLanguage@... + redoProfile = false + retryProfile = false + returnID = org.truffleruby.language.control.ReturnID@... + sharedMethodInfo = SharedMethodInfo(sourceSection = SourceSection(source= [1 - 1], index=0, length=12, characters=-> { super }), staticLexicalScope = :: Object, arity = Arity{preRequired = 0, optional = 0, hasRest = false, postRequired = 0, keywordArguments = [], requiredKeywordArgumentsCount = 0, hasKeywordsRest = false}, originName = block in , blockDepth = 1, parseName = block in , notes = , argumentDescriptors = []) + sourceSection = SourceSection(source= [1 - 1], index=0, length=12, characters=-> { super }) + split = HEURISTIC + children: + body = + SequenceNode + attributes: + flags = 12 + children: + body = [ + WriteLocalVariableNode + attributes: + flags = 0 + frameSlot = 0 + children: + valueNode = + ProfileArgumentNodeGen + attributes: + flags = 0 + children: + childNode_ = + ReadSelfNode + attributes: + flags = 0 + ZSuperOutsideMethodNode + attributes: + flags = 0 + insideDefineMethod = false + children: + lookupSuperMethodNode = + LookupSuperMethodNodeGen + ] \ No newline at end of file diff --git a/spec/truffle/parsing/fixtures/method_calls/with_explicit_self_receiver.yaml b/spec/truffle/parsing/fixtures/method_calls/with_explicit_self_receiver.yaml new file mode 100644 index 000000000000..8d59c0eda37a --- /dev/null +++ b/spec/truffle/parsing/fixtures/method_calls/with_explicit_self_receiver.yaml @@ -0,0 +1,27 @@ +subject: "Method call" +description: Method call with explicit `self` receiver (`self.foo`) +notes: > + Explicit receiver is represented with `SelfNode` node. +focused_on_node: "org.truffleruby.language.dispatch.RubyCallNode" +ruby: | + self.foo +ast: | + RubyCallNode + attributes: + descriptor = org.truffleruby.language.arguments.EmptyArgumentsDescriptor@... + dispatchConfig = PRIVATE + emptyKeywordsProfile = false + flags = 1 + isAttrAssign = false + isSafeNavigation = false + isSplatted = false + isVCall = false + lastArgIsNotHashProfile = false + methodName = "foo" + notEmptyKeywordsProfile = false + notRuby2KeywordsHashProfile = false + children: + receiver = + SelfNode + attributes: + flags = 0 \ No newline at end of file diff --git a/spec/truffle/parsing/fixtures/method_calls/with_implicit_receiver.yaml b/spec/truffle/parsing/fixtures/method_calls/with_implicit_receiver.yaml index f283b43886b6..20474e9ad932 100644 --- a/spec/truffle/parsing/fixtures/method_calls/with_implicit_receiver.yaml +++ b/spec/truffle/parsing/fixtures/method_calls/with_implicit_receiver.yaml @@ -20,7 +20,6 @@ ast: | methodName = "foo" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: receiver = SelfNode diff --git a/spec/truffle/parsing/fixtures/method_calls/with_safe_navigation.yaml b/spec/truffle/parsing/fixtures/method_calls/with_safe_navigation.yaml index 55b8d885d899..f22e9a97fd7e 100644 --- a/spec/truffle/parsing/fixtures/method_calls/with_safe_navigation.yaml +++ b/spec/truffle/parsing/fixtures/method_calls/with_safe_navigation.yaml @@ -20,7 +20,6 @@ ast: | methodName = "foobar" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: receiver = IntegerFixnumLiteralNode diff --git a/spec/truffle/parsing/fixtures/method_calls/without_arguments.yaml b/spec/truffle/parsing/fixtures/method_calls/without_arguments.yaml index 7211b9712bf5..6bb1e44bd1aa 100644 --- a/spec/truffle/parsing/fixtures/method_calls/without_arguments.yaml +++ b/spec/truffle/parsing/fixtures/method_calls/without_arguments.yaml @@ -18,7 +18,6 @@ ast: | methodName = "foo" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: receiver = SelfNode diff --git a/spec/truffle/parsing/fixtures/method_calls/without_arguments_with_paranthesis.yaml b/spec/truffle/parsing/fixtures/method_calls/without_arguments_with_paranthesis.yaml new file mode 100644 index 000000000000..a4c8d42b0d55 --- /dev/null +++ b/spec/truffle/parsing/fixtures/method_calls/without_arguments_with_paranthesis.yaml @@ -0,0 +1,25 @@ +subject: "Method call" +description: "Without arguments but with parentheses" +focused_on_node: "org.truffleruby.language.dispatch.RubyCallNode" +ruby: | + foo() +ast: | + RubyCallNode + attributes: + descriptor = org.truffleruby.language.arguments.EmptyArgumentsDescriptor@... + dispatchConfig = PRIVATE + emptyKeywordsProfile = false + flags = 1 + isAttrAssign = false + isSafeNavigation = false + isSplatted = false + isVCall = false + lastArgIsNotHashProfile = false + methodName = "foo" + notEmptyKeywordsProfile = false + notRuby2KeywordsHashProfile = false + children: + receiver = + SelfNode + attributes: + flags = 0 \ No newline at end of file diff --git a/spec/truffle/parsing/fixtures/next/with_multiple_arguments.yaml b/spec/truffle/parsing/fixtures/next/with_multiple_arguments.yaml index 422c4978f1c4..6225a623743b 100644 --- a/spec/truffle/parsing/fixtures/next/with_multiple_arguments.yaml +++ b/spec/truffle/parsing/fixtures/next/with_multiple_arguments.yaml @@ -1,5 +1,5 @@ subject: "Next" -description: "next operator with multiple returned values" +description: "with multiple returned values" notes: > Operator is represented by NextNode. Returned values are represented by an ArrayLiteralNode$UninitialisedArrayLiteralNode. diff --git a/spec/truffle/parsing/fixtures/next/with_argument.yaml b/spec/truffle/parsing/fixtures/next/with_single_argument.yaml similarity index 90% rename from spec/truffle/parsing/fixtures/next/with_argument.yaml rename to spec/truffle/parsing/fixtures/next/with_single_argument.yaml index 38591432634f..1865dc9b23ff 100644 --- a/spec/truffle/parsing/fixtures/next/with_argument.yaml +++ b/spec/truffle/parsing/fixtures/next/with_single_argument.yaml @@ -1,5 +1,5 @@ subject: "Next" -description: "next operator with returning value" +description: "with single returned value" notes: > Operator is represented by NextNode. Returned value is represented by a child node (IntegerFixnumLiteralNode in the example below). diff --git a/spec/truffle/parsing/fixtures/next/with_splat_operator.yaml b/spec/truffle/parsing/fixtures/next/with_splat_operator.yaml index 17c21ceb761a..e374f1ff7cae 100644 --- a/spec/truffle/parsing/fixtures/next/with_splat_operator.yaml +++ b/spec/truffle/parsing/fixtures/next/with_splat_operator.yaml @@ -1,5 +1,5 @@ subject: "Next" -description: "next operator with splat operator (next *a)" +description: "with splat operator (next *a)" notes: > Argument is represented by SplatCastNodeGen node focused_on_node: "org.truffleruby.language.control.NextNode" diff --git a/spec/truffle/parsing/fixtures/next/with_splat_operator_and_following_argument.yaml b/spec/truffle/parsing/fixtures/next/with_splat_operator_and_following_argument.yaml new file mode 100644 index 000000000000..8d0b77f5072b --- /dev/null +++ b/spec/truffle/parsing/fixtures/next/with_splat_operator_and_following_argument.yaml @@ -0,0 +1,64 @@ +subject: "Next" +description: "with splat operator and following argument (next *a, b)" +notes: > + Arguments are represented with combination of: + - ArrayConcatNode + - SplatCastNodeGen + - ArrayLiteralNode$UninitialisedArrayLiteralNode + nodes +yarp_specific: true # Simplified AST and replaced ArrayAppendOneNodeGen by concatenating with array of one element +focused_on_node: "org.truffleruby.language.control.NextNode" +ruby: | + while (true) + next *foo, 42 + end +ast: | + NextNode + attributes: + flags = 1 + children: + child = + ArrayConcatNode + attributes: + flags = 0 + children: + children = [ + SplatCastNodeGen + attributes: + conversionMethod = :to_a + copy = true + flags = 0 + nilBehavior = CONVERT + children: + childNode_ = + RubyCallNode + attributes: + descriptor = org.truffleruby.language.arguments.EmptyArgumentsDescriptor@... + dispatchConfig = PRIVATE + emptyKeywordsProfile = false + flags = 0 + isAttrAssign = false + isSafeNavigation = false + isSplatted = false + isVCall = true + lastArgIsNotHashProfile = false + methodName = "foo" + notEmptyKeywordsProfile = false + notRuby2KeywordsHashProfile = false + children: + receiver = + SelfNode + attributes: + flags = 0 + ArrayLiteralNode$UninitialisedArrayLiteralNode + attributes: + flags = 0 + language = org.truffleruby.RubyLanguage@... + children: + values = [ + IntegerFixnumLiteralNode + attributes: + flags = 0 + value = 42 + ] + ] \ No newline at end of file diff --git a/spec/truffle/parsing/fixtures/next/with_splat_operator_and_following_arguments.yaml b/spec/truffle/parsing/fixtures/next/with_splat_operator_and_following_arguments.yaml new file mode 100644 index 000000000000..529bf4a2fcdd --- /dev/null +++ b/spec/truffle/parsing/fixtures/next/with_splat_operator_and_following_arguments.yaml @@ -0,0 +1,69 @@ +subject: "Next" +description: "with splat operator and following arguments (next *a, b, ...)" +notes: > + Arguments are represented with combination of: + - ArrayConcatNode + - SplatCastNodeGen + - ArrayLiteralNode$UninitialisedArrayLiteralNode + nodes +yarp_specific: true # Removed excessive SplatCastNodeGen: + # SplatCastNodeGen(UninitialisedArrayLiteralNode) -> UninitialisedArrayLiteralNode +focused_on_node: "org.truffleruby.language.control.NextNode" +ruby: | + while (true) + next *foo, 42, 100500 + end +ast: | + NextNode + attributes: + flags = 1 + children: + child = + ArrayConcatNode + attributes: + flags = 0 + children: + children = [ + SplatCastNodeGen + attributes: + conversionMethod = :to_a + copy = true + flags = 0 + nilBehavior = CONVERT + children: + childNode_ = + RubyCallNode + attributes: + descriptor = org.truffleruby.language.arguments.EmptyArgumentsDescriptor@... + dispatchConfig = PRIVATE + emptyKeywordsProfile = false + flags = 0 + isAttrAssign = false + isSafeNavigation = false + isSplatted = false + isVCall = true + lastArgIsNotHashProfile = false + methodName = "foo" + notEmptyKeywordsProfile = false + notRuby2KeywordsHashProfile = false + children: + receiver = + SelfNode + attributes: + flags = 0 + ArrayLiteralNode$UninitialisedArrayLiteralNode + attributes: + flags = 0 + language = org.truffleruby.RubyLanguage@... + children: + values = [ + IntegerFixnumLiteralNode + attributes: + flags = 0 + value = 42 + IntegerFixnumLiteralNode + attributes: + flags = 0 + value = 100500 + ] + ] \ No newline at end of file diff --git a/spec/truffle/parsing/fixtures/next/with_splat_operator_and_preceding_and_following_argument.yaml b/spec/truffle/parsing/fixtures/next/with_splat_operator_and_preceding_and_following_argument.yaml new file mode 100644 index 000000000000..1158472b6a1c --- /dev/null +++ b/spec/truffle/parsing/fixtures/next/with_splat_operator_and_preceding_and_following_argument.yaml @@ -0,0 +1,75 @@ +subject: "Next" +description: "with splat operator and preceding and following argument (next a, *b, c)" +notes: > + Arguments are represented with combination of: + - ArrayConcatNode + - SplatCastNodeGen + - ArrayLiteralNode$UninitialisedArrayLiteralNode + nodes +yarp_specific: true # Simplified AST and replaced ArrayAppendOneNodeGen by concatenating with array of one element +focused_on_node: "org.truffleruby.language.control.NextNode" +ruby: | + while (true) + next 42, *foo, 100500 + end +ast: | + NextNode + attributes: + flags = 1 + children: + child = + ArrayConcatNode + attributes: + flags = 0 + children: + children = [ + ArrayLiteralNode$UninitialisedArrayLiteralNode + attributes: + flags = 0 + language = org.truffleruby.RubyLanguage@... + children: + values = [ + IntegerFixnumLiteralNode + attributes: + flags = 0 + value = 42 + ] + SplatCastNodeGen + attributes: + conversionMethod = :to_a + copy = true + flags = 0 + nilBehavior = CONVERT + children: + childNode_ = + RubyCallNode + attributes: + descriptor = org.truffleruby.language.arguments.EmptyArgumentsDescriptor@... + dispatchConfig = PRIVATE + emptyKeywordsProfile = false + flags = 0 + isAttrAssign = false + isSafeNavigation = false + isSplatted = false + isVCall = true + lastArgIsNotHashProfile = false + methodName = "foo" + notEmptyKeywordsProfile = false + notRuby2KeywordsHashProfile = false + children: + receiver = + SelfNode + attributes: + flags = 0 + ArrayLiteralNode$UninitialisedArrayLiteralNode + attributes: + flags = 0 + language = org.truffleruby.RubyLanguage@... + children: + values = [ + IntegerFixnumLiteralNode + attributes: + flags = 0 + value = 100500 + ] + ] \ No newline at end of file diff --git a/spec/truffle/parsing/fixtures/next/with_splat_operator_and_preceding_and_following_arguments.yaml b/spec/truffle/parsing/fixtures/next/with_splat_operator_and_preceding_and_following_arguments.yaml new file mode 100644 index 000000000000..6cb0bdf3511c --- /dev/null +++ b/spec/truffle/parsing/fixtures/next/with_splat_operator_and_preceding_and_following_arguments.yaml @@ -0,0 +1,86 @@ +subject: "Next" +description: "with splat operator and preceding and following arguments (next a, ..., *b, c, ...)" +notes: > + Arguments are represented with combination of: + - ArrayConcatNode + - SplatCastNodeGen + - ArrayLiteralNode$UninitialisedArrayLiteralNode + nodes +yarp_specific: true # Removed excessive SplatCastNodeGen: + # SplatCastNodeGen(UninitialisedArrayLiteralNode) -> UninitialisedArrayLiteralNode +focused_on_node: "org.truffleruby.language.control.NextNode" +ruby: | + while (true) + next 42, 100500, *foo, "bar", "baz" + end +ast: | + NextNode + attributes: + flags = 1 + children: + child = + ArrayConcatNode + attributes: + flags = 0 + children: + children = [ + ArrayLiteralNode$UninitialisedArrayLiteralNode + attributes: + flags = 0 + language = org.truffleruby.RubyLanguage@... + children: + values = [ + IntegerFixnumLiteralNode + attributes: + flags = 0 + value = 42 + IntegerFixnumLiteralNode + attributes: + flags = 0 + value = 100500 + ] + SplatCastNodeGen + attributes: + conversionMethod = :to_a + copy = true + flags = 0 + nilBehavior = CONVERT + children: + childNode_ = + RubyCallNode + attributes: + descriptor = org.truffleruby.language.arguments.EmptyArgumentsDescriptor@... + dispatchConfig = PRIVATE + emptyKeywordsProfile = false + flags = 0 + isAttrAssign = false + isSafeNavigation = false + isSplatted = false + isVCall = true + lastArgIsNotHashProfile = false + methodName = "foo" + notEmptyKeywordsProfile = false + notRuby2KeywordsHashProfile = false + children: + receiver = + SelfNode + attributes: + flags = 0 + ArrayLiteralNode$UninitialisedArrayLiteralNode + attributes: + flags = 0 + language = org.truffleruby.RubyLanguage@... + children: + values = [ + StringLiteralNode + attributes: + encoding = UTF-8 + flags = 0 + tstring = bar + StringLiteralNode + attributes: + encoding = UTF-8 + flags = 0 + tstring = baz + ] + ] \ No newline at end of file diff --git a/spec/truffle/parsing/fixtures/next/with_splat_operator_and_preceding_argument.yaml b/spec/truffle/parsing/fixtures/next/with_splat_operator_and_preceding_argument.yaml new file mode 100644 index 000000000000..15b97705e926 --- /dev/null +++ b/spec/truffle/parsing/fixtures/next/with_splat_operator_and_preceding_argument.yaml @@ -0,0 +1,63 @@ +subject: "Next" +description: "with splat operator and preceding argument (next a, *b)" +notes: > + Arguments are represented with combination of: + - ArrayConcatNode + - SplatCastNodeGen + - ArrayLiteralNode$UninitialisedArrayLiteralNode + nodes +focused_on_node: "org.truffleruby.language.control.NextNode" +ruby: | + while (true) + next 42, *foo + end +ast: | + NextNode + attributes: + flags = 1 + children: + child = + ArrayConcatNode + attributes: + flags = 0 + children: + children = [ + ArrayLiteralNode$UninitialisedArrayLiteralNode + attributes: + flags = 0 + language = org.truffleruby.RubyLanguage@... + children: + values = [ + IntegerFixnumLiteralNode + attributes: + flags = 0 + value = 42 + ] + SplatCastNodeGen + attributes: + conversionMethod = :to_a + copy = true + flags = 0 + nilBehavior = CONVERT + children: + childNode_ = + RubyCallNode + attributes: + descriptor = org.truffleruby.language.arguments.EmptyArgumentsDescriptor@... + dispatchConfig = PRIVATE + emptyKeywordsProfile = false + flags = 0 + isAttrAssign = false + isSafeNavigation = false + isSplatted = false + isVCall = true + lastArgIsNotHashProfile = false + methodName = "foo" + notEmptyKeywordsProfile = false + notRuby2KeywordsHashProfile = false + children: + receiver = + SelfNode + attributes: + flags = 0 + ] \ No newline at end of file diff --git a/spec/truffle/parsing/fixtures/next/with_splat_operator_and_preceding_arguments.yaml b/spec/truffle/parsing/fixtures/next/with_splat_operator_and_preceding_arguments.yaml new file mode 100644 index 000000000000..e1a7b70c5040 --- /dev/null +++ b/spec/truffle/parsing/fixtures/next/with_splat_operator_and_preceding_arguments.yaml @@ -0,0 +1,67 @@ +subject: "Next" +description: "with splat operator and preceding arguments (next a, ..., *b)" +notes: > + Arguments are represented with combination of: + - ArrayConcatNode + - SplatCastNodeGen + - ArrayLiteralNode$UninitialisedArrayLiteralNode + nodes +focused_on_node: "org.truffleruby.language.control.NextNode" +ruby: | + while (true) + next 42, 100500, *foo + end +ast: | + NextNode + attributes: + flags = 1 + children: + child = + ArrayConcatNode + attributes: + flags = 0 + children: + children = [ + ArrayLiteralNode$UninitialisedArrayLiteralNode + attributes: + flags = 0 + language = org.truffleruby.RubyLanguage@... + children: + values = [ + IntegerFixnumLiteralNode + attributes: + flags = 0 + value = 42 + IntegerFixnumLiteralNode + attributes: + flags = 0 + value = 100500 + ] + SplatCastNodeGen + attributes: + conversionMethod = :to_a + copy = true + flags = 0 + nilBehavior = CONVERT + children: + childNode_ = + RubyCallNode + attributes: + descriptor = org.truffleruby.language.arguments.EmptyArgumentsDescriptor@... + dispatchConfig = PRIVATE + emptyKeywordsProfile = false + flags = 0 + isAttrAssign = false + isSafeNavigation = false + isSplatted = false + isVCall = true + lastArgIsNotHashProfile = false + methodName = "foo" + notEmptyKeywordsProfile = false + notRuby2KeywordsHashProfile = false + children: + receiver = + SelfNode + attributes: + flags = 0 + ] \ No newline at end of file diff --git a/spec/truffle/parsing/fixtures/next/without_argument.yaml b/spec/truffle/parsing/fixtures/next/without_argument.yaml index aefa7a9afc2d..92e88fa77360 100644 --- a/spec/truffle/parsing/fixtures/next/without_argument.yaml +++ b/spec/truffle/parsing/fixtures/next/without_argument.yaml @@ -1,5 +1,5 @@ subject: "Next" -description: "next operator without returning value" +description: "without returned value" notes: > Operator is represented by NextNode. Returned value is represented by NilLiteralNode. diff --git a/spec/truffle/parsing/fixtures/operators/&&=/attribute_assignment.yaml b/spec/truffle/parsing/fixtures/operators/&&=/attribute_assignment.yaml index 1e5d7bfe3101..a7a8e6fa82ec 100644 --- a/spec/truffle/parsing/fixtures/operators/&&=/attribute_assignment.yaml +++ b/spec/truffle/parsing/fixtures/operators/&&=/attribute_assignment.yaml @@ -42,7 +42,6 @@ ast: | methodName = "a" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: receiver = SelfNode @@ -67,7 +66,6 @@ ast: | methodName = "foo" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: receiver = ReadLocalVariableNode @@ -90,7 +88,6 @@ ast: | methodName = "foo=" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: arguments = [ IntegerFixnumLiteralNode diff --git a/spec/truffle/parsing/fixtures/operators/&&=/reference_assignment.yaml b/spec/truffle/parsing/fixtures/operators/&&=/reference_assignment.yaml index ede2780d3673..116fd91911f5 100644 --- a/spec/truffle/parsing/fixtures/operators/&&=/reference_assignment.yaml +++ b/spec/truffle/parsing/fixtures/operators/&&=/reference_assignment.yaml @@ -62,7 +62,6 @@ ast: | methodName = "foo" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: receiver = SelfNode diff --git a/spec/truffle/parsing/fixtures/operators/&&=/reference_assignment_with_multiple_indexes.yaml b/spec/truffle/parsing/fixtures/operators/&&=/reference_assignment_with_multiple_indexes.yaml index b1242fbf1b3a..8827b0e51d68 100644 --- a/spec/truffle/parsing/fixtures/operators/&&=/reference_assignment_with_multiple_indexes.yaml +++ b/spec/truffle/parsing/fixtures/operators/&&=/reference_assignment_with_multiple_indexes.yaml @@ -84,7 +84,6 @@ ast: | methodName = "foo" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: receiver = SelfNode @@ -109,7 +108,6 @@ ast: | methodName = "[]" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: arguments = [ ReadLocalVariableNode @@ -149,7 +147,6 @@ ast: | methodName = "[]=" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: arguments = [ ReadLocalVariableNode diff --git a/spec/truffle/parsing/fixtures/operators/flip_flop_operator/in_block.yaml b/spec/truffle/parsing/fixtures/operators/flip_flop_operator/in_block.yaml index d1f535623500..6dd1e5dafb96 100644 --- a/spec/truffle/parsing/fixtures/operators/flip_flop_operator/in_block.yaml +++ b/spec/truffle/parsing/fixtures/operators/flip_flop_operator/in_block.yaml @@ -94,7 +94,6 @@ ast: | methodName = "each" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: block = BlockDefinitionNode diff --git a/spec/truffle/parsing/fixtures/operators/flip_flop_operator/in_class.yaml b/spec/truffle/parsing/fixtures/operators/flip_flop_operator/in_class.yaml index 32a252ae000f..13dafc443356 100644 --- a/spec/truffle/parsing/fixtures/operators/flip_flop_operator/in_class.yaml +++ b/spec/truffle/parsing/fixtures/operators/flip_flop_operator/in_class.yaml @@ -99,13 +99,13 @@ ast: | begin = ReadLocalVariableNode attributes: - flags = 1 + flags = 0 frameSlot = 2 type = FRAME_LOCAL end = ReadLocalVariableNode attributes: - flags = 1 + flags = 0 frameSlot = 2 type = FRAME_LOCAL thenBody = diff --git a/spec/truffle/parsing/fixtures/operators/flip_flop_operator/in_module.yaml b/spec/truffle/parsing/fixtures/operators/flip_flop_operator/in_module.yaml index a37e0964474c..41718adf84c2 100644 --- a/spec/truffle/parsing/fixtures/operators/flip_flop_operator/in_module.yaml +++ b/spec/truffle/parsing/fixtures/operators/flip_flop_operator/in_module.yaml @@ -99,13 +99,13 @@ ast: | begin = ReadLocalVariableNode attributes: - flags = 1 + flags = 0 frameSlot = 2 type = FRAME_LOCAL end = ReadLocalVariableNode attributes: - flags = 1 + flags = 0 frameSlot = 2 type = FRAME_LOCAL thenBody = diff --git a/spec/truffle/parsing/fixtures/operators/flip_flop_operator/operator.yaml b/spec/truffle/parsing/fixtures/operators/flip_flop_operator/operator.yaml index a9437cd5a290..fdd6a20038e1 100644 --- a/spec/truffle/parsing/fixtures/operators/flip_flop_operator/operator.yaml +++ b/spec/truffle/parsing/fixtures/operators/flip_flop_operator/operator.yaml @@ -19,12 +19,12 @@ ast: | begin = ReadLocalVariableNode attributes: - flags = 1 + flags = 0 frameSlot = 2 type = FRAME_LOCAL end = ReadLocalVariableNode attributes: - flags = 1 + flags = 0 frameSlot = 2 type = FRAME_LOCAL \ No newline at end of file diff --git a/spec/truffle/parsing/fixtures/operators/match/=~.yaml b/spec/truffle/parsing/fixtures/operators/match/=~.yaml index 352b99452bea..03301079db0d 100644 --- a/spec/truffle/parsing/fixtures/operators/match/=~.yaml +++ b/spec/truffle/parsing/fixtures/operators/match/=~.yaml @@ -20,7 +20,6 @@ ast: | methodName = "=~" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: arguments = [ RubyCallNode @@ -37,7 +36,6 @@ ast: | methodName = "bar" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: receiver = SelfNode @@ -59,7 +57,6 @@ ast: | methodName = "foo" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: receiver = SelfNode diff --git a/spec/truffle/parsing/fixtures/operators/match/=~_with_regexp_as_argument.yaml b/spec/truffle/parsing/fixtures/operators/match/=~_with_regexp_as_argument.yaml index 6533cb62cda3..35056cc1d02f 100644 --- a/spec/truffle/parsing/fixtures/operators/match/=~_with_regexp_as_argument.yaml +++ b/spec/truffle/parsing/fixtures/operators/match/=~_with_regexp_as_argument.yaml @@ -20,7 +20,6 @@ ast: | methodName = "=~" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: arguments = [ ObjectLiteralNode @@ -43,7 +42,6 @@ ast: | methodName = "foo" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: receiver = SelfNode diff --git a/spec/truffle/parsing/fixtures/operators/match/=~_with_regexp_as_receiver.yaml b/spec/truffle/parsing/fixtures/operators/match/=~_with_regexp_as_receiver.yaml index 4df6aa5f79b1..abe6b55e0a43 100644 --- a/spec/truffle/parsing/fixtures/operators/match/=~_with_regexp_as_receiver.yaml +++ b/spec/truffle/parsing/fixtures/operators/match/=~_with_regexp_as_receiver.yaml @@ -20,7 +20,6 @@ ast: | methodName = "=~" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: arguments = [ RubyCallNode @@ -37,7 +36,6 @@ ast: | methodName = "bar" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: receiver = SelfNode diff --git a/spec/truffle/parsing/fixtures/operators/match/=~_with_regexp_without_interpolation_with_named_capture_groups_as_receiver.yaml b/spec/truffle/parsing/fixtures/operators/match/=~_with_regexp_without_interpolation_with_named_capture_groups_as_receiver.yaml index 4fc9359993e3..cc577fdfa668 100644 --- a/spec/truffle/parsing/fixtures/operators/match/=~_with_regexp_without_interpolation_with_named_capture_groups_as_receiver.yaml +++ b/spec/truffle/parsing/fixtures/operators/match/=~_with_regexp_without_interpolation_with_named_capture_groups_as_receiver.yaml @@ -26,7 +26,6 @@ ast: | methodName = "=~" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: arguments = [ StringLiteralNode diff --git a/spec/truffle/parsing/fixtures/operators/multi_assignments/when_attribute_assignment.yaml b/spec/truffle/parsing/fixtures/operators/multi_assignments/when_attribute_assignment.yaml index 24462335fcd0..b3b7dfe46335 100644 --- a/spec/truffle/parsing/fixtures/operators/multi_assignments/when_attribute_assignment.yaml +++ b/spec/truffle/parsing/fixtures/operators/multi_assignments/when_attribute_assignment.yaml @@ -25,7 +25,6 @@ ast: | methodName = "attribute=" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: arguments = [ NilLiteralNode @@ -48,7 +47,6 @@ ast: | methodName = "a" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: receiver = SelfNode diff --git a/spec/truffle/parsing/fixtures/operators/||=/attribute_assignment.yaml b/spec/truffle/parsing/fixtures/operators/||=/attribute_assignment.yaml index 86dcb7eb9ee0..4d6867291f6a 100644 --- a/spec/truffle/parsing/fixtures/operators/||=/attribute_assignment.yaml +++ b/spec/truffle/parsing/fixtures/operators/||=/attribute_assignment.yaml @@ -42,7 +42,6 @@ ast: | methodName = "a" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: receiver = SelfNode @@ -67,7 +66,6 @@ ast: | methodName = "foo" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: receiver = ReadLocalVariableNode @@ -90,7 +88,6 @@ ast: | methodName = "foo=" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: arguments = [ IntegerFixnumLiteralNode diff --git a/spec/truffle/parsing/fixtures/operators/||=/reference_assignment.yaml b/spec/truffle/parsing/fixtures/operators/||=/reference_assignment.yaml index b8d12791f97d..9c6572afad22 100644 --- a/spec/truffle/parsing/fixtures/operators/||=/reference_assignment.yaml +++ b/spec/truffle/parsing/fixtures/operators/||=/reference_assignment.yaml @@ -62,7 +62,6 @@ ast: | methodName = "foo" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: receiver = SelfNode diff --git a/spec/truffle/parsing/fixtures/operators/||=/reference_assignment_with_multiple_indexes.yaml b/spec/truffle/parsing/fixtures/operators/||=/reference_assignment_with_multiple_indexes.yaml index b15f08dd5e1b..63e8164e663f 100644 --- a/spec/truffle/parsing/fixtures/operators/||=/reference_assignment_with_multiple_indexes.yaml +++ b/spec/truffle/parsing/fixtures/operators/||=/reference_assignment_with_multiple_indexes.yaml @@ -84,7 +84,6 @@ ast: | methodName = "foo" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: receiver = SelfNode @@ -109,7 +108,6 @@ ast: | methodName = "[]" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: arguments = [ ReadLocalVariableNode @@ -149,7 +147,6 @@ ast: | methodName = "[]=" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: arguments = [ ReadLocalVariableNode diff --git a/spec/truffle/parsing/fixtures/rational_numbers/with_big_integer_value.yaml b/spec/truffle/parsing/fixtures/rational_numbers/with_big_integer_value.yaml index b008e47b1927..43adb2d00216 100644 --- a/spec/truffle/parsing/fixtures/rational_numbers/with_big_integer_value.yaml +++ b/spec/truffle/parsing/fixtures/rational_numbers/with_big_integer_value.yaml @@ -20,7 +20,6 @@ ast: | methodName = "convert" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: arguments = [ ObjectLiteralNode diff --git a/spec/truffle/parsing/fixtures/rational_numbers/with_float_point_value.yaml b/spec/truffle/parsing/fixtures/rational_numbers/with_float_point_value.yaml index 425d40cd188b..caa17e52d45e 100644 --- a/spec/truffle/parsing/fixtures/rational_numbers/with_float_point_value.yaml +++ b/spec/truffle/parsing/fixtures/rational_numbers/with_float_point_value.yaml @@ -21,7 +21,6 @@ ast: | methodName = "convert" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: arguments = [ IntegerFixnumLiteralNode diff --git a/spec/truffle/parsing/fixtures/rational_numbers/with_integer_value.yaml b/spec/truffle/parsing/fixtures/rational_numbers/with_integer_value.yaml index 29702dc37e38..06b09a81baad 100644 --- a/spec/truffle/parsing/fixtures/rational_numbers/with_integer_value.yaml +++ b/spec/truffle/parsing/fixtures/rational_numbers/with_integer_value.yaml @@ -21,7 +21,6 @@ ast: | methodName = "convert" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: arguments = [ IntegerFixnumLiteralNode diff --git a/spec/truffle/parsing/fixtures/rational_numbers/with_negative_value.yaml b/spec/truffle/parsing/fixtures/rational_numbers/with_negative_value.yaml index c7b303432bef..b2c6c101773d 100644 --- a/spec/truffle/parsing/fixtures/rational_numbers/with_negative_value.yaml +++ b/spec/truffle/parsing/fixtures/rational_numbers/with_negative_value.yaml @@ -21,7 +21,6 @@ ast: | methodName = "convert" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: arguments = [ IntegerFixnumLiteralNode diff --git a/spec/truffle/parsing/fixtures/regexps/in_boolean_context.yaml b/spec/truffle/parsing/fixtures/regexps/in_boolean_context.yaml index 4beac8db7b58..5fde857b7801 100644 --- a/spec/truffle/parsing/fixtures/regexps/in_boolean_context.yaml +++ b/spec/truffle/parsing/fixtures/regexps/in_boolean_context.yaml @@ -21,7 +21,6 @@ ast: | methodName = "=~" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: arguments = [ ReadGlobalVariableNodeGen diff --git a/spec/truffle/parsing/fixtures/return/in_block_in_class_body.yaml b/spec/truffle/parsing/fixtures/return/in_block_in_class_body.yaml index 86f6813589e6..0afa503c4bfb 100644 --- a/spec/truffle/parsing/fixtures/return/in_block_in_class_body.yaml +++ b/spec/truffle/parsing/fixtures/return/in_block_in_class_body.yaml @@ -93,7 +93,6 @@ ast: | methodName = "times" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: block = BlockDefinitionNode diff --git a/spec/truffle/parsing/fixtures/return/in_block_in_module_body.yaml b/spec/truffle/parsing/fixtures/return/in_block_in_module_body.yaml index efa12de9bb3b..42fa25c78f18 100644 --- a/spec/truffle/parsing/fixtures/return/in_block_in_module_body.yaml +++ b/spec/truffle/parsing/fixtures/return/in_block_in_module_body.yaml @@ -93,7 +93,6 @@ ast: | methodName = "times" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: block = BlockDefinitionNode diff --git a/spec/truffle/parsing/fixtures/yield/with_arguments.yaml b/spec/truffle/parsing/fixtures/yield/with_arguments.yaml index 7649584c794f..b570f12ee814 100644 --- a/spec/truffle/parsing/fixtures/yield/with_arguments.yaml +++ b/spec/truffle/parsing/fixtures/yield/with_arguments.yaml @@ -68,7 +68,6 @@ ast: | lastArgIsNotHashProfile = false notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: arguments = [ IntegerFixnumLiteralNode diff --git a/spec/truffle/parsing/fixtures/yield/with_keyword_arguments.yaml b/spec/truffle/parsing/fixtures/yield/with_keyword_arguments.yaml index f3313220e694..859d088052a0 100644 --- a/spec/truffle/parsing/fixtures/yield/with_keyword_arguments.yaml +++ b/spec/truffle/parsing/fixtures/yield/with_keyword_arguments.yaml @@ -72,7 +72,6 @@ ast: | lastArgIsNotHashProfile = false notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: arguments = [ PackedHashStoreLibraryFactory$SmallHashLiteralNodeGen diff --git a/spec/truffle/parsing/fixtures/yield/with_only_double_splat_operator.yaml b/spec/truffle/parsing/fixtures/yield/with_only_double_splat_operator.yaml index 623e83866b87..c4c1d89708dd 100644 --- a/spec/truffle/parsing/fixtures/yield/with_only_double_splat_operator.yaml +++ b/spec/truffle/parsing/fixtures/yield/with_only_double_splat_operator.yaml @@ -70,7 +70,6 @@ ast: | lastArgIsNotHashProfile = false notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: arguments = [ HashCastNodeGen$HashCastASTNodeGen @@ -92,7 +91,6 @@ ast: | methodName = "foo" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: receiver = SelfNode diff --git a/spec/truffle/parsing/fixtures/yield/with_only_splat_operator.yaml b/spec/truffle/parsing/fixtures/yield/with_only_splat_operator.yaml index 688ff34ba9b8..dda136eb51f2 100644 --- a/spec/truffle/parsing/fixtures/yield/with_only_splat_operator.yaml +++ b/spec/truffle/parsing/fixtures/yield/with_only_splat_operator.yaml @@ -71,7 +71,6 @@ ast: | lastArgIsNotHashProfile = false notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: arguments = [ SplatCastNodeGen @@ -96,7 +95,6 @@ ast: | methodName = "foo" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: receiver = SelfNode diff --git a/spec/truffle/parsing/fixtures/yield/with_splat_operator_and_multiple_following_arguments.yaml b/spec/truffle/parsing/fixtures/yield/with_splat_operator_and_multiple_following_arguments.yaml index 5be3b72611a0..3d28a724ec45 100644 --- a/spec/truffle/parsing/fixtures/yield/with_splat_operator_and_multiple_following_arguments.yaml +++ b/spec/truffle/parsing/fixtures/yield/with_splat_operator_and_multiple_following_arguments.yaml @@ -87,7 +87,6 @@ ast: | lastArgIsNotHashProfile = false notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: arguments = [ ArrayConcatNode @@ -117,7 +116,6 @@ ast: | methodName = "foo" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: receiver = SelfNode @@ -151,7 +149,6 @@ ast: | methodName = "bar" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: receiver = SelfNode @@ -171,7 +168,6 @@ ast: | methodName = "baz" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: receiver = SelfNode diff --git a/spec/truffle/parsing/fixtures/yield/with_splat_operator_and_multiple_preceding_and_following_positional_arguments.yaml b/spec/truffle/parsing/fixtures/yield/with_splat_operator_and_multiple_preceding_and_following_positional_arguments.yaml index 73896c6f52df..b329c832f3be 100644 --- a/spec/truffle/parsing/fixtures/yield/with_splat_operator_and_multiple_preceding_and_following_positional_arguments.yaml +++ b/spec/truffle/parsing/fixtures/yield/with_splat_operator_and_multiple_preceding_and_following_positional_arguments.yaml @@ -93,7 +93,6 @@ ast: | lastArgIsNotHashProfile = false notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: arguments = [ ArrayConcatNode @@ -121,7 +120,6 @@ ast: | methodName = "foo" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: receiver = SelfNode @@ -141,7 +139,6 @@ ast: | methodName = "bar" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: receiver = SelfNode @@ -170,7 +167,6 @@ ast: | methodName = "foobar" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: receiver = SelfNode @@ -204,7 +200,6 @@ ast: | methodName = "foo2" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: receiver = SelfNode @@ -224,7 +219,6 @@ ast: | methodName = "bar2" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: receiver = SelfNode diff --git a/spec/truffle/parsing/fixtures/yield/with_splat_operator_and_multiple_preceding_arguments.yaml b/spec/truffle/parsing/fixtures/yield/with_splat_operator_and_multiple_preceding_arguments.yaml index 15f7afb1c2a9..17a2baa2e77c 100644 --- a/spec/truffle/parsing/fixtures/yield/with_splat_operator_and_multiple_preceding_arguments.yaml +++ b/spec/truffle/parsing/fixtures/yield/with_splat_operator_and_multiple_preceding_arguments.yaml @@ -85,7 +85,6 @@ ast: | lastArgIsNotHashProfile = false notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: arguments = [ ArrayConcatNode @@ -113,7 +112,6 @@ ast: | methodName = "foo" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: receiver = SelfNode @@ -133,7 +131,6 @@ ast: | methodName = "bar" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: receiver = SelfNode @@ -162,7 +159,6 @@ ast: | methodName = "baz" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: receiver = SelfNode diff --git a/spec/truffle/parsing/fixtures/yield/with_splat_operator_and_single_following_argument.yaml b/spec/truffle/parsing/fixtures/yield/with_splat_operator_and_single_following_argument.yaml index 82fe3ba7bc03..5968d195c9fc 100644 --- a/spec/truffle/parsing/fixtures/yield/with_splat_operator_and_single_following_argument.yaml +++ b/spec/truffle/parsing/fixtures/yield/with_splat_operator_and_single_following_argument.yaml @@ -82,7 +82,6 @@ ast: | lastArgIsNotHashProfile = false notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: arguments = [ ArrayAppendOneNodeGen @@ -117,7 +116,6 @@ ast: | methodName = "foo" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: receiver = SelfNode @@ -138,7 +136,6 @@ ast: | methodName = "bar" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: receiver = SelfNode diff --git a/spec/truffle/parsing/fixtures/yield/with_splat_operator_and_single_preceding_and_following_positional_arguments.yaml b/spec/truffle/parsing/fixtures/yield/with_splat_operator_and_single_preceding_and_following_positional_arguments.yaml index 02ab31e1f543..a5a11aa7be23 100644 --- a/spec/truffle/parsing/fixtures/yield/with_splat_operator_and_single_preceding_and_following_positional_arguments.yaml +++ b/spec/truffle/parsing/fixtures/yield/with_splat_operator_and_single_preceding_and_following_positional_arguments.yaml @@ -85,7 +85,6 @@ ast: | lastArgIsNotHashProfile = false notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: arguments = [ ArrayAppendOneNodeGen @@ -123,7 +122,6 @@ ast: | methodName = "foo" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: receiver = SelfNode @@ -152,7 +150,6 @@ ast: | methodName = "bar" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: receiver = SelfNode @@ -174,7 +171,6 @@ ast: | methodName = "baz" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: receiver = SelfNode diff --git a/spec/truffle/parsing/fixtures/yield/with_splat_operator_and_single_preceding_argument.yaml b/spec/truffle/parsing/fixtures/yield/with_splat_operator_and_single_preceding_argument.yaml index d650fb63b17d..2e2fd2296928 100644 --- a/spec/truffle/parsing/fixtures/yield/with_splat_operator_and_single_preceding_argument.yaml +++ b/spec/truffle/parsing/fixtures/yield/with_splat_operator_and_single_preceding_argument.yaml @@ -82,7 +82,6 @@ ast: | lastArgIsNotHashProfile = false notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: arguments = [ ArrayConcatNode @@ -110,7 +109,6 @@ ast: | methodName = "foo" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: receiver = SelfNode @@ -139,7 +137,6 @@ ast: | methodName = "bar" notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: receiver = SelfNode diff --git a/spec/truffle/parsing/fixtures/yield/without_arguments.yaml b/spec/truffle/parsing/fixtures/yield/without_arguments.yaml index dab51ab8cb56..7f2636b3cb3e 100644 --- a/spec/truffle/parsing/fixtures/yield/without_arguments.yaml +++ b/spec/truffle/parsing/fixtures/yield/without_arguments.yaml @@ -69,7 +69,6 @@ ast: | lastArgIsNotHashProfile = false notEmptyKeywordsProfile = false notRuby2KeywordsHashProfile = false - ruby2KeywordsHashProfile = false children: readBlockNode = ReadLocalVariableNode diff --git a/src/main/java/org/truffleruby/parser/BodyTranslator.java b/src/main/java/org/truffleruby/parser/BodyTranslator.java index b8a504939b7e..5bee46374e6c 100644 --- a/src/main/java/org/truffleruby/parser/BodyTranslator.java +++ b/src/main/java/org/truffleruby/parser/BodyTranslator.java @@ -794,10 +794,18 @@ public RubyNode visitCaseNode(CaseParseNode node) { for (int n = node.getCases().size() - 1; n >= 0; n--) { final WhenParseNode when = (WhenParseNode) node.getCases().get(n); - // JRuby AST always gives WhenParseNode with only one expression. - // "when 1,2; body" gets translated to 2 WhenParseNode. final ParseNode expressionNode = when.getExpressionNodes(); - final RubyNode conditionNode = expressionNode.accept(this); + final RubyNode rubyExpression = expressionNode.accept(this); + final RubyNode conditionNode; + + if (when instanceof WhenOneArgParseNode) { + // JRuby AST always gives WhenParseNode with only one expression. + // "when 1,2; body" gets translated to 2 WhenParseNode. + conditionNode = rubyExpression; + } else { + // when a, *b, c + conditionNode = createCallNode(rubyExpression, "any?"); + } // Create the if node final RubyNode thenNode = when.getBodyNode().accept(this); @@ -1507,12 +1515,7 @@ public RubyNode visitFlipNode(FlipParseNode node) { protected FrameSlotAndDepth createFlipFlopState(SourceIndexLength sourceSection, int depth) { final int frameSlot = environment.declareLocalTemp("flipflop"); environment.getFlipFlopStates().add(frameSlot); - - if (depth == 0) { - return new FrameSlotAndDepth(frameSlot, 0); - } else { - return new FrameSlotAndDepth(frameSlot, depth); - } + return new FrameSlotAndDepth(frameSlot, depth); } @Override diff --git a/src/main/java/org/truffleruby/parser/YARPTranslator.java b/src/main/java/org/truffleruby/parser/YARPTranslator.java index 0973133561c6..bd12b3a35c79 100644 --- a/src/main/java/org/truffleruby/parser/YARPTranslator.java +++ b/src/main/java/org/truffleruby/parser/YARPTranslator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved. This + * Copyright (c) 2022, 2023 Oracle and/or its affiliates. All rights reserved. This * code is released under a tri EPL/GPL/LGPL license. You can use it, * redistribute it and/or modify it under the terms of the: * @@ -11,13 +11,16 @@ import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.nodes.NodeUtil; import com.oracle.truffle.api.source.Source; import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.strings.TruffleString; import org.truffleruby.RubyContext; import org.truffleruby.RubyLanguage; import org.truffleruby.annotations.Split; +import org.truffleruby.builtins.PrimitiveNodeConstructor; import org.truffleruby.core.CoreLibrary; +import org.truffleruby.core.array.ArrayConcatNode; import org.truffleruby.core.array.ArrayLiteralNode; import org.truffleruby.core.array.AssignableNode; import org.truffleruby.core.cast.HashCastNodeGen; @@ -34,14 +37,17 @@ import org.truffleruby.core.module.ModuleNodes; import org.truffleruby.core.rescue.AssignRescueVariableNode; import org.truffleruby.core.string.FrozenStrings; +import org.truffleruby.core.string.ImmutableRubyString; import org.truffleruby.core.string.InterpolatedStringNode; import org.truffleruby.core.symbol.RubySymbol; import org.truffleruby.debug.ChaosNode; import org.truffleruby.language.LexicalScope; +import org.truffleruby.language.NotProvided; import org.truffleruby.language.RubyContextSourceNode; import org.truffleruby.language.RubyNode; import org.truffleruby.language.RubyRootNode; import org.truffleruby.language.RubyTopLevelRootNode; +import org.truffleruby.language.SourceIndexLength; import org.truffleruby.language.arguments.EmptyArgumentsDescriptor; import org.truffleruby.language.arguments.ProfileArgumentNodeGen; import org.truffleruby.language.arguments.ReadSelfNode; @@ -51,6 +57,7 @@ import org.truffleruby.language.constants.WriteConstantNode; import org.truffleruby.language.control.AndNodeGen; import org.truffleruby.language.control.BreakNode; +import org.truffleruby.language.control.IfElseNode; import org.truffleruby.language.control.IfElseNodeGen; import org.truffleruby.language.control.IfNodeGen; import org.truffleruby.language.control.NextNode; @@ -86,6 +93,9 @@ import org.truffleruby.language.literal.ObjectClassLiteralNode; import org.truffleruby.language.literal.ObjectLiteralNode; import org.truffleruby.language.literal.StringLiteralNode; +import org.truffleruby.language.literal.TruffleInternalModuleLiteralNode; +import org.truffleruby.language.locals.FindDeclarationVariableNodes; +import org.truffleruby.language.locals.FlipFlopNodeGen; import org.truffleruby.language.locals.InitFlipFlopSlotNode; import org.truffleruby.language.locals.ReadLocalNode; import org.truffleruby.language.locals.WriteLocalNode; @@ -214,12 +224,7 @@ public RubyNode visitArgumentsNode(Nodes.ArgumentsNode node) { return values[0].accept(this); } - final RubyNode[] translatedValues = createArray(values.length); - - for (int n = 0; n < values.length; n++) { - translatedValues[n] = values[n].accept(this); - } - + final RubyNode[] translatedValues = translate(values); final RubyNode rubyNode = ArrayLiteralNode.create(language, translatedValues); assignNodePositionInSource(node, rubyNode); @@ -227,11 +232,7 @@ public RubyNode visitArgumentsNode(Nodes.ArgumentsNode node) { } public RubyNode visitArrayNode(Nodes.ArrayNode node) { - RubyNode[] elements = new RubyNode[node.elements.length]; - for (int i = 0; i < node.elements.length; i++) { - elements[i] = node.elements[i].accept(this); - } - + RubyNode[] elements = translate(node.elements); RubyNode rubyNode = ArrayLiteralNode.create(language, elements); assignNodePositionInSource(node, rubyNode); return rubyNode; @@ -378,11 +379,7 @@ private RescueNode translateExceptionNodes(ArrayList exceptionNodes, RubyNode translatedBody = translateNodeOrNil(rescueClause.statements); final Nodes.Node[] exceptionNodesArray = exceptionNodes.toArray(Nodes.Node.EMPTY_ARRAY); - final RubyNode[] handlingClasses = new RubyNode[exceptionNodesArray.length]; - - for (int i = 0; i < exceptionNodesArray.length; i++) { - handlingClasses[i] = exceptionNodesArray[i].accept(this); - } + final RubyNode[] handlingClasses = translate(exceptionNodesArray); if (rescueClause.reference != null) { final RubyNode exceptionWriteNode = translateRescueException( @@ -426,7 +423,7 @@ public RubyNode visitBreakNode(Nodes.BreakNode node) { } public RubyNode visitCallNode(Nodes.CallNode node) { - var methodName = TStringUtils.toJavaStringOrThrow(node.name, sourceEncoding); + var methodName = toString(node.name); var receiver = node.receiver == null ? new SelfNode() : node.receiver.accept(this); final Nodes.Node[] arguments; @@ -436,20 +433,63 @@ public RubyNode visitCallNode(Nodes.CallNode node) { arguments = node.arguments.arguments; } - var translatedArguments = new RubyNode[arguments.length]; - for (int i = 0; i < arguments.length; i++) { - translatedArguments[i] = arguments[i].accept(this); + var translatedArguments = translate(arguments); + + // If the receiver is explicit or implicit 'self' then we can call private methods + final boolean ignoreVisibility = node.receiver == null || node.receiver instanceof Nodes.SelfNode; + final boolean isVariableCall = node.isVariableCall(); + final boolean isAttrAssign = methodName.endsWith("="); + final boolean isSafeNavigation = node.isSafeNavigation(); + + // TODO (pitr-ch 02-Dec-2019): replace with a primitive + if (environment.getParseEnvironment().inCore() && isVariableCall && methodName.equals("undefined")) { + // translate undefined + final RubyNode rubyNode = new ObjectLiteralNode(NotProvided.INSTANCE); + assignNodePositionInSource(node, rubyNode); + return rubyNode; + } + + if (node.receiver instanceof Nodes.StringNode stringNode && + (methodName.equals("freeze") || methodName.equals("-@") || methodName.equals("dedup"))) { + final TruffleString tstring = TStringUtils.fromByteArray(stringNode.unescaped, sourceEncoding); + final ImmutableRubyString frozenString = language.getFrozenStringLiteral(tstring, sourceEncoding); + final RubyNode rubyNode = new FrozenStringLiteralNode(frozenString, FrozenStrings.METHOD); + + assignNodePositionInSource(node, rubyNode); + return rubyNode; + } + + // Translates something that looks like + // Primitive.foo arg1, arg2, argN + // into + // InvokePrimitiveNode(FooNode(arg1, arg2, ..., argN)) + if (environment.getParseEnvironment().canUsePrimitives() && + node.receiver instanceof Nodes.ConstantReadNode constantReadNode && + toString(constantReadNode).equals("Primitive")) { + + final PrimitiveNodeConstructor constructor = language.primitiveManager.getPrimitive(methodName); + // TODO: avoid SourceIndexLength + final SourceIndexLength sourceSection = new SourceIndexLength(node.startOffset, node.length); + final RubyNode rubyNode = constructor.createInvokePrimitiveNode(source, sourceSection, translatedArguments); + + return rubyNode; } - boolean ignoreVisibility = node.receiver == null; - boolean isVCall = node.isVariableCall(); - boolean isAttrAssign = methodName.endsWith("="); - var rubyCallNode = new RubyCallNode(new RubyCallNodeParameters(receiver, methodName, null, - EmptyArgumentsDescriptor.INSTANCE, translatedArguments, false, ignoreVisibility, isVCall, false, - isAttrAssign)); + final var callNodeParameters = new RubyCallNodeParameters( + receiver, + methodName, + null, + EmptyArgumentsDescriptor.INSTANCE, + translatedArguments, + false, + ignoreVisibility, + isVariableCall, + isSafeNavigation, + isAttrAssign); + final RubyNode rubyNode = language.coreMethodAssumptions.createCallNode(callNodeParameters); - assignNodePositionInSource(node, rubyCallNode); - return rubyCallNode; + assignNodePositionInSource(node, rubyNode); + return rubyNode; } public RubyNode visitCallOperatorWriteNode(Nodes.CallOperatorWriteNode node) { @@ -461,7 +501,140 @@ public RubyNode visitCapturePatternNode(Nodes.CapturePatternNode node) { } public RubyNode visitCaseNode(Nodes.CaseNode node) { - return defaultVisit(node); + // There are two sorts of case + // - one compares a list of expressions against a value, + // - the other just checks a list of expressions for truth. + + final RubyNode rubyNode; + RubyNode elseNode = translateNodeOrNil(node.consequent); + + if (node.predicate != null) { + // Evaluate the case expression and store it in a local + final int tempSlot = environment.declareLocalTemp("case"); + final ReadLocalNode readTemp = environment.readNode(tempSlot, null); + final RubyNode assignTemp = readTemp.makeWriteNode(node.predicate.accept(this)); + + // Build an if expression from `when` and `else` branches. + // Work backwards to make the first if contain all the others in its `else` clause. + final Nodes.Node[] conditions = node.conditions; + + for (int n = conditions.length - 1; n >= 0; n--) { + // condition is either WhenNode or InNode + // don't handle InNode for now + assert conditions[n] instanceof Nodes.WhenNode; + + final Nodes.WhenNode when = (Nodes.WhenNode) conditions[n]; + final Nodes.Node[] whenConditions = when.conditions; + boolean containSplatOperator = containYARPSplatNode(whenConditions); + + if (containSplatOperator) { + final RubyNode receiver = new TruffleInternalModuleLiteralNode(); + final RubyNode whenConditionNode = translateExpressionsList(whenConditions); + final RubyNode[] arguments = new RubyNode[]{ whenConditionNode, NodeUtil.cloneNode(readTemp) }; + final RubyNode predicateNode = createCallNode(receiver, "when_splat", arguments); + + // create `if` node + final RubyNode thenNode = translateNodeOrNil(when.statements); + final IfElseNode ifNode = IfElseNodeGen.create(predicateNode, thenNode, elseNode); + + // this `if` becomes `else` branch of the outer `if` + elseNode = ifNode; + } else { + // translate `when` with multiple expressions into a single `if` operator, e.g. + // case x + // when a, b, c + // is translated into + // if x === a || x === b || x === c + + RubyNode predicateNode = null; + + for (var whenCondition : whenConditions) { + final RubyNode receiver = whenCondition.accept(this); + final RubyNode[] arguments = new RubyNode[]{ NodeUtil.cloneNode(readTemp) }; + final RubyNode nextPredicateNode = createCallNode(receiver, "===", arguments); + + if (predicateNode == null) { + predicateNode = nextPredicateNode; + } else { + predicateNode = OrNodeGen.create(predicateNode, nextPredicateNode); + } + } + + // create `if` node + final RubyNode thenNode = translateNodeOrNil(when.statements); + final IfElseNode ifNode = IfElseNodeGen.create(predicateNode, thenNode, elseNode); + + // this `if` becomes `else` branch of the outer `if` + elseNode = ifNode; + } + } + + final RubyNode ifNode = elseNode; + + // A top-level block assigns the temp then runs the `if` + rubyNode = sequence(Arrays.asList(assignTemp, ifNode)); + } else { + // Build an if expression from `when` and `else` branches. + // Work backwards to make the first if contain all the others in its `else` clause. + + final Nodes.Node[] conditions = node.conditions; + + for (int n = node.conditions.length - 1; n >= 0; n--) { + // condition is either WhenNode or InNode + // don't handle InNode for now + assert conditions[n] instanceof Nodes.WhenNode; + + final Nodes.WhenNode when = (Nodes.WhenNode) conditions[n]; + final Nodes.Node[] whenConditions = when.conditions; + boolean containSplatOperator = containYARPSplatNode(whenConditions); + + if (!containSplatOperator) { + // translate `when` with multiple expressions into a single `if` operator, e.g. + // case + // when a, b, c + // is translated into + // if a || b || c + + RubyNode predicateNode = null; + + for (var whenCondition : whenConditions) { + final RubyNode nextPredicateNode = whenCondition.accept(this); + + if (predicateNode == null) { + predicateNode = nextPredicateNode; + } else { + predicateNode = OrNodeGen.create(predicateNode, nextPredicateNode); + } + } + + // create `if` node + final RubyNode thenNode = translateNodeOrNil(when.statements); + final IfElseNode ifNode = IfElseNodeGen.create(predicateNode, thenNode, elseNode); + + // this `if` becomes `else` branch of the outer `if` + elseNode = ifNode; + } else { + // use Array#any? to check whether there is any truthy value + // whenConditions are translated into an array-producing node + // so `when a, *b, c` is translated into `[a, *b, c].any?` + final RubyNode whenConditionNode = translateExpressionsList(whenConditions); + final RubyNode receiver = whenConditionNode; + final RubyNode predicateNode = createCallNode(receiver, "any?"); + + // create `if` node + final RubyNode thenNode = translateNodeOrNil(when.statements); + final IfElseNode ifNode = IfElseNodeGen.create(predicateNode, thenNode, elseNode); + + // this `if` becomes `else` branch of the outer `if` + elseNode = ifNode; + } + } + + rubyNode = elseNode; + } + + assignNodePositionInSource(node, rubyNode); + return rubyNode; } public RubyNode visitClassNode(Nodes.ClassNode node) { @@ -519,6 +692,9 @@ public RubyNode visitClassVariableTargetNode(Nodes.ClassVariableTargetNode node) } public RubyNode visitConstantPathNode(Nodes.ConstantPathNode node) { + // The child field should always be ConstantReadNode if there are no syntax errors. + // MissingNode could be assigned as well as an error recovery means, + // but we don't handle this case as far as it means there is a syntax error and translation is skipped at all. assert node.child instanceof Nodes.ConstantReadNode; final String name = ((Nodes.ConstantReadNode) node.child).name; @@ -675,6 +851,18 @@ public RubyNode visitFindPatternNode(Nodes.FindPatternNode node) { return defaultVisit(node); } + public RubyNode visitFlipFlopNode(Nodes.FlipFlopNode node) { + final RubyNode begin = node.left.accept(this); + final RubyNode end = node.right.accept(this); + + final FindDeclarationVariableNodes.FrameSlotAndDepth slotAndDepth = createFlipFlopState(0); + final RubyNode rubyNode = FlipFlopNodeGen.create(begin, end, node.isExcludeEnd(), slotAndDepth.depth, + slotAndDepth.slot); + + assignNodePositionInSource(node, rubyNode); + return rubyNode; + } + public RubyNode visitFloatNode(Nodes.FloatNode node) { // parse Integer literal ourselves // See https://github.com/ruby/yarp/issues/1098 @@ -867,7 +1055,8 @@ public RubyNode visitIntegerNode(Nodes.IntegerNode node) { } else if (string.startsWith("0o") || string.startsWith("0O")) { radix = 8; offset = 2; - } else if (string.startsWith("0")) { + } else if (string.startsWith("0") && string.length() > 1) { + // check length to distinguish `0` from octal literal `0...` radix = 8; offset = 1; } else { @@ -889,18 +1078,6 @@ public RubyNode visitInterpolatedRegularExpressionNode(Nodes.InterpolatedRegular public RubyNode visitInterpolatedStringNode(Nodes.InterpolatedStringNode node) { final ToSNode[] children = new ToSNode[node.parts.length]; - // a special case for `:"abc"` literal - convert to Symbol ourselves - if (node.parts.length == 1 && node.parts[0] instanceof Nodes.StringNode s) { - final TruffleString tstring = TStringUtils.fromByteArray(s.unescaped, sourceEncoding); - final TruffleString cachedTString = language.tstringCache.getTString(tstring, sourceEncoding); - final RubyNode rubyNode = new StringLiteralNode(cachedTString, sourceEncoding); - - assignNodePositionInSource(node, rubyNode); - copyNewlineFlag(s, rubyNode); - - return rubyNode; - } - for (int i = 0; i < node.parts.length; i++) { var part = node.parts[i]; @@ -1062,11 +1239,9 @@ public RubyNode visitNilNode(Nodes.NilNode node) { } public RubyNode visitNumberedReferenceReadNode(Nodes.NumberedReferenceReadNode node) { - final String name = toString(node); - final int index = Integer.parseInt(name.substring(1)); final RubyNode lastMatchNode = ReadGlobalVariableNodeGen.create("$~"); + final RubyNode rubyNode = new ReadMatchReferenceNodes.ReadNthMatchNode(lastMatchNode, node.number); - final RubyNode rubyNode = new ReadMatchReferenceNodes.ReadNthMatchNode(lastMatchNode, index); assignNodePositionInSource(node, rubyNode); return rubyNode; } @@ -1218,12 +1393,8 @@ public RubyNode visitSplatNode(Nodes.SplatNode node) { } public RubyNode visitStatementsNode(Nodes.StatementsNode node) { - var body = node.body; - var translated = new RubyNode[body.length]; - for (int i = 0; i < body.length; i++) { - translated[i] = body[i].accept(this); - } - return sequence(node, Arrays.asList(translated)); + RubyNode[] rubyNodes = translate(node.body); + return sequence(node, Arrays.asList(rubyNodes)); } public RubyNode visitStringConcatNode(Nodes.StringConcatNode node) { @@ -1261,11 +1432,7 @@ public RubyNode visitTrueNode(Nodes.TrueNode node) { } public RubyNode visitUndefNode(Nodes.UndefNode node) { - RubyNode[] names = new RubyNode[node.names.length]; - for (int i = 0; i < node.names.length; i++) { - names[i] = node.names[i].accept(this); - } - + final RubyNode[] names = translate(node.names); final RubyNode rubyNode = new ModuleNodes.UndefNode(names); assignNodePositionInSource(node, rubyNode); return rubyNode; @@ -1296,17 +1463,20 @@ public RubyNode visitUnlessNode(Nodes.UnlessNode node) { } public RubyNode visitUntilNode(Nodes.UntilNode node) { - final RubyNode rubyNode = translateWhileNode(node, node.predicate, node.statements, true); + final RubyNode rubyNode = translateWhileNode(node, node.predicate, node.statements, true, + !node.isBeginModifier()); assignNodePositionInSource(node, rubyNode); return rubyNode; } + // handled in #visitCaseNode method public RubyNode visitWhenNode(Nodes.WhenNode node) { return defaultVisit(node); } public RubyNode visitWhileNode(Nodes.WhileNode node) { - final RubyNode rubyNode = translateWhileNode(node, node.predicate, node.statements, false); + final RubyNode rubyNode = translateWhileNode(node, node.predicate, node.statements, false, + !node.isBeginModifier()); assignNodePositionInSource(node, rubyNode); return rubyNode; } @@ -1330,6 +1500,65 @@ protected RubyNode defaultVisit(Nodes.Node node) { throw new Error("Unknown node: " + node); } + protected FindDeclarationVariableNodes.FrameSlotAndDepth createFlipFlopState(int depth) { + final int frameSlot = environment.declareLocalTemp("flipflop"); + environment.getFlipFlopStates().add(frameSlot); + return new FindDeclarationVariableNodes.FrameSlotAndDepth(frameSlot, depth); + } + + /** Translate a list of nodes, e.g. break/return operands, into an array producing node. It returns ArrayLiteralNode + * subclass in the simplest case (when there is no splat operator) or combination of + * ArrayConcatNode/ArrayLiteralNode/SplatCastNodeGen nodes to "join" destructured by splat operator array with other + * operands. */ + private RubyNode translateExpressionsList(Nodes.Node[] nodes) { + assert nodes != null; + assert nodes.length > 0; + + boolean containSplatOperator = containYARPSplatNode(nodes); + + // fast path (no SplatNode) + + if (!containSplatOperator) { + RubyNode[] rubyNodes = translate(nodes); + return ArrayLiteralNode.create(language, rubyNodes); + } + + // generic path + + ArrayList arraysToConcat = new ArrayList<>(); + ArrayList current = new ArrayList<>(); + + // group nodes before/after/between splat operators into array literals and concat them, e.g. + // a, b, *c, d + // is translated into + // ArrayConcatNode(ArrayLiteralNode(a, b), "splat-operator-node", ArrayLiteralNode(d)) + for (Nodes.Node node : nodes) { + if (node instanceof Nodes.SplatNode) { + if (!current.isEmpty()) { + arraysToConcat.add(ArrayLiteralNode.create(language, current.toArray(RubyNode.EMPTY_ARRAY))); + current = new ArrayList<>(); + } + arraysToConcat.add(node.accept(this)); + } else { + current.add(node.accept(this)); + } + } + + if (!current.isEmpty()) { + arraysToConcat.add(ArrayLiteralNode.create(language, current.toArray(RubyNode.EMPTY_ARRAY))); + } + + final RubyNode rubyNode; + + if (arraysToConcat.size() == 1) { + rubyNode = arraysToConcat.get(0); + } else { + rubyNode = new ArrayConcatNode(arraysToConcat.toArray(RubyNode.EMPTY_ARRAY)); + } + + return rubyNode; + } + private RubyNode getLexicalScopeNode(String kind, Nodes.Node yarpNode) { if (environment.isDynamicConstantLookup()) { if (language.options.LOG_DYNAMIC_CONSTANT_LOOKUP) { @@ -1552,15 +1781,9 @@ private RubyNode translateControlFlowArguments(Nodes.ArgumentsNode node) { return values[0].accept(this); } - final RubyNode[] translatedValues = createArray(values.length); - - for (int n = 0; n < values.length; n++) { - translatedValues[n] = values[n].accept(this); - } + final RubyNode rubyNode = translateExpressionsList(values); - final RubyNode rubyNode = ArrayLiteralNode.create(language, translatedValues); assignNodePositionInSource(node, rubyNode); - return rubyNode; } @@ -1589,7 +1812,7 @@ private RubyNode translateRescueException(Nodes.Node exception) { } private RubyNode translateWhileNode(Nodes.Node node, Nodes.Node predicate, Nodes.StatementsNode statements, - boolean conditionInversed) { + boolean conditionInversed, boolean evaluateConditionBeforeBody) { RubyNode condition = predicate.accept(this); if (conditionInversed) { condition = NotNodeGen.create(condition); @@ -1613,11 +1836,10 @@ private RubyNode translateWhileNode(Nodes.Node node, Nodes.Node predicate, Nodes } final RubyNode loop; - final boolean evaluateAtStart = !(statements != null && statements.body[0] instanceof Nodes.BeginNode); // in case of `begin ... end while ()` // the begin/end block is executed before condition - if (evaluateAtStart) { + if (evaluateConditionBeforeBody) { loop = new WhileNode(WhileNodeFactory.WhileRepeatingNodeGen.create(condition, body)); } else { loop = new WhileNode(WhileNodeFactory.DoWhileRepeatingNodeGen.create(condition, body)); @@ -1644,10 +1866,6 @@ protected boolean isSideEffectFreeRescueExpression(Nodes.Node node) { node instanceof Nodes.ClassVariableReadNode || node instanceof Nodes.StringNode || node instanceof Nodes.SymbolNode || - // See https://github.com/ruby/yarp/issues/1120 - (node instanceof Nodes.InterpolatedSymbolNode isn && // :"abc" - isn.parts.length == 1 && - isn.parts[0] instanceof Nodes.StringNode) || node instanceof Nodes.IntegerNode || node instanceof Nodes.FloatNode || node instanceof Nodes.ImaginaryNode || @@ -1658,7 +1876,12 @@ protected boolean isSideEffectFreeRescueExpression(Nodes.Node node) { node instanceof Nodes.NilNode; } - private String toString(Nodes.Node node) { + private String toString(byte[] bytes) { + return TStringUtils.toJavaStringOrThrow( + TruffleString.fromByteArrayUncached(bytes, sourceEncoding.tencoding, false), sourceEncoding); + } + + protected String toString(Nodes.Node node) { return TStringUtils.toJavaStringOrThrow(TruffleString.fromByteArrayUncached(sourceBytes, node.startOffset, node.length, sourceEncoding.tencoding, false), sourceEncoding); } @@ -1714,4 +1937,27 @@ private void assignNodePositionInSource(Nodes.Node[] nodes, RubyNode rubyNode) { rubyNode.unsafeSetSourceSection(first.startOffset, length); } + private boolean containYARPSplatNode(Nodes.Node[] nodes) { + for (var n : nodes) { + if (n instanceof Nodes.SplatNode) { + return true; + } + } + + return false; + } + + private RubyNode[] translate(Nodes.Node[] nodes) { + if (nodes.length == 0) { + return RubyNode.EMPTY_ARRAY; + } + + RubyNode[] rubyNodes = new RubyNode[nodes.length]; + + for (int i = 0; i < nodes.length; i++) { + rubyNodes[i] = nodes[i].accept(this); + } + + return rubyNodes; + } } diff --git a/src/main/java/org/truffleruby/parser/YARPTranslatorDriver.java b/src/main/java/org/truffleruby/parser/YARPTranslatorDriver.java index 5ee39f68caac..16678744831b 100644 --- a/src/main/java/org/truffleruby/parser/YARPTranslatorDriver.java +++ b/src/main/java/org/truffleruby/parser/YARPTranslatorDriver.java @@ -79,6 +79,7 @@ import org.truffleruby.language.locals.WriteLocalVariableNode; import org.truffleruby.language.methods.Arity; import org.truffleruby.language.methods.SharedMethodInfo; +import org.truffleruby.parser.lexer.RubyLexer; import org.truffleruby.parser.parser.ParserConfiguration; import org.truffleruby.parser.scope.StaticScope; import org.truffleruby.platform.Platform; @@ -165,6 +166,12 @@ public RootCallTarget parse(RubySource rubySource, ParserContext parserContext, parserConfiguration.setFrozenStringLiteral(true); } + RubyLexer.parseMagicComment(rubySource.getTStringWithEncoding(), (name, value) -> { + if (RubyLexer.isMagicTruffleRubyPrimitivesComment(name)) { + parserConfiguration.allowTruffleRubyPrimitives = value.equalsIgnoreCase("true"); + } + }); + // Parse to the YARP AST final org.yarp.Nodes.Node node; diff --git a/src/main/java/org/truffleruby/parser/lexer/RubyLexer.java b/src/main/java/org/truffleruby/parser/lexer/RubyLexer.java index 1cc0e6e1b01e..43ccbd076ad1 100644 --- a/src/main/java/org/truffleruby/parser/lexer/RubyLexer.java +++ b/src/main/java/org/truffleruby/parser/lexer/RubyLexer.java @@ -1396,6 +1396,10 @@ public static boolean isMagicEncodingComment(String name) { return "coding".equalsIgnoreCase(name) || "encoding".equalsIgnoreCase(name); } + public static boolean isMagicTruffleRubyPrimitivesComment(String name) { + return "truffleruby_primitives".equalsIgnoreCase(name); + } + private int at() { newtok(true); int c = nextc(); diff --git a/src/main/java/org/truffleruby/parser/parser/ParserConfiguration.java b/src/main/java/org/truffleruby/parser/parser/ParserConfiguration.java index 52e1f658bdc6..6939272ba988 100644 --- a/src/main/java/org/truffleruby/parser/parser/ParserConfiguration.java +++ b/src/main/java/org/truffleruby/parser/parser/ParserConfiguration.java @@ -30,8 +30,6 @@ ***** END LICENSE BLOCK *****/ package org.truffleruby.parser.parser; -import org.jcodings.Encoding; -import org.jcodings.specific.UTF8Encoding; import org.truffleruby.RubyContext; import org.truffleruby.parser.scope.StaticScope; @@ -50,7 +48,6 @@ public final class ParserConfiguration { private boolean frozenStringLiteral = false; public boolean allowTruffleRubyPrimitives = false; - private Encoding defaultEncoding; private RubyContext context; public ParserConfiguration(RubyContext context, boolean inlineSource, boolean isFileParse, boolean saveData) { @@ -68,18 +65,6 @@ public boolean isFrozenStringLiteral() { return frozenStringLiteral; } - public void setDefaultEncoding(Encoding encoding) { - this.defaultEncoding = encoding; - } - - public Encoding getDefaultEncoding() { - if (defaultEncoding == null) { - defaultEncoding = UTF8Encoding.INSTANCE; - } - - return defaultEncoding; - } - public boolean isDebug() { return isDebug; }