Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/zjit-macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ jobs:
timeout-minutes: 60
env:
RUBY_TESTOPTS: '-q --tty=no'
EXCLUDES: '../src/test/.excludes-zjit'
TEST_BUNDLED_GEMS_ALLOW_FAILURES: ''
SYNTAX_SUGGEST_TIMEOUT: '5'
PRECHECK_BUNDLED_GEMS: 'no'
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/zjit-ubuntu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ jobs:
timeout-minutes: 90
env:
RUBY_TESTOPTS: '-q --tty=no'
EXCLUDES: '../src/test/.excludes-zjit'
TEST_BUNDLED_GEMS_ALLOW_FAILURES: ''
PRECHECK_BUNDLED_GEMS: 'no'
SYNTAX_SUGGEST_TIMEOUT: '5'
Expand Down
11 changes: 11 additions & 0 deletions bootstraptest/test_yjit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5369,3 +5369,14 @@ class << self

RESULT.any?
}

# throw and String#dup with GC stress
assert_equal 'foo', %{
GC.stress = true

def foo
1.times { return "foo".dup }
end

10.times.map { foo.dup }.last
}
2 changes: 1 addition & 1 deletion lib/prism/polyfill/scan_byte.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
require "strscan"

# Polyfill for StringScanner#scan_byte, which didn't exist until Ruby 3.4.
if !(StringScanner.instance_methods.include?(:scan_byte))
if !(StringScanner.method_defined?(:scan_byte))
StringScanner.include(
Module.new {
def scan_byte # :nodoc:
Expand Down
5 changes: 3 additions & 2 deletions prism/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@ errors:
- UNEXPECTED_INDEX_KEYWORDS
- UNEXPECTED_LABEL
- UNEXPECTED_MULTI_WRITE
- UNEXPECTED_PARAMETER_DEFAULT_VALUE
- UNEXPECTED_RANGE_OPERATOR
- UNEXPECTED_SAFE_NAVIGATION
- UNEXPECTED_TOKEN_CLOSE_CONTEXT
Expand Down Expand Up @@ -358,6 +359,8 @@ tokens:
comment: "a newline character outside of other tokens"
- name: PARENTHESIS_RIGHT
comment: ")"
- name: PIPE
comment: "|"
- name: SEMICOLON
comment: ";"
# Tokens from here on are not used for lookup, and can be in any order.
Expand Down Expand Up @@ -591,8 +594,6 @@ tokens:
comment: "%I"
- name: PERCENT_UPPER_W
comment: "%W"
- name: PIPE
comment: "|"
- name: PIPE_EQUAL
comment: "|="
- name: PIPE_PIPE
Expand Down
3 changes: 3 additions & 0 deletions prism/parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,9 @@ typedef enum {
/** a rescue else statement within a do..end block */
PM_CONTEXT_BLOCK_ELSE,

/** expressions in block parameters `foo do |...| end ` */
PM_CONTEXT_BLOCK_PARAMETERS,

/** a rescue statement within a do..end block */
PM_CONTEXT_BLOCK_RESCUE,

Expand Down
18 changes: 18 additions & 0 deletions prism/prism.c
Original file line number Diff line number Diff line change
Expand Up @@ -8606,6 +8606,7 @@ static const uint32_t context_terminators[] = {
[PM_CONTEXT_BLOCK_KEYWORDS] = (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ENSURE),
[PM_CONTEXT_BLOCK_ENSURE] = (1U << PM_TOKEN_KEYWORD_END),
[PM_CONTEXT_BLOCK_ELSE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_END),
[PM_CONTEXT_BLOCK_PARAMETERS] = (1U << PM_TOKEN_PIPE),
[PM_CONTEXT_BLOCK_RESCUE] = (1U << PM_TOKEN_KEYWORD_ENSURE) | (1U << PM_TOKEN_KEYWORD_RESCUE) | (1U << PM_TOKEN_KEYWORD_ELSE) | (1U << PM_TOKEN_KEYWORD_END),
[PM_CONTEXT_CASE_WHEN] = (1U << PM_TOKEN_KEYWORD_WHEN) | (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_ELSE),
[PM_CONTEXT_CASE_IN] = (1U << PM_TOKEN_KEYWORD_IN) | (1U << PM_TOKEN_KEYWORD_END) | (1U << PM_TOKEN_KEYWORD_ELSE),
Expand Down Expand Up @@ -8756,6 +8757,7 @@ context_human(pm_context_t context) {
case PM_CONTEXT_BEGIN: return "begin statement";
case PM_CONTEXT_BLOCK_BRACES: return "'{'..'}' block";
case PM_CONTEXT_BLOCK_KEYWORDS: return "'do'..'end' block";
case PM_CONTEXT_BLOCK_PARAMETERS: return "'|'..'|' block parameter";
case PM_CONTEXT_CASE_WHEN: return "'when' clause";
case PM_CONTEXT_CASE_IN: return "'in' clause";
case PM_CONTEXT_CLASS: return "class definition";
Expand Down Expand Up @@ -15357,6 +15359,9 @@ parse_block_parameters(
) {
pm_parameters_node_t *parameters = NULL;
if (!match1(parser, PM_TOKEN_SEMICOLON)) {
if (!is_lambda_literal) {
context_push(parser, PM_CONTEXT_BLOCK_PARAMETERS);
}
parameters = parse_parameters(
parser,
is_lambda_literal ? PM_BINDING_POWER_DEFINED : PM_BINDING_POWER_INDEX,
Expand All @@ -15367,6 +15372,9 @@ parse_block_parameters(
true,
(uint16_t) (depth + 1)
);
if (!is_lambda_literal) {
context_pop(parser);
}
}

pm_block_parameters_node_t *block_parameters = pm_block_parameters_node_create(parser, parameters, opening);
Expand Down Expand Up @@ -15722,6 +15730,7 @@ parse_return(pm_parser_t *parser, pm_node_t *node) {
case PM_CONTEXT_BLOCK_ENSURE:
case PM_CONTEXT_BLOCK_KEYWORDS:
case PM_CONTEXT_BLOCK_RESCUE:
case PM_CONTEXT_BLOCK_PARAMETERS:
case PM_CONTEXT_DEF_ELSE:
case PM_CONTEXT_DEF_ENSURE:
case PM_CONTEXT_DEF_PARAMS:
Expand Down Expand Up @@ -15758,6 +15767,7 @@ parse_block_exit(pm_parser_t *parser, pm_node_t *node) {
case PM_CONTEXT_BLOCK_KEYWORDS:
case PM_CONTEXT_BLOCK_ELSE:
case PM_CONTEXT_BLOCK_ENSURE:
case PM_CONTEXT_BLOCK_PARAMETERS:
case PM_CONTEXT_BLOCK_RESCUE:
case PM_CONTEXT_DEFINED:
case PM_CONTEXT_FOR:
Expand Down Expand Up @@ -17992,6 +18002,7 @@ parse_retry(pm_parser_t *parser, const pm_node_t *node) {
case PM_CONTEXT_BEGIN:
case PM_CONTEXT_BLOCK_BRACES:
case PM_CONTEXT_BLOCK_KEYWORDS:
case PM_CONTEXT_BLOCK_PARAMETERS:
case PM_CONTEXT_CASE_IN:
case PM_CONTEXT_CASE_WHEN:
case PM_CONTEXT_DEFAULT_PARAMS:
Expand Down Expand Up @@ -18072,6 +18083,7 @@ parse_yield(pm_parser_t *parser, const pm_node_t *node) {
case PM_CONTEXT_BLOCK_KEYWORDS:
case PM_CONTEXT_BLOCK_ELSE:
case PM_CONTEXT_BLOCK_ENSURE:
case PM_CONTEXT_BLOCK_PARAMETERS:
case PM_CONTEXT_BLOCK_RESCUE:
case PM_CONTEXT_CASE_IN:
case PM_CONTEXT_CASE_WHEN:
Expand Down Expand Up @@ -19635,6 +19647,12 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
if (!accept_endless_def) {
pm_parser_err_previous(parser, PM_ERR_DEF_ENDLESS_PARAMETERS);
}
if (
parser->current_context->context == PM_CONTEXT_DEFAULT_PARAMS &&
parser->current_context->prev->context == PM_CONTEXT_BLOCK_PARAMETERS
) {
PM_PARSER_ERR_FORMAT(parser, def_keyword.start, parser->previous.end, PM_ERR_UNEXPECTED_PARAMETER_DEFAULT_VALUE, "endless method definition");
}
equal = parser->previous;

context_push(parser, PM_CONTEXT_DEF);
Expand Down
1 change: 1 addition & 0 deletions prism/templates/src/diagnostic.c.erb
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,7 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_MAX] = {
[PM_ERR_UNEXPECTED_INDEX_KEYWORDS] = { "unexpected keyword arg given in index assignment; keywords are not allowed in index assignment expressions", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_UNEXPECTED_LABEL] = { "unexpected label", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_UNEXPECTED_MULTI_WRITE] = { "unexpected multiple assignment; multiple assignment is not allowed in this context", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_UNEXPECTED_PARAMETER_DEFAULT_VALUE] = { "unexpected %s; expected a default value for a parameter", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_UNEXPECTED_RANGE_OPERATOR] = { "unexpected range operator; .. and ... are non-associative and cannot be chained", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_UNEXPECTED_SAFE_NAVIGATION] = { "&. inside multiple assignment destination", PM_ERROR_LEVEL_SYNTAX },
[PM_ERR_UNEXPECTED_TOKEN_CLOSE_CONTEXT] = { "unexpected %s, assuming it is closing the parent %s", PM_ERROR_LEVEL_SYNTAX },
Expand Down
1 change: 1 addition & 0 deletions test/.excludes-zjit/TestResolvDNS.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
exclude(:test_multiple_servers_with_timeout_and_truncated_tcp_fallback, 'randomly crashes on Thread#value, showing up as Timeout') # https://github.com/Shopify/ruby/issues/852
5 changes: 5 additions & 0 deletions test/prism/errors/block_args_with_endless_def.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
p do |a = def f = 1; b| end
^~~~~~~ unexpected endless method definition; expected a default value for a parameter
p do |a = def f = 1| 2; b|c end
^~~~~~~ unexpected endless method definition; expected a default value for a parameter

11 changes: 11 additions & 0 deletions test/prism/fixtures/endless_method_as_default_arg.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
def foo(a = def f = 1); end

def foo(a = def f = 1, b); end

def foo(b, a = def f = 1); end

def foo(a: def f = 1); end

def foo(a = def f = 1+2); end

->(a = def f = 1) {}
38 changes: 37 additions & 1 deletion test/ruby/test_zjit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -498,11 +498,47 @@ def entry(arr) = test(*arr)
def test_send_kwarg
assert_runs '[1, 2]', %q{
def test(a:, b:) = [a, b]
def entry = test(a: 1, b: 2)
def entry = test(b: 2, a: 1) # change order
entry
}
end

def test_send_kwarg_optional
assert_compiles '[1, 2]', %q{
def test(a: 1, b: 2) = [a, b]
def entry = test
entry
entry
}, call_threshold: 2
end

def test_send_kwarg_required_and_optional
assert_compiles '[3, 2]', %q{
def test(a:, b: 2) = [a, b]
def entry = test(a: 3)
entry
entry
}, call_threshold: 2
end

def test_send_kwarg_to_hash
assert_compiles '{a: 3}', %q{
def test(hash) = hash
def entry = test(a: 3)
entry
entry
}, call_threshold: 2
end

def test_send_kwrest
assert_compiles '{a: 3}', %q{
def test(**kwargs) = kwargs
def entry = test(a: 3)
entry
entry
}, call_threshold: 2
end

def test_send_ccall_variadic_with_different_receiver_classes
assert_compiles '[true, true]', %q{
def test(obj) = obj.start_with?("a")
Expand Down
6 changes: 4 additions & 2 deletions yjit/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6168,7 +6168,7 @@ fn jit_rb_str_dup(

jit_prepare_call_with_gc(jit, asm);

let recv_opnd = asm.stack_pop(1);
let recv_opnd = asm.stack_opnd(0);
let recv_opnd = asm.load(recv_opnd);

let shape_id_offset = unsafe { rb_shape_id_offset() };
Expand All @@ -6177,8 +6177,10 @@ fn jit_rb_str_dup(
asm.jnz(Target::side_exit(Counter::send_str_dup_exivar));

// Call rb_str_dup
let stack_ret = asm.stack_push(Type::CString);
let ret_opnd = asm.ccall(rb_str_dup as *const u8, vec![recv_opnd]);

asm.stack_pop(1);
let stack_ret = asm.stack_push(Type::CString);
asm.mov(stack_ret, ret_opnd);

true
Expand Down