{% skip_file if flag?(:without_interpreter) %} require "../spec_helper" require "compiler/crystal/interpreter/*" def interpret(code, *, prelude = "primitives", file = __FILE__, line = __LINE__) if prelude == "primitives" context, value = interpret_with_context(code) context.loader?.try &.close_all value.value else interpret_in_separate_process(code, prelude, file: file, line: line) end end def interpret_with_context(code) repl = Crystal::Repl.new repl.prelude = "primitives" value = repl.run_code(code) {repl.context, value} end # FIXME: The following is a dirty hack to work around GC issues in interpreted programs. https://github.com/crystal-lang/crystal/issues/11602 # In a nutshell, `interpret_in_separate_process` below calls this same process with an extra option that causes # the interpretation of the code from stdin, reading the output from stdout. That string is used as the result of # the program being tested. class Spec::CLI def option_parser option_parser = previous_def option_parser.on("", "--interpret-code PRELUDE", "Execute interpreted code") do |prelude| code = STDIN.gets_to_end repl = Crystal::Repl.new repl.prelude = prelude print repl.run_code(code) exit end option_parser end end def interpret_in_separate_process(code, prelude, file = __FILE__, line = __LINE__) input = IO::Memory.new(code) output = IO::Memory.new error = IO::Memory.new executable = Process.executable_path || fail "Can't find executable path of current process" process = Process.new(executable, ["--interpret-code", prelude], input: input, output: output, error: error) status = process.wait unless status.success? fail error.rewind.gets_to_end + output.rewind.gets_to_end, file: file, line: line end output.rewind.gets_to_end end