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
4 changes: 2 additions & 2 deletions include/ruby/internal/core/rdata.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,8 @@ struct RData {
RUBY_DATA_FUNC dfree;

/** Pointer to the actual C level struct that you want to wrap.
* This is in between dmark and dfree to allow DATA_PTR to continue
* to work for both RData and non-embedded RTypedData.
* This is after dmark and dfree to allow DATA_PTR to continue to work for
* both RData and non-embedded RTypedData.
*/
void *data;
};
Expand Down
3 changes: 3 additions & 0 deletions include/ruby/internal/core/rtypeddata.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include "ruby/internal/dllexport.h"
#include "ruby/internal/error.h"
#include "ruby/internal/fl_type.h"
#include "ruby/internal/static_assert.h"
#include "ruby/internal/stdbool.h"
#include "ruby/internal/value_type.h"

Expand Down Expand Up @@ -374,6 +375,8 @@ struct RTypedData {
void *data;
};

RBIMPL_STATIC_ASSERT(data_in_rtypeddata, offsetof(struct RData, data) == offsetof(struct RTypedData, data));

RBIMPL_SYMBOL_EXPORT_BEGIN()
RBIMPL_ATTR_NONNULL((3))
/**
Expand Down
31 changes: 16 additions & 15 deletions lib/prism/translation/parser/lexer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -277,20 +277,20 @@ def to_a
when :tCOMMENT
if token.type == :EMBDOC_BEGIN

while !((next_token = lexed[index][0]) && next_token.type == :EMBDOC_END) && (index < length - 1)
while !((next_token = lexed[index]&.first) && next_token.type == :EMBDOC_END) && (index < length - 1)
value += next_token.value
index += 1
end

value += next_token.value
location = range(token.location.start_offset, lexed[index][0].location.end_offset)
location = range(token.location.start_offset, next_token.location.end_offset)
index += 1
else
is_at_eol = value.chomp!.nil?
location = range(token.location.start_offset, token.location.end_offset + (is_at_eol ? 0 : -1))

prev_token = lexed[index - 2][0] if index - 2 >= 0
next_token = lexed[index][0]
prev_token, _ = lexed[index - 2] if index - 2 >= 0
next_token, _ = lexed[index]

is_inline_comment = prev_token&.location&.start_line == token.location.start_line
if is_inline_comment && !is_at_eol && !COMMENT_CONTINUATION_TYPES.include?(next_token&.type)
Expand All @@ -309,7 +309,7 @@ def to_a
end
end
when :tNL
next_token = next_token = lexed[index][0]
next_token, _ = lexed[index]
# Newlines after comments are emitted out of order.
if next_token&.type == :COMMENT
comment_newline_location = location
Expand Down Expand Up @@ -346,8 +346,8 @@ def to_a
location = range(token.location.start_offset, token.location.start_offset + percent_array_leading_whitespace(value))
value = nil
when :tSTRING_BEG
next_token = lexed[index][0]
next_next_token = lexed[index + 1][0]
next_token, _ = lexed[index]
next_next_token, _ = lexed[index + 1]
basic_quotes = value == '"' || value == "'"

if basic_quotes && next_token&.type == :STRING_END
Expand Down Expand Up @@ -415,7 +415,8 @@ def to_a
while token.type == :STRING_CONTENT
current_length += token.value.bytesize
# Heredoc interpolation can have multiple STRING_CONTENT nodes on the same line.
is_first_token_on_line = lexed[index - 1] && token.location.start_line != lexed[index - 2][0].location&.start_line
prev_token, _ = lexed[index - 2] if index - 2 >= 0
is_first_token_on_line = prev_token && token.location.start_line != prev_token.location.start_line
# The parser gem only removes indentation when the heredoc is not nested
not_nested = heredoc_stack.size == 1
if is_percent_array
Expand All @@ -434,7 +435,7 @@ def to_a
tokens << [:tSTRING_CONTENT, [current_string, range(start_offset, start_offset + current_length)]]
break
end
token = lexed[index][0]
token, _ = lexed[index]
index += 1
end
else
Expand Down Expand Up @@ -489,7 +490,7 @@ def to_a
end

if percent_array?(quote_stack.pop)
prev_token = lexed[index - 2][0] if index - 2 >= 0
prev_token, _ = lexed[index - 2] if index - 2 >= 0
empty = %i[PERCENT_LOWER_I PERCENT_LOWER_W PERCENT_UPPER_I PERCENT_UPPER_W].include?(prev_token&.type)
ends_with_whitespace = prev_token&.type == :WORDS_SEP
# parser always emits a space token after content in a percent array, even if no actual whitespace is present.
Expand All @@ -498,7 +499,7 @@ def to_a
end
end
when :tSYMBEG
if (next_token = lexed[index][0]) && next_token.type != :STRING_CONTENT && next_token.type != :EMBEXPR_BEGIN && next_token.type != :EMBVAR && next_token.type != :STRING_END
if (next_token = lexed[index]&.first) && next_token.type != :STRING_CONTENT && next_token.type != :EMBEXPR_BEGIN && next_token.type != :EMBVAR && next_token.type != :STRING_END
next_location = token.location.join(next_token.location)
type = :tSYMBOL
value = next_token.value
Expand All @@ -513,13 +514,13 @@ def to_a
type = :tIDENTIFIER
end
when :tXSTRING_BEG
if (next_token = lexed[index][0]) && !%i[STRING_CONTENT STRING_END EMBEXPR_BEGIN].include?(next_token.type)
if (next_token = lexed[index]&.first) && !%i[STRING_CONTENT STRING_END EMBEXPR_BEGIN].include?(next_token.type)
# self.`()
type = :tBACK_REF2
end
quote_stack.push(value)
when :tSYMBOLS_BEG, :tQSYMBOLS_BEG, :tWORDS_BEG, :tQWORDS_BEG
if (next_token = lexed[index][0]) && next_token.type == :WORDS_SEP
if (next_token = lexed[index]&.first) && next_token.type == :WORDS_SEP
index += 1
end

Expand Down Expand Up @@ -595,9 +596,9 @@ def calculate_heredoc_whitespace(heredoc_token_index)
previous_line = -1
result = Float::MAX

while (lexed[next_token_index] && next_token = lexed[next_token_index][0])
while (next_token = lexed[next_token_index]&.first)
next_token_index += 1
next_next_token = lexed[next_token_index] && lexed[next_token_index][0]
next_next_token, _ = lexed[next_token_index]
first_token_on_line = next_token.location.start_column == 0

# String content inside nested heredocs and interpolation is ignored
Expand Down
2 changes: 1 addition & 1 deletion test/io/wait/test_io_wait.rb
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def test_wait_eof
ret = nil
assert_nothing_raised(Timeout::Error) do
q.push(true)
t = EnvUtil.apply_timeout_scale(0.1)
t = EnvUtil.apply_timeout_scale(1)
Timeout.timeout(t) { ret = @r.wait }
end
assert_equal @r, ret
Expand Down
16 changes: 16 additions & 0 deletions test/prism/ruby/parser_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,22 @@ def test_current_parser_for_current_ruby
end
end

def test_invalid_syntax
code = <<~RUBY
foo do
case bar
when
end
end
RUBY
buffer = Parser::Source::Buffer.new("(string)")
buffer.source = code

parser = Prism::Translation::Parser33.new
parser.diagnostics.all_errors_are_fatal = true
assert_raise(Parser::SyntaxError) { parser.tokenize(buffer) }
end

def test_it_block_parameter_syntax
it_fixture_path = Pathname(__dir__).join("../../../test/prism/fixtures/it.txt")

Expand Down
2 changes: 1 addition & 1 deletion test/ruby/test_fiber.rb
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,7 @@ def test_create_fiber_in_new_thread
end

def test_machine_stack_gc
assert_normal_exit <<-RUBY, '[Bug #14561]', timeout: 10
assert_normal_exit <<-RUBY, '[Bug #14561]', timeout: 60
enum = Enumerator.new { |y| y << 1 }
thread = Thread.new { enum.peek }
thread.join
Expand Down
2 changes: 1 addition & 1 deletion test/ruby/test_gc.rb
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ def test_stat_heap_constraints
end

def test_measure_total_time
assert_separately([], __FILE__, __LINE__, <<~RUBY)
assert_separately([], __FILE__, __LINE__, <<~RUBY, timeout: 60)
GC.measure_total_time = false

time_before = GC.stat(:time)
Expand Down
1 change: 1 addition & 0 deletions test/ruby/test_transcode.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2343,6 +2343,7 @@ def test_ractor_lazy_load_encoding
end

def test_ractor_lazy_load_encoding_random
omit 'unstable on s390x and windows' if RUBY_PLATFORM =~ /s390x|mswin/
assert_ractor("#{<<~"begin;"}\n#{<<~'end;'}")
begin;
rs = []
Expand Down
2 changes: 1 addition & 1 deletion zjit/src/backend/arm64/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1372,7 +1372,7 @@ impl Assembler
pub fn compile_with_regs(self, cb: &mut CodeBlock, regs: Vec<Reg>) -> Option<(CodePtr, Vec<CodePtr>)> {
let asm = self.arm64_split();
let mut asm = asm.alloc_regs(regs)?;
asm.compile_side_exits()?;
asm.compile_side_exits();

// Create label instances in the code block
for (idx, name) in asm.label_names.iter().enumerate() {
Expand Down
60 changes: 24 additions & 36 deletions zjit/src/backend/lir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,14 +256,6 @@ impl From<VALUE> for Opnd {
}
}

/// Set of things we need to restore for side exits.
#[derive(Clone, Debug)]
pub struct SideExitContext {
pub pc: *const VALUE,
pub stack: Vec<Opnd>,
pub locals: Vec<Opnd>,
}

/// Branch target (something that we can jump to)
/// for branch instructions
#[derive(Clone, Debug)]
Expand All @@ -275,9 +267,9 @@ pub enum Target
Label(Label),
/// Side exit to the interpreter
SideExit {
/// Context to restore on regular side exits. None for side exits right
/// after JIT-to-JIT calls because we restore them before the JIT call.
context: Option<SideExitContext>,
pc: *const VALUE,
stack: Vec<Opnd>,
locals: Vec<Opnd>,
/// We use this to enrich asm comments.
reason: SideExitReason,
/// Some if the side exit should write this label. We use it for patch points.
Expand Down Expand Up @@ -761,7 +753,7 @@ impl<'a> Iterator for InsnOpndIterator<'a> {
Insn::Label(target) |
Insn::LeaJumpTarget { target, .. } |
Insn::PatchPoint(target) => {
if let Target::SideExit { context: Some(SideExitContext { stack, locals, .. }), .. } = target {
if let Target::SideExit { stack, locals, .. } = target {
let stack_idx = self.idx;
if stack_idx < stack.len() {
let opnd = &stack[stack_idx];
Expand All @@ -786,7 +778,7 @@ impl<'a> Iterator for InsnOpndIterator<'a> {
return Some(opnd);
}

if let Target::SideExit { context: Some(SideExitContext { stack, locals, .. }), .. } = target {
if let Target::SideExit { stack, locals, .. } = target {
let stack_idx = self.idx - 1;
if stack_idx < stack.len() {
let opnd = &stack[stack_idx];
Expand Down Expand Up @@ -917,7 +909,7 @@ impl<'a> InsnOpndMutIterator<'a> {
Insn::Label(target) |
Insn::LeaJumpTarget { target, .. } |
Insn::PatchPoint(target) => {
if let Target::SideExit { context: Some(SideExitContext { stack, locals, .. }), .. } = target {
if let Target::SideExit { stack, locals, .. } = target {
let stack_idx = self.idx;
if stack_idx < stack.len() {
let opnd = &mut stack[stack_idx];
Expand All @@ -942,7 +934,7 @@ impl<'a> InsnOpndMutIterator<'a> {
return Some(opnd);
}

if let Target::SideExit { context: Some(SideExitContext { stack, locals, .. }), .. } = target {
if let Target::SideExit { stack, locals, .. } = target {
let stack_idx = self.idx - 1;
if stack_idx < stack.len() {
let opnd = &mut stack[stack_idx];
Expand Down Expand Up @@ -1555,8 +1547,7 @@ impl Assembler
}

/// Compile Target::SideExit and convert it into Target::CodePtr for all instructions
#[must_use]
pub fn compile_side_exits(&mut self) -> Option<()> {
pub fn compile_side_exits(&mut self) {
let mut targets = HashMap::new();
for (idx, insn) in self.insns.iter().enumerate() {
if let Some(target @ Target::SideExit { .. }) = insn.target() {
Expand All @@ -1567,7 +1558,7 @@ impl Assembler
for (idx, target) in targets {
// Compile a side exit. Note that this is past the split pass and alloc_regs(),
// so you can't use a VReg or an instruction that needs to be split.
if let Target::SideExit { context, reason, label } = target {
if let Target::SideExit { pc, stack, locals, reason, label } = target {
asm_comment!(self, "Exit: {reason}");
let side_exit_label = if let Some(label) = label {
Target::Label(label)
Expand All @@ -1578,26 +1569,24 @@ impl Assembler

// Restore the PC and the stack for regular side exits. We don't do this for
// side exits right after JIT-to-JIT calls, which restore them before the call.
if let Some(SideExitContext { pc, stack, locals }) = context {
asm_comment!(self, "write stack slots: {stack:?}");
for (idx, &opnd) in stack.iter().enumerate() {
self.store(Opnd::mem(64, SP, idx as i32 * SIZEOF_VALUE_I32), opnd);
}
asm_comment!(self, "write stack slots: {stack:?}");
for (idx, &opnd) in stack.iter().enumerate() {
self.store(Opnd::mem(64, SP, idx as i32 * SIZEOF_VALUE_I32), opnd);
}

asm_comment!(self, "write locals: {locals:?}");
for (idx, &opnd) in locals.iter().enumerate() {
self.store(Opnd::mem(64, SP, (-local_size_and_idx_to_ep_offset(locals.len(), idx) - 1) * SIZEOF_VALUE_I32), opnd);
}
asm_comment!(self, "write locals: {locals:?}");
for (idx, &opnd) in locals.iter().enumerate() {
self.store(Opnd::mem(64, SP, (-local_size_and_idx_to_ep_offset(locals.len(), idx) - 1) * SIZEOF_VALUE_I32), opnd);
}

asm_comment!(self, "save cfp->pc");
self.load_into(Opnd::Reg(Assembler::SCRATCH_REG), Opnd::const_ptr(pc));
self.store(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_PC), Opnd::Reg(Assembler::SCRATCH_REG));
asm_comment!(self, "save cfp->pc");
self.load_into(Opnd::Reg(Assembler::SCRATCH_REG), Opnd::const_ptr(pc));
self.store(Opnd::mem(64, CFP, RUBY_OFFSET_CFP_PC), Opnd::Reg(Assembler::SCRATCH_REG));

asm_comment!(self, "save cfp->sp");
self.lea_into(Opnd::Reg(Assembler::SCRATCH_REG), Opnd::mem(64, SP, stack.len() as i32 * SIZEOF_VALUE_I32));
let cfp_sp = Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SP);
self.store(cfp_sp, Opnd::Reg(Assembler::SCRATCH_REG));
}
asm_comment!(self, "save cfp->sp");
self.lea_into(Opnd::Reg(Assembler::SCRATCH_REG), Opnd::mem(64, SP, stack.len() as i32 * SIZEOF_VALUE_I32));
let cfp_sp = Opnd::mem(64, CFP, RUBY_OFFSET_CFP_SP);
self.store(cfp_sp, Opnd::Reg(Assembler::SCRATCH_REG));

asm_comment!(self, "exit to the interpreter");
self.frame_teardown(&[]); // matching the setup in :bb0-prologue:
Expand All @@ -1607,7 +1596,6 @@ impl Assembler
*self.insns[idx].target_mut().unwrap() = side_exit_label;
}
}
Some(())
}
}

Expand Down
2 changes: 1 addition & 1 deletion zjit/src/backend/x86_64/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -895,7 +895,7 @@ impl Assembler
pub fn compile_with_regs(self, cb: &mut CodeBlock, regs: Vec<Reg>) -> Option<(CodePtr, Vec<CodePtr>)> {
let asm = self.x86_split();
let mut asm = asm.alloc_regs(regs)?;
asm.compile_side_exits()?;
asm.compile_side_exits();

// Create label instances in the code block
for (idx, name) in asm.label_names.iter().enumerate() {
Expand Down
Loading