From 8bd5cf064bb488e9f92986ab20ac30bd468ccf2e Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Tue, 21 Apr 2026 16:52:39 +0300 Subject: [PATCH] jsgen: fix js-backend cannot compile life.v (fixes #20667) --- vlib/builtin/js/builtin.js.v | 29 +++++++++++++++++++++++++++++ vlib/v/gen/js/jsgen_test.v | 12 ++++++++++++ 2 files changed, 41 insertions(+) diff --git a/vlib/builtin/js/builtin.js.v b/vlib/builtin/js/builtin.js.v index 7c24a1913..c24330f4d 100644 --- a/vlib/builtin/js/builtin.js.v +++ b/vlib/builtin/js/builtin.js.v @@ -2,6 +2,10 @@ module builtin // used to generate JS throw statements. +$if js_node { + #var $fs = require('fs'); +} + @[noreturn] pub fn js_throw(s any) { #throw s @@ -57,6 +61,31 @@ pub fn eprint(s string) { } } +// input_character gives back a single character, read from the standard input. +// It returns -1 on error (when the input is finished (EOF), on a broken pipe etc). +pub fn input_character() int { + $if js_node { + mut ch := -1 + #try { + #const read_buffer = Buffer.alloc(1) + #const nbytes = $fs.readSync(0, read_buffer, 0, 1, null) + #if (nbytes > 0) { ch.val = read_buffer[0] } + #} catch (e) {} + + return ch + } $else { + return -1 + } +} + +// print_character writes the single character `ch` to the standard output. +// It returns the written character value, or panics if the active JS runtime +// does not support stdout writes. +pub fn print_character(ch u8) int { + print(ch.ascii_str()) + return ch +} + // Exits the process in node, and halts execution in the browser // because `process.exit` is undefined. Workaround for not having // a 'real' way to exit in the browser. diff --git a/vlib/v/gen/js/jsgen_test.v b/vlib/v/gen/js/jsgen_test.v index 593acb871..91e90bfbf 100644 --- a/vlib/v/gen/js/jsgen_test.v +++ b/vlib/v/gen/js/jsgen_test.v @@ -76,6 +76,18 @@ fn test_issue_11379_js_browser_builtin_print_call_does_not_panic() { assert os.exists(output) } +fn test_issue_20667_js_can_compile_game_of_life_example() { + vexe := os.getenv('VEXE') + os.chdir(os.dir(vexe)) or {} + os.mkdir_all(output_dir) or { panic(err) } + program := os.join_path('examples', 'game_of_life', 'life.v') + output := os.join_path(output_dir, 'game_of_life.js') + res := + os.execute('${os.quoted_path(vexe)} -prod -b js -w -o ${os.quoted_path(output)} ${os.quoted_path(program)}') + assert res.exit_code == 0, res.output + assert os.exists(output) +} + fn find_test_files() []string { files := os.ls(test_dir) or { panic(err) } // The life example never exits, so tests would hang with it, skip -- 2.39.5