| Age | Commit message (Collapse) | Author |
|
|
|
|
|
|
|
This commit implements safe navigation for CallNodes,
CallAndWriteNodes and CallOperatorWriteNodes
|
|
* Start using the renamed `PM_KEYWORD_HASH_NODE_FLAGS_SYMBOL_KEYS` flag
to check if all keys of the keyword hash node are symbols.
* For arguments passed as a hash, start marking them as `KW_SPLAT_MUT` only if the number of entries in the hash is greater than 1 (which is what the old compiler used to do).
|
|
|
|
This commit adjusts the local table size to be consistent regardless
of the number of anonymous locals.
|
|
This PR fixes two bugs when compiling optional keyword parameters:
- It moves keyword parameter compilation to STEP 5 in the parameters
sequence, where the rest of compilation happens. This is important
because keyword parameter compilation relies on the value of
body->param.keyword->bits_start which gets set in an earlier step
- It compiles array and hash values for keyword parameters, which
it didn't previously
|
|
|
|
|
|
|
|
I looked at this at RubyConf with Kevin, and we noticed that there was a
`putobject` empty string missing from the instructions. I just got back
around to implementing this and pushing a PR and while doing that
noticed that we also have a `getspecial` when we want a `getglobal`.
This change adds the `putobject` instruction and replaces the
`getspecial` with a `getglobal`. If we look at the parsetree for the
following code:
```ruby
$pit = '.oo'; if /"#{$pit}"/mix; end
```
We can see it has a `NODE_GVAR` and the [Ruby
compiler](https://github.com/ruby/ruby/blob/a4b43e92645e46ee5a8c9af42d3de57cd052e87c/compile.c#L10024-L10030) shows that should
use `getglobal.
```
@ NODE_SCOPE (id: 14, line: 1, location: (1,0)-(22,36))
+- nd_tbl: (empty)
+- nd_args:
| (null node)
+- nd_body:
@ NODE_BLOCK (id: 12, line: 22, location: (22,0)-(22,36))
+- nd_head (1):
| @ NODE_GASGN (id: 0, line: 22, location: (22,0)-(22,12))*
| +- nd_vid: :$pit
| +- nd_value:
| @ NODE_STR (id: 1, line: 22, location: (22,7)-(22,12))
| +- nd_lit: ".oo"
+- nd_head (2):
@ NODE_IF (id: 11, line: 22, location: (22,14)-(22,36))*
+- nd_cond:
| @ NODE_MATCH2 (id: 10, line: 22, location: (22,14)-(22,36))
| +- nd_recv:
| | @ NODE_DREGX (id: 2, line: 22, location: (22,17)-(22,31))
| | +- nd_lit: "\""
| | +- nd_next->nd_head:
| | | @ NODE_EVSTR (id: 4, line: 22, location: (22,19)-(22,26))
| | | +- nd_body:
| | | @ NODE_GVAR (id: 3, line: 22, location: (22,21)-(22,25))
| | | +- nd_vid: :$pit
| | +- nd_next->nd_next:
| | @ NODE_LIST (id: 7, line: 22, location: (22,26)-(22,27))
| | +- as.nd_alen: 1
| | +- nd_head:
| | | @ NODE_STR (id: 6, line: 22, location: (22,26)-(22,27))
| | | +- nd_lit: "\""
| | +- nd_next:
| | (null node)
| +- nd_value:
| @ NODE_GVAR (id: 9, line: 22, location: (22,14)-(22,36))
| +- nd_vid: :$_
+- nd_body:
| @ NODE_BEGIN (id: 8, line: 22, location: (22,32)-(22,32))
| +- nd_body:
| (null node)
+- nd_else:
(null node)
```
I'm struggling with writing a failing test, but the before/after
instructions show that `getglobal` is correct here. I compared the
instructions for the other `InterpolatedMatchLastLine` node tests
and they also used `getglobal`. I've edited the existing
`InterpolatedLastMatchLineNode` test so that it will use that instruction when
copied out of the test. Without the quotes it thinks it's just a
`MatchLastLineNode`.
Incorrect instructions:
```
"********* Ruby *************"
== disasm: #<ISeq:<compiled>@<compiled>:1 (1,0)-(22,36)>
0000 putstring ".oo" ( 22)[Li]
0002 setglobal :$pit
0004 putobject "\""
0006 getglobal :$pit
0008 dup
0009 objtostring <calldata!mid:to_s, argc:0, FCALL|ARGS_SIMPLE>
0011 anytostring
0012 putobject "\""
0014 toregexp 7, 3
0017 getglobal :$_
0019 send <calldata!mid:=~, argc:1, ARGS_SIMPLE>, nil
0022 branchunless 30
0024 jump 26
0026 putnil
0027 jump 31
0029 pop
0030 putnil
0031 leave
"********* PRISM *************"
== disasm: #<ISeq:<compiled>@<compiled>:21 (21,0)-(21,36)>
0000 putstring ".oo" ( 21)[Li]
0002 setglobal :$pit
0004 putobject "\""
0006 getglobal :$pit
0008 dup
0009 objtostring <calldata!mid:to_s, argc:0, FCALL|ARGS_SIMPLE>
0011 anytostring
0012 putobject "\""
0014 toregexp 7, 3
0017 getspecial 0, 0
0020 send <calldata!mid:=~, argc:1, ARGS_SIMPLE>, nil
0023 branchunless 31
0025 jump 27
0027 putnil
0028 jump 32
0030 pop
0031 putnil
0032 leave
```
Fixed instructions:
```
== disasm: #<ISeq:<compiled>@<compiled>:1 (1,0)-(22,36)>
0000 putstring ".oo" ( 22)[Li]
0002 setglobal :$pit
0004 putobject "\""
0006 getglobal :$pit
0008 dup
0009 objtostring <calldata!mid:to_s, argc:0, FCALL|ARGS_SIMPLE>
0011 anytostring
0012 putobject "\""
0014 toregexp 7, 3
0017 getglobal :$_
0019 send <calldata!mid:=~, argc:1, ARGS_SIMPLE>, nil
0022 branchunless 30
0024 jump 26
0026 putnil
0027 jump 31
0029 pop
0030 putnil
0031 leave
"********* PRISM *************"
== disasm: #<ISeq:<compiled>@<compiled>:21 (21,0)-(21,36)>
0000 putstring ".oo" ( 21)[Li]
0002 setglobal :$pit
0004 putobject "\""
0006 getglobal :$pit
0008 dup
0009 objtostring <calldata!mid:to_s, argc:0, FCALL|ARGS_SIMPLE>
0011 anytostring
0012 putobject "\""
0014 toregexp 7, 3
0017 getglobal :$_
0019 send <calldata!mid:=~, argc:1, ARGS_SIMPLE>, nil
0022 branchunless 30
0024 jump 26
0026 putnil
0027 jump 31
0029 pop
0030 putnil
0031 leave
```
|
|
|
|
|
|
In some cases code may look like an attrset ID but should actually
return the value of the method, not the passed values.
In ruby/prism#2051 a flag was added when we have a attribute write call.
I used this flag to add the proper instructions when we have a real
attrset instead of using `rb_is_attrset_id` which was kind of hacky
anyway.
The value that should be returned in the newly added test is 42, not 2.
Previously the changes we had made returned 2.
Related to ruby/prism#1715
|
|
This commit passes an `end` to rb_int_parse_cstr which allows us
to correctly parse non-base 10 integers which are enclosed in
parenthesis. Prior to this commit, we were getting a putobject nil
when compiling `(0o0)` for example.
|
|
When you have an interpolated regex with a `once` flag and local
variable is outside the block created by the `once` flag, Prism would
see a segv. This is because it was not taking the depth into account.
To fix this, we need to add 1 to the `local_depth_offset` on the
`scope`.
Fixes: ruby/prism#2047
|
|
|
|
ForwardingSuperNodes need to actually forward any applicable
arguments. This commit implements that logic, by using the data
stored on the local iseq about the parameters to forward the
appropriate arguments.
|
|
If there are MultiTargetNodes within parameters, we need to
iterate over them and compile them individually correctly, once
the locals are all in the correct spaces. We need to add one
getlocal for the hidden variable, and then can recurse into the
MultiTargetNodes themselves
|
|
|
|
Prior to this commit, we were using `add_ensure_iseq` which compiled
a node as if it was a CRuby node. This commit defines
`pm_add_ensure_iseq` which compiles the Prism node appropriately.
|
|
The logic within the consequent for the CaseNodes in popped cases
was incorrect as it wouldn't emit consequent instructions for a
popped CaseNode. This commit fixes that.
|
|
Arguments that are passed as a hash need special consideration since in certain case they are not treated as keyword arguments. For example, if a call is passing `"a" => 1` as an argument, this will be turned into an implicit hash argument and not a keyword argument.
The existing compiler checks to see if all hash nodes can be treated as keyword arguments. If they can, then it will treat them as keyword arguments. If not, then it will treat them as implicit hash arguments.
This commit implements the same logic inside the Prism compiler.
|
|
This PR fixes ruby/prism#1963. Array and variable assignment was broken
for call nodes. The change checks if the `method_id` is related to
assignment and if is adds a `putnil`, `setn` and a `pop`.
The incorrect instructions meant that in some cases (demonstrated in
tests) the wrong value would be returned.
I verified that this fixes the test mentioned in the issue
(run: `RUBY_ISEQ_DUMP_DEBUG=prism make test/-ext-/st/test_numhash.rb`)
Incorrect instructions:
```
"********* Ruby *************"
== disasm: #<ISeq:<compiled>@<compiled>:1 (1,0)-(4,10)>
0000 putnil ( 4)[Li]
0001 putself
0002 send <calldata!mid:tbl, argc:0, FCALL|VCALL|ARGS_SIMPLE>, nil
0005 putself
0006 send <calldata!mid:i, argc:0, FCALL|VCALL|ARGS_SIMPLE>, nil
0009 putself
0010 send <calldata!mid:j, argc:0, FCALL|VCALL|ARGS_SIMPLE>, nil
0013 setn 3
0015 send <calldata!mid:[]=, argc:2, ARGS_SIMPLE>, nil
0018 pop
0019 leave
"********* PRISM *************"
== disasm: #<ISeq:<compiled>@<compiled>:3 (3,0)-(3,10)>
0000 putself ( 3)[Li]
0001 send <calldata!mid:tbl, argc:0, FCALL|VCALL|ARGS_SIMPLE>, nil
0004 putself
0005 send <calldata!mid:i, argc:0, FCALL|VCALL|ARGS_SIMPLE>, nil
0008 putself
0009 send <calldata!mid:j, argc:0, FCALL|VCALL|ARGS_SIMPLE>, nil
0012 send <calldata!mid:[]=, argc:2, ARGS_SIMPLE>, nil
0015 leave
```
Fixed instructions:
```
"********* Ruby *************"
== disasm: #<ISeq:<compiled>@<compiled>:1 (1,0)-(4,10)>
0000 putnil ( 4)[Li]
0001 putself
0002 send <calldata!mid:tbl, argc:0, FCALL|VCALL|ARGS_SIMPLE>, nil
0005 putself
0006 send <calldata!mid:i, argc:0, FCALL|VCALL|ARGS_SIMPLE>, nil
0009 putself
0010 send <calldata!mid:j, argc:0, FCALL|VCALL|ARGS_SIMPLE>, nil
0013 setn 3
0015 send <calldata!mid:[]=, argc:2, ARGS_SIMPLE>, nil
0018 pop
0019 leave
"********* PRISM *************"
== disasm: #<ISeq:<compiled>@<compiled>:3 (3,0)-(3,10)>
0000 putnil ( 3)[Li]
0001 putself
0002 send <calldata!mid:tbl, argc:0, FCALL|VCALL|ARGS_SIMPLE>, nil
0005 putself
0006 send <calldata!mid:i, argc:0, FCALL|VCALL|ARGS_SIMPLE>, nil
0009 putself
0010 send <calldata!mid:j, argc:0, FCALL|VCALL|ARGS_SIMPLE>, nil
0013 setn 3
0015 send <calldata!mid:[]=, argc:2, ARGS_SIMPLE>, nil
0018 pop
0019 leave
```
Fixes ruby/prism#1963
|
|
This commit supports all kinds of method calls (including methods with
parameters) inside `defined?` calls.
|
|
BreakNode, ReturnNode and NextNode all compile the ArgumentsNode
directly, but we weren't accounting for multiple arguments. If there
is more than one argument, we need to also emit a newarray
instruction to put the arguments onto the stack
|
|
|
|
Since ReturnNodes don't get popped, their arguments shouldn't either
|
|
In order for a break inside the rescue to have the correct jump target
|
|
Blocks should always look at their own local table first, even when
defined inside an ensure/rescue or something else that uses depth
offset. We can ignore the depth offset if we're doing local lookups
inside a block
|
|
This code is almost exactly the same (fixed variable names) as
what exists already in compile.c
|
|
Prior to this commit, we were not accounting for the case of a nil
parent in a CallXPathWriteNode, for example ::A ||= 1. This commit
checks if the parent exists, and if not, uses Object as the inferred
parent
|
|
Ruby code:
```ruby
defined? 1 in 1
```
Instructions:
```
"********* Ruby *************"
== disasm: #<ISeq:<compiled>@<compiled>:0 (0,0)-(0,16)>
0000 putobject "expression"
0002 leave
"********* PRISM *************"
== disasm: #<ISeq:<compiled>@<compiled>:0 (0,0)-(0,16)>
0000 putobject "expression"
0002 leave
```
|
|
Ruby code:
```ruby
defined? [a: [:b, :c]]
```
Instructions (without optimizations):
```
"********* Ruby *************"
== disasm: #<ISeq:<compiled>@<compiled>:0 (0,0)-(0,23)>
== catch table
| catch type: rescue st: 0001 ed: 0007 sp: 0000 cont: 0009
| == disasm: #<ISeq:defined guard in <compiled>@<compiled>:0 (0,0)-(-1,-1)>
| local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
| [ 1] "$!"@0
| 0000 putnil
| 0001 leave
|------------------------------------------------------------------------
0000 putnil
0001 putobject true
0003 branchunless 9
0005 putobject "expression"
0007 swap
0008 pop
0009 leave
"********* PRISM *************"
== disasm: #<ISeq:<compiled>@<compiled>:0 (0,0)-(0,23)>
== catch table
| catch type: rescue st: 0000 ed: 0009 sp: 0000 cont: 0009
| == disasm: #<ISeq:defined guard in <compiled>@<compiled>:0 (0,0)-(-1,-1)>
| local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
| [ 1] "$!"@0
| 0000 putnil
| 0001 leave
|------------------------------------------------------------------------
0000 putnil
0001 putobject true
0003 branchunless 9
0005 putobject "expression"
0007 swap
0008 pop
0009 leave
```
Instructions (with optimizations):
```
"********* Ruby *************"
== disasm: #<ISeq:<compiled>@<compiled>:0 (0,0)-(0,23)>
== catch table
| catch type: rescue st: 0001 ed: 0003 sp: 0000 cont: 0005
| == disasm: #<ISeq:defined guard in <compiled>@<compiled>:0 (0,0)-(-1,-1)>
| local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
| [ 1] "$!"@0
| 0000 putnil
| 0001 leave
|------------------------------------------------------------------------
0000 putnil
0001 putobject "expression"
0003 swap
0004 pop
0005 leave
"********* PRISM *************"
== disasm: #<ISeq:<compiled>@<compiled>:0 (0,0)-(0,23)>
== catch table
| catch type: rescue st: 0000 ed: 0005 sp: 0000 cont: 0005
| == disasm: #<ISeq:defined guard in <compiled>@<compiled>:0 (0,0)-(-1,-1)>
| local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
| [ 1] "$!"@0
| 0000 putnil
| 0001 leave
|------------------------------------------------------------------------
0000 putnil
0001 putobject "expression"
0003 swap
0004 pop
0005 leave
```
|
|
In an array for `defined?` we need to check if there is a
`contains_splat` flag, if so bail early.
Ruby code:
```ruby
defined?([[*1..2], 3, *4..5])
```
Instructions:
```
"********* Ruby *************"
== disasm: #<ISeq:<compiled>@<compiled>:0 (0,0)-(0,29)>
0000 putobject "expression"
0002 leave
"********* PRISM *************"
== disasm: #<ISeq:<compiled>@<compiled>:0 (0,0)-(0,29)>
0000 putobject "expression"
0002 leave
```
|
|
Ruby code:
```ruby
defined?(__LINE__)
```
Instructions:
```
"********* Ruby *************"
== disasm: #<ISeq:<compiled>@<compiled>:0 (0,0)-(0,18)>
0000 putobject "expression"
0002 leave
"********* PRISM *************"
== disasm: #<ISeq:<compiled>@<compiled>:0 (0,0)-(0,18)>
0000 putobject "expression"
0002 leave
```
|
|
Ruby code:
```ruby
defined?(__FILE__)
```
Instructions:
```
"********* Ruby *************"
== disasm: #<ISeq:<compiled>@<compiled>:0 (0,0)-(0,18)>
0000 putobject "expression"
0002 leave
"********* PRISM *************"
== disasm: #<ISeq:<compiled>@<compiled>:0 (0,0)-(0,18)>
0000 putobject "expression"
0002 leave
```
|
|
Ruby code:
```ruby
defined?(__ENCODING__)
```
Instructions:
```
"********* Ruby *************"
== disasm: #<ISeq:<compiled>@<compiled>:0 (0,0)-(0,22)>
0000 putobject "expression"
0002 leave
"********* PRISM *************"
== disasm: #<ISeq:<compiled>@<compiled>:0 (0,0)-(0,22)>
0000 putobject "expression"
0002 leave
```
|
|
Ruby Code:
```
defined?(1i)
```
Instructions:
```
"********* Ruby *************"
== disasm: #<ISeq:<compiled>@<compiled>:0 (0,0)-(0,12)>
0000 putobject "expression"
0002 leave
"********* PRISM *************"
== disasm: #<ISeq:<compiled>@<compiled>:0 (0,0)-(0,12)>
0000 putobject "expression"
0002 leave
```
|
|
|
|
|
|
|
|
Tests all the possible percent literal with `defined?`. Implements the
missing `PM_X_STRING_NODE` for the `%x` literal.
Code:
```ruby
defined?(%x[1,2,3])
```
```
"********* Ruby *************"
== disasm: #<ISeq:<compiled>@<compiled>:0 (0,0)-(0,19)>
0000 putobject "expression"
0002 leave
"********* PRISM *************"
== disasm: #<ISeq:<compiled>@<compiled>:0 (0,0)-(0,19)>
0000 putobject "expression"
0002 leave
```
|
|
Implements `PM_INTERPOLATED_REGULAR_EXPRESSION_NODE` for `defined?`
Code:
```ruby
defined?(/#{1}/)
```
```
"********* Ruby *************"
== disasm: #<ISeq:<compiled>@<compiled>:0 (0,0)-(0,16)>
0000 putobject "expression"
0002 leave
"********* PRISM *************"
== disasm: #<ISeq:<compiled>@<compiled>:0 (0,0)-(0,16)>
0000 putobject "expression"
0002 leave
```
|
|
Implements `PM_INTERPOLATED_STRING_NODE` for `defined?`
Code:
```ruby
defined?("#{expr}")
```
```
"********* Ruby *************"
== disasm: #<ISeq:<compiled>@<compiled>:0 (0,0)-(0,19)>
0000 putobject "expression"
0002 leave
"********* PRISM *************"
== disasm: #<ISeq:<compiled>@<compiled>:0 (0,0)-(0,19)>
0000 putobject "expression"
0002 leave
```
|
|
In #9101 I only accounted for an empty paren. This change implements the
`PM_PARENTHESES_NODE` for when it's `nil` and when it's an expression.
Code:
```ruby
defined?(("a"))
```
```
"********* Ruby *************"
== disasm: #<ISeq:<compiled>@<compiled>:0 (0,0)-(0,15)>
0000 putobject "expression"
0002 leave
"********* PRISM *************"
== disasm: #<ISeq:<compiled>@<compiled>:0 (0,0)-(0,15)>
0000 putobject "expression"
0002 leave
```
|
|
This PR implements `PM_BACK_REFERENCE_READ_NODE` and
`PM_NUMBERED_REFERENCE_READ_NODE` for `defined?`. The following now
works:
* `PM_NUMBERED_REFERENCE_READ_NODE`
```
defined? $1
defined? $2
```
Instructions:
```
"********* RUBY *************"
== disasm: #<ISeq:<compiled>@<compiled>:0 (0,0)-(0,12)>
0000 putnil
0001 defined ref, :$1, "global-variable"
0005 leave
"********* PRISM *************"
== disasm: #<ISeq:<compiled>@<compiled>:0 (0,0)-(0,12)>
0000 putnil
0001 defined ref, :$1, "global-variable"
0005 leave
```
* `PM_BACK_REFERENCE_READ_NODE`
```
defined? $'
defined? $`
defined? $&
```
Instructions:
```
"********* RUBY *************"
== disasm: #<ISeq:<compiled>@<compiled>:0 (0,0)-(0,12)>
0000 putnil
0001 defined ref, :$`, "global-variable"
0005 leave
"********* PRISM *************"
== disasm: #<ISeq:<compiled>@<compiled>:0 (0,0)-(0,12)>
0000 putnil
0001 defined ref, :$`, "global-variable"
0005 leave
```
|
|
This code was almost enitrely the same as the existing compiler's
code for its NextNode.
|
|
Implements and adds a test for passing a parentheses node to `defined?`.
|