| 1 | // Copyright (c) 2020-2024 Joe Conigliaro. All rights reserved. |
| 2 | // Use of this source code is governed by an MIT license |
| 3 | // that can be found in the LICENSE file. |
| 4 | // vtest build: macos |
| 5 | module builder |
| 6 | |
| 7 | import os |
| 8 | import v2.ast |
| 9 | import v2.parser |
| 10 | import v2.pref |
| 11 | import v2.token |
| 12 | |
| 13 | // Phase 2 regression: Parser.parse_files_to_flat must produce a FlatAst whose |
| 14 | // canonical signature is identical to ast.flatten_files(Parser.parse_files()). |
| 15 | // This guards the streaming path while downstream consumers are migrated. |
| 16 | |
| 17 | fn write_tmp_file(name string, src string) string { |
| 18 | path := '/tmp/v2_flat_stream_${os.getpid()}_${name}.v' |
| 19 | os.write_file(path, src) or { panic('write_file: ${err}') } |
| 20 | return path |
| 21 | } |
| 22 | |
| 23 | fn assert_streaming_matches(srcs map[string]string) { |
| 24 | mut paths := []string{cap: srcs.len} |
| 25 | for name, src in srcs { |
| 26 | paths << write_tmp_file(name, src) |
| 27 | } |
| 28 | defer { |
| 29 | for p in paths { |
| 30 | os.rm(p) or {} |
| 31 | } |
| 32 | } |
| 33 | |
| 34 | prefs := &pref.Preferences{} |
| 35 | |
| 36 | mut fs_batch := token.FileSet.new() |
| 37 | mut p_batch := parser.Parser.new(prefs) |
| 38 | legacy_files := p_batch.parse_files(paths, mut fs_batch) |
| 39 | flat_batch := ast.flatten_files(legacy_files) |
| 40 | |
| 41 | mut fs_stream := token.FileSet.new() |
| 42 | mut p_stream := parser.Parser.new(prefs) |
| 43 | flat_stream := p_stream.parse_files_to_flat(paths, mut fs_stream) |
| 44 | |
| 45 | sig_batch := flat_batch.signature() |
| 46 | sig_stream := flat_stream.signature() |
| 47 | if sig_batch != sig_stream { |
| 48 | mut diff_at := 0 |
| 49 | for diff_at < sig_batch.len && diff_at < sig_stream.len |
| 50 | && sig_batch[diff_at] == sig_stream[diff_at] { |
| 51 | diff_at++ |
| 52 | } |
| 53 | from := if diff_at > 80 { diff_at - 80 } else { 0 } |
| 54 | end_b := if diff_at + 200 < sig_batch.len { diff_at + 200 } else { sig_batch.len } |
| 55 | end_s := if diff_at + 200 < sig_stream.len { diff_at + 200 } else { sig_stream.len } |
| 56 | eprintln('signature mismatch at offset ${diff_at}') |
| 57 | eprintln('batch: ${sig_batch[from..end_b]}') |
| 58 | eprintln('stream: ${sig_stream[from..end_s]}') |
| 59 | } |
| 60 | assert sig_batch == sig_stream, 'streaming flat AST diverges from batch flatten' |
| 61 | } |
| 62 | |
| 63 | fn test_streaming_single_file() { |
| 64 | assert_streaming_matches({ |
| 65 | 'single': 'module main |
| 66 | |
| 67 | fn add(a int, b int) int { |
| 68 | return a + b |
| 69 | } |
| 70 | ' |
| 71 | }) |
| 72 | } |
| 73 | |
| 74 | fn test_streaming_multi_file_order() { |
| 75 | // Two files should produce node ids in parse order regardless of whether |
| 76 | // the conversion is batched or streamed. Strings interned by file 0 must |
| 77 | // be reused by file 1 in both cases. |
| 78 | a := 'module main |
| 79 | |
| 80 | pub const greeting = "hello" |
| 81 | |
| 82 | fn say() string { |
| 83 | return greeting |
| 84 | } |
| 85 | ' |
| 86 | b := 'module main |
| 87 | |
| 88 | pub struct Point { x int y int } |
| 89 | |
| 90 | fn (p Point) sum() int { return p.x + p.y } |
| 91 | ' |
| 92 | assert_streaming_matches({ |
| 93 | 'a': a |
| 94 | 'b': b |
| 95 | }) |
| 96 | } |
| 97 | |
| 98 | fn test_streaming_real_source_file() { |
| 99 | path := os.real_path('vlib/v2/ast/ast.v') |
| 100 | if !os.exists(path) { |
| 101 | return |
| 102 | } |
| 103 | prefs := &pref.Preferences{} |
| 104 | |
| 105 | mut fs_batch := token.FileSet.new() |
| 106 | mut p_batch := parser.Parser.new(prefs) |
| 107 | legacy := p_batch.parse_files([path], mut fs_batch) |
| 108 | flat_batch := ast.flatten_files(legacy) |
| 109 | |
| 110 | mut fs_stream := token.FileSet.new() |
| 111 | mut p_stream := parser.Parser.new(prefs) |
| 112 | flat_stream := p_stream.parse_files_to_flat([path], mut fs_stream) |
| 113 | |
| 114 | assert flat_batch.signature() == flat_stream.signature(), 'real-source streaming mismatch' |
| 115 | } |
| 116 | |
| 117 | fn test_default_flat_parallel_transform_keeps_user_files() { |
| 118 | path := write_tmp_file('parallel_transform_main', 'module main |
| 119 | |
| 120 | fn main() { |
| 121 | println("flat parallel ok") |
| 122 | } |
| 123 | ') |
| 124 | out_path := '/tmp/v2_flat_parallel_transform_${os.getpid()}.out' |
| 125 | defer { |
| 126 | os.rm(path) or {} |
| 127 | os.rm(out_path) or {} |
| 128 | } |
| 129 | cmd := '${os.quoted_path(@VEXE)} -v2 -nocache -o ${os.quoted_path(out_path)} ${os.quoted_path(path)} 2>&1' |
| 130 | res := os.execute(cmd) |
| 131 | assert res.exit_code == 0, res.output |
| 132 | run_res := os.execute(os.quoted_path(out_path)) |
| 133 | assert run_res.exit_code == 0, run_res.output |
| 134 | assert run_res.output.trim_space() == 'flat parallel ok', run_res.output |
| 135 | } |
| 136 | |
| 137 | fn test_default_parallel_transform_monomorphizes_generic_calls_for_native_backend() { |
| 138 | path := write_tmp_file('parallel_transform_generic_main', 'module main |
| 139 | |
| 140 | fn id[T](x T) T { |
| 141 | return x |
| 142 | } |
| 143 | |
| 144 | fn main() { |
| 145 | _ = id(41) |
| 146 | } |
| 147 | ') |
| 148 | out_path := '/tmp/v2_parallel_generic_transform_${os.getpid()}.out' |
| 149 | defer { |
| 150 | os.rm(path) or {} |
| 151 | os.rm(out_path) or {} |
| 152 | } |
| 153 | cmd := '${os.quoted_path(@VEXE)} -v2 -nocache -backend arm64 -gc none -o ${os.quoted_path(out_path)} ${os.quoted_path(path)} 2>&1' |
| 154 | res := os.execute(cmd) |
| 155 | assert !res.output.contains('_id_T_int'), res.output |
| 156 | assert res.exit_code == 0, res.output |
| 157 | } |
| 158 | |