Fix stdout.print/stderr.print panicking

stdout.print and stderr.print perform two writes: one for the message,
and one for a newline. The newline write was not wrapped in a "try"
expression, causing any exceptions thrown to go unnoticed.

Sending messages to optional types inside a "try" was also broken. When
doing so, the VM would jump to the incorrect instruction and effectively
ignore the "else" clause; bubbling up the error instead. This is fixed
by having the compiler generate different code: instead of jumping to
the next block (whatever that may be), we jump to a specific block.

As part of this the signature of stdout.print/stderr.print is also
changed. Instead of the argument being typed as `?ToString`, it's now a
`ToString` with an empty String as the default value.

This fixes #199
parent cc20ee2a
Pipeline #157607468 passed with stages
in 71 minutes and 24 seconds
......@@ -111,6 +111,7 @@ require 'inkoc/tir/instruction/get_local'
require 'inkoc/tir/instruction/get_parent_local'
require 'inkoc/tir/instruction/set_parent_local'
require 'inkoc/tir/instruction/goto_next_block_if_true'
require 'inkoc/tir/instruction/goto_block_if_true'
require 'inkoc/tir/instruction/skip_next_block'
require 'inkoc/tir/instruction/local_exists'
require 'inkoc/tir/instruction/return'
......
......@@ -98,6 +98,13 @@ module Inkoc
compiled_code.instruct(:GotoIfTrue, [index, register], tir_ins.location)
end
def on_goto_block_if_true(tir_ins, compiled_code, basic_block)
index = tir_ins.block.instruction_offset
register = tir_ins.register.id
compiled_code.instruct(:GotoIfTrue, [index, register], tir_ins.location)
end
def on_skip_next_block(tir_ins, compiled_code, basic_block)
index = basic_block.next.next.instruction_offset
......
......@@ -1645,7 +1645,9 @@ module Inkoc
# Look up the "unknown_message" block if the initial block was not
# found.
body.instruct(:GotoNextBlockIfTrue, block_reg, loc)
goto_block = body.new_basic_block
body.instruct(:GotoBlockIfTrue, block_reg, goto_block, loc)
body.instruct(:Binary, :GetAttribute, block_reg, rec, alt_name_reg, loc)
# Store all the arguments passed in the array and execute the
......@@ -1666,7 +1668,7 @@ module Inkoc
body.instruct(:SkipNextBlock, loc)
# The code we'd run if the method _is_ defined.
body.add_connected_basic_block
body.push_connected_basic_block(goto_block)
body.instruct(
:RunBlockWithReceiver,
......
......@@ -111,10 +111,7 @@ module Inkoc
end
def add_connected_basic_block(*args)
block = new_basic_block(*args)
current_block&.next = block
push_basic_block(block)
push_connected_basic_block(new_basic_block(*args))
end
def push_basic_block(block)
......@@ -123,6 +120,14 @@ module Inkoc
block
end
def push_connected_basic_block(block)
current_block&.next = block
@blocks << block
block
end
def new_basic_block(name = @blocks.length.to_s, *args)
BasicBlock.new(name, *args)
end
......
# frozen_string_literal: true
module Inkoc
module TIR
module Instruction
class GotoBlockIfTrue
include Predicates
include Inspect
attr_reader :register, :block, :location
# register - The virtual register containing the condition to evaluate.
# block - The block to jump to.
# location - The SourceLocation of this instruction.
def initialize(register, block, location)
@register = register
@block = block
@location = location
end
def visitor_method
:on_goto_block_if_true
end
end
end
end
end
......@@ -56,10 +56,11 @@ impl Write for ThisModule {
# import std::stdio::stderr
#
# stderr.print # => Nil
def print(data: ?ToString = Nil) -> Integer {
def print(data: ToString = '') -> Integer {
process.blocking {
let written = try _INKOC.stderr_write(data.to_string) else return 0
written + _INKOC.stderr_write(NEWLINE)
written + try _INKOC.stderr_write(NEWLINE) else 0
}
}
......
......@@ -57,10 +57,11 @@ impl Write for ThisModule {
# import std::stdio::stdout
#
# stdout.print # => Nil
def print(data: ?ToString = Nil) -> Integer {
def print(data: ToString = '') -> Integer {
process.blocking {
let written = try _INKOC.stdout_write(data.to_string) else return 0
written + _INKOC.stdout_write(NEWLINE)
written + try _INKOC.stdout_write(NEWLINE) else 0
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment