mt-lang 0.3.8 → 0.3.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (200) hide show
  1. checksums.yaml +4 -4
  2. data/lib/milk_tea/base.rb +1 -2
  3. data/lib/milk_tea/bindings/bindgen/ast_parser.rb +0 -2
  4. data/lib/milk_tea/bindings/bindgen/declaration.rb +0 -2
  5. data/lib/milk_tea/bindings/bindgen/emitter.rb +0 -2
  6. data/lib/milk_tea/bindings/bindgen/overrides.rb +0 -2
  7. data/lib/milk_tea/bindings/bindgen/type_mapper.rb +51 -53
  8. data/lib/milk_tea/bindings/imported_bindings/generator.rb +7 -9
  9. data/lib/milk_tea/bindings/imported_bindings/method_source.rb +0 -2
  10. data/lib/milk_tea/bindings/imported_bindings/naming.rb +6 -8
  11. data/lib/milk_tea/bindings/imported_bindings.rb +0 -2
  12. data/lib/milk_tea/bindings/opengl_registry.rb +3 -7
  13. data/lib/milk_tea/bindings/steamworks.rb +12 -16
  14. data/lib/milk_tea/bindings/upstream_sources.rb +10 -14
  15. data/lib/milk_tea/bindings/vendored_box2d.rb +10 -12
  16. data/lib/milk_tea/bindings/vendored_cjson.rb +6 -8
  17. data/lib/milk_tea/bindings/vendored_flecs.rb +10 -12
  18. data/lib/milk_tea/bindings/vendored_glfw.rb +11 -13
  19. data/lib/milk_tea/bindings/vendored_libuv.rb +11 -13
  20. data/lib/milk_tea/bindings/vendored_pcre2.rb +11 -13
  21. data/lib/milk_tea/bindings/vendored_raylib.rb +6 -10
  22. data/lib/milk_tea/bindings/vendored_sdl3.rb +11 -13
  23. data/lib/milk_tea/bindings/vendored_steamworks.rb +1 -5
  24. data/lib/milk_tea/core/ast.rb +103 -45
  25. data/lib/milk_tea/core/async_runtime_installer.rb +0 -2
  26. data/lib/milk_tea/core/bindings/attribute_binding.rb +1 -3
  27. data/lib/milk_tea/core/c_backend/aggregate_utils.rb +147 -84
  28. data/lib/milk_tea/core/c_backend/control_flow_emission.rb +394 -396
  29. data/lib/milk_tea/core/c_backend/expressions.rb +489 -476
  30. data/lib/milk_tea/core/c_backend/feature_detection.rb +446 -437
  31. data/lib/milk_tea/core/c_backend/format_helpers.rb +0 -2
  32. data/lib/milk_tea/core/c_backend/reachability.rb +339 -341
  33. data/lib/milk_tea/core/c_backend/reinterpret.rb +231 -217
  34. data/lib/milk_tea/core/c_backend/runtime_helpers.rb +531 -526
  35. data/lib/milk_tea/core/c_backend/statements.rb +528 -530
  36. data/lib/milk_tea/core/c_backend/type_collectors.rb +1344 -1346
  37. data/lib/milk_tea/core/c_backend/type_declaration.rb +215 -213
  38. data/lib/milk_tea/core/c_backend/type_system.rb +347 -349
  39. data/lib/milk_tea/core/c_backend.rb +17 -9
  40. data/lib/milk_tea/core/compatibility_helpers.rb +0 -2
  41. data/lib/milk_tea/core/compile_time/const_eval.rb +7 -12
  42. data/lib/milk_tea/core/compile_time.rb +32 -38
  43. data/lib/milk_tea/core/control_flow/builder.rb +3 -5
  44. data/lib/milk_tea/core/control_flow/constant_propagation.rb +14 -17
  45. data/lib/milk_tea/core/control_flow/definite_assignment.rb +0 -1
  46. data/lib/milk_tea/core/ir.rb +10 -10
  47. data/lib/milk_tea/core/lexer/character_classes.rb +5 -7
  48. data/lib/milk_tea/core/lexer/format_strings.rb +7 -9
  49. data/lib/milk_tea/core/lexer/heredocs.rb +22 -22
  50. data/lib/milk_tea/core/lexer/indentation.rb +0 -2
  51. data/lib/milk_tea/core/lexer/numbers.rb +0 -2
  52. data/lib/milk_tea/core/lexer/recovery.rb +0 -2
  53. data/lib/milk_tea/core/lexer/strings.rb +0 -2
  54. data/lib/milk_tea/core/lexer/symbols.rb +0 -2
  55. data/lib/milk_tea/core/lexer/trivia.rb +0 -2
  56. data/lib/milk_tea/core/lexer.rb +0 -2
  57. data/lib/milk_tea/core/lowering/async/analysis.rb +0 -2
  58. data/lib/milk_tea/core/lowering/async/lowering.rb +1264 -1265
  59. data/lib/milk_tea/core/lowering/async/normalization.rb +0 -2
  60. data/lib/milk_tea/core/lowering/async.rb +627 -630
  61. data/lib/milk_tea/core/lowering/block.rb +413 -416
  62. data/lib/milk_tea/core/lowering/calls.rb +1381 -1384
  63. data/lib/milk_tea/core/lowering/declarations.rb +180 -183
  64. data/lib/milk_tea/core/lowering/dyn.rb +48 -50
  65. data/lib/milk_tea/core/lowering/events.rb +983 -985
  66. data/lib/milk_tea/core/lowering/expressions.rb +1513 -1518
  67. data/lib/milk_tea/core/lowering/foreign_cstr.rb +181 -184
  68. data/lib/milk_tea/core/lowering/functions.rb +185 -188
  69. data/lib/milk_tea/core/lowering/loops.rb +1028 -1031
  70. data/lib/milk_tea/core/lowering/proc.rb +363 -366
  71. data/lib/milk_tea/core/lowering/resolve.rb +2133 -2135
  72. data/lib/milk_tea/core/lowering/scans.rb +179 -181
  73. data/lib/milk_tea/core/lowering/str_buffer.rb +102 -105
  74. data/lib/milk_tea/core/lowering/utils.rb +1040 -1044
  75. data/lib/milk_tea/core/lowering.rb +5 -7
  76. data/lib/milk_tea/core/module_binder.rb +0 -2
  77. data/lib/milk_tea/core/module_loader/errors.rb +4 -2
  78. data/lib/milk_tea/core/module_loader.rb +113 -20
  79. data/lib/milk_tea/core/module_path_resolver.rb +0 -2
  80. data/lib/milk_tea/core/module_roots.rb +6 -8
  81. data/lib/milk_tea/core/parser/attributes.rb +0 -2
  82. data/lib/milk_tea/core/parser/blocks.rb +0 -2
  83. data/lib/milk_tea/core/parser/declarations.rb +0 -2
  84. data/lib/milk_tea/core/parser/expressions.rb +31 -14
  85. data/lib/milk_tea/core/parser/recovery.rb +0 -2
  86. data/lib/milk_tea/core/parser/statements.rb +0 -2
  87. data/lib/milk_tea/core/parser/types.rb +8 -10
  88. data/lib/milk_tea/core/parser.rb +1 -3
  89. data/lib/milk_tea/core/pretty_printer/ast_formatter.rb +31 -33
  90. data/lib/milk_tea/core/pretty_printer/base_formatter.rb +0 -2
  91. data/lib/milk_tea/core/pretty_printer/ir_formatter.rb +0 -2
  92. data/lib/milk_tea/core/semantic_analyzer/analysis_context.rb +0 -2
  93. data/lib/milk_tea/core/semantic_analyzer/attributes.rb +14 -16
  94. data/lib/milk_tea/core/semantic_analyzer/calls.rb +0 -2
  95. data/lib/milk_tea/core/semantic_analyzer/expressions.rb +1 -14
  96. data/lib/milk_tea/core/semantic_analyzer/flow_refinement.rb +18 -21
  97. data/lib/milk_tea/core/semantic_analyzer/foreign_functions.rb +0 -3
  98. data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +12 -15
  99. data/lib/milk_tea/core/semantic_analyzer/generics.rb +57 -59
  100. data/lib/milk_tea/core/semantic_analyzer/interface_conformance.rb +0 -2
  101. data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +5 -9
  102. data/lib/milk_tea/core/semantic_analyzer/nullability.rb +0 -3
  103. data/lib/milk_tea/core/semantic_analyzer/statements.rb +35 -40
  104. data/lib/milk_tea/core/semantic_analyzer/top_level.rb +10 -12
  105. data/lib/milk_tea/core/semantic_analyzer/type_compatibility.rb +0 -6
  106. data/lib/milk_tea/core/semantic_analyzer/type_declaration.rb +102 -105
  107. data/lib/milk_tea/core/semantic_analyzer.rb +17 -8
  108. data/lib/milk_tea/core/types/layout.rb +21 -22
  109. data/lib/milk_tea/core/types/predicates.rb +0 -2
  110. data/lib/milk_tea/core/types/registry.rb +16 -18
  111. data/lib/milk_tea/core/types/visitor.rb +0 -4
  112. data/lib/milk_tea/dap/backends/lldb_dap.rb +0 -2
  113. data/lib/milk_tea/dap/protocol.rb +1 -1
  114. data/lib/milk_tea/dap/server/breakpoints.rb +0 -2
  115. data/lib/milk_tea/dap/server/debug_map.rb +0 -2
  116. data/lib/milk_tea/dap/server/handlers.rb +0 -2
  117. data/lib/milk_tea/dap/server/launch.rb +0 -2
  118. data/lib/milk_tea/dap/server/lldb_backend.rb +8 -10
  119. data/lib/milk_tea/dap/server/pause_diagnostics.rb +14 -16
  120. data/lib/milk_tea/dap/server/utilities.rb +0 -2
  121. data/lib/milk_tea/dap/server/wire.rb +0 -2
  122. data/lib/milk_tea/dap/server.rb +0 -2
  123. data/lib/milk_tea/lsp/diagnostics.rb +16 -16
  124. data/lib/milk_tea/lsp/server/call_hierarchy.rb +11 -12
  125. data/lib/milk_tea/lsp/server/code_actions.rb +355 -357
  126. data/lib/milk_tea/lsp/server/code_lens.rb +23 -25
  127. data/lib/milk_tea/lsp/server/completion.rb +873 -875
  128. data/lib/milk_tea/lsp/server/configuration.rb +129 -131
  129. data/lib/milk_tea/lsp/server/debug_info.rb +0 -2
  130. data/lib/milk_tea/lsp/server/definition.rb +564 -565
  131. data/lib/milk_tea/lsp/server/diagnostics_scheduling.rb +191 -193
  132. data/lib/milk_tea/lsp/server/execute_command.rb +0 -2
  133. data/lib/milk_tea/lsp/server/folding_range.rb +0 -4
  134. data/lib/milk_tea/lsp/server/formatting.rb +478 -480
  135. data/lib/milk_tea/lsp/server/hover.rb +1282 -1284
  136. data/lib/milk_tea/lsp/server/inlay_hints.rb +179 -181
  137. data/lib/milk_tea/lsp/server/lifecycle.rb +192 -194
  138. data/lib/milk_tea/lsp/server/linked_editing_range.rb +0 -2
  139. data/lib/milk_tea/lsp/server/on_type_formatting.rb +0 -2
  140. data/lib/milk_tea/lsp/server/progress.rb +0 -2
  141. data/lib/milk_tea/lsp/server/references.rb +316 -318
  142. data/lib/milk_tea/lsp/server/rename.rb +445 -447
  143. data/lib/milk_tea/lsp/server/selection_range.rb +0 -4
  144. data/lib/milk_tea/lsp/server/semantic_tokens.rb +1318 -1320
  145. data/lib/milk_tea/lsp/server/signature_help.rb +146 -148
  146. data/lib/milk_tea/lsp/server/text_documents.rb +99 -101
  147. data/lib/milk_tea/lsp/server/type_hierarchy.rb +2 -4
  148. data/lib/milk_tea/lsp/server/utilities.rb +397 -399
  149. data/lib/milk_tea/lsp/server.rb +0 -3
  150. data/lib/milk_tea/lsp/workspace/analysis.rb +40 -42
  151. data/lib/milk_tea/lsp/workspace/dependency_graph.rb +4 -10
  152. data/lib/milk_tea/lsp/workspace/utilities.rb +19 -19
  153. data/lib/milk_tea/packages/atomic_write.rb +4 -5
  154. data/lib/milk_tea/packages/dependency_solver.rb +15 -15
  155. data/lib/milk_tea/packages/lock.rb +4 -4
  156. data/lib/milk_tea/packages/manager_cli.rb +1 -1
  157. data/lib/milk_tea/packages/manifest.rb +3 -3
  158. data/lib/milk_tea/packages/registry_store.rb +4 -6
  159. data/lib/milk_tea/packages/services.rb +16 -16
  160. data/lib/milk_tea/packages/source_cache.rb +4 -6
  161. data/lib/milk_tea/packages/source_resolver.rb +44 -44
  162. data/lib/milk_tea/packages/version.rb +8 -8
  163. data/lib/milk_tea/tooling/build.rb +3 -7
  164. data/lib/milk_tea/tooling/build_cache.rb +0 -2
  165. data/lib/milk_tea/tooling/debug_info_formatter.rb +31 -33
  166. data/lib/milk_tea/tooling/debug_map.rb +8 -10
  167. data/lib/milk_tea/tooling/docs_app.rb +0 -2
  168. data/lib/milk_tea/tooling/linter/doc_tags.rb +25 -27
  169. data/lib/milk_tea/tooling/linter/fix_engine.rb +87 -24
  170. data/lib/milk_tea/tooling/linter/flow_rules.rb +44 -46
  171. data/lib/milk_tea/tooling/linter/imports_platform.rb +14 -16
  172. data/lib/milk_tea/tooling/linter/release_rules.rb +3 -5
  173. data/lib/milk_tea/tooling/linter/reserved_names.rb +23 -25
  174. data/lib/milk_tea/tooling/linter/rules.rb +87 -89
  175. data/lib/milk_tea/tooling/linter/source_helpers.rb +40 -42
  176. data/lib/milk_tea/tooling/linter/trailing_comma.rb +17 -19
  177. data/lib/milk_tea/tooling/linter/visitors.rb +162 -50
  178. data/lib/milk_tea/tooling/linter.rb +0 -2
  179. data/lib/milk_tea/tooling/project_scaffold.rb +0 -2
  180. data/lib/milk_tea/tooling/run.rb +0 -2
  181. data/lib/milk_tea/tooling/toolchain_cli.rb +0 -2
  182. data/std/cli.mt +13 -32
  183. data/std/color.mt +28 -28
  184. data/std/encoding.mt +4 -4
  185. data/std/graph.mt +1 -2
  186. data/std/hash.mt +14 -14
  187. data/std/http/server.mt +3 -3
  188. data/std/input.mt +1 -1
  189. data/std/json.mt +1 -1
  190. data/std/linear_algebra.mt +6 -6
  191. data/std/net/mux.mt +1 -1
  192. data/std/noise.mt +23 -23
  193. data/std/parse.mt +2 -2
  194. data/std/process.mt +3 -4
  195. data/std/sparse_set.mt +1 -1
  196. data/std/steering.mt +5 -5
  197. data/std/str.mt +1 -1
  198. data/std/tween.mt +4 -4
  199. data/std/utility.mt +3 -3
  200. metadata +2 -2
@@ -3,536 +3,541 @@
3
3
  module MilkTea
4
4
  class CBackend
5
5
  module RuntimeHelpers
6
- private
7
-
8
- def emit_fatal_helper
9
- lines = []
10
-
11
- if uses_mt_fatal_helper?
12
- lines.concat([
13
- "static _Noreturn void mt_fatal(const char* message) {",
14
- "#{INDENT}fputs(message, stderr);",
15
- "#{INDENT}fputc('\\n', stderr);",
16
- "#{INDENT}abort();",
17
- "}",
18
- ])
19
- end
20
-
21
- if uses_mt_fatal_str_helper?
22
- lines << "" unless lines.empty?
23
- lines.concat([
24
- "static _Noreturn void mt_fatal_str(mt_str message) {",
25
- "#{INDENT}fwrite(message.data, 1, message.len, stderr);",
26
- "#{INDENT}fputc('\\n', stderr);",
27
- "#{INDENT}abort();",
28
- "}",
29
- ])
30
- end
31
-
32
- lines
33
- end
34
-
35
- def emit_str_equality_helper
36
- [
37
- "static bool mt_str_equal(mt_str left, mt_str right) {",
38
- "#{INDENT}if (left.len != right.len) return false;",
39
- "#{INDENT}for (uintptr_t index = 0; index < left.len; index++) {",
40
- "#{INDENT * 2}if (left.data[index] != right.data[index]) return false;",
41
- "#{INDENT}}",
42
- "#{INDENT}return true;",
43
- "}",
44
- ]
45
- end
46
-
47
- def emit_variant_equality_helpers
48
- emitted_aggregate_variants.flat_map { |variant_decl| emit_variant_equality_helper(variant_decl) }
49
- end
50
-
51
- def emit_variant_equality_helper(variant_decl)
52
- outer_c = variant_decl.linkage_name
53
- lines = ["static bool mt_variant_eq_#{outer_c}(struct #{outer_c} left, struct #{outer_c} right) {"]
54
- lines << "#{INDENT}if (left.kind != right.kind) return false;"
55
- lines << "#{INDENT}switch (left.kind) {"
56
-
57
- variant_decl.arms.each do |arm|
58
- lines << "#{INDENT * 2}case #{outer_c}_kind_#{arm.name}:"
59
- if arm.fields.empty?
60
- lines << "#{INDENT * 3}return true;"
61
- else
62
- arm.fields.each do |field|
63
- field_type = field.type
64
- left_expr = "left.data.#{sanitize_c_identifier(arm.name)}.#{sanitize_c_identifier(field.name)}"
65
- right_expr = "right.data.#{sanitize_c_identifier(arm.name)}.#{sanitize_c_identifier(field.name)}"
66
- if field_type.is_a?(Types::StringView)
67
- lines << "#{INDENT * 3}if (!mt_str_equal(#{left_expr}, #{right_expr})) return false;"
68
- elsif field_type.is_a?(Types::Variant)
69
- lines << "#{INDENT * 3}if (!mt_variant_eq_#{named_type_c_name(field_type)}(#{left_expr}, #{right_expr})) return false;"
70
- elsif field_type.is_a?(Types::Primitive) || field_type.is_a?(Types::EnumBase) || field_type.is_a?(Types::Nullable)
71
- lines << "#{INDENT * 3}if (#{left_expr} != #{right_expr}) return false;"
72
- else
73
- lines << "#{INDENT * 3}if (#{left_expr} != #{right_expr}) return false;"
74
- end
6
+ def emit_fatal_helper
7
+ lines = []
8
+
9
+ if uses_mt_fatal_helper?
10
+ lines.concat([
11
+ "static _Noreturn void mt_fatal(const char* message) {",
12
+ "#{INDENT}fputs(message, stderr);",
13
+ "#{INDENT}fputc('\\n', stderr);",
14
+ "#{INDENT}abort();",
15
+ "}",
16
+ ])
17
+ end
18
+
19
+ if uses_mt_fatal_str_helper?
20
+ lines << "" unless lines.empty?
21
+ lines.concat([
22
+ "static _Noreturn void mt_fatal_str(mt_str message) {",
23
+ "#{INDENT}fwrite(message.data, 1, message.len, stderr);",
24
+ "#{INDENT}fputc('\\n', stderr);",
25
+ "#{INDENT}abort();",
26
+ "}",
27
+ ])
28
+ end
29
+
30
+ lines
31
+ end
32
+
33
+ def emit_str_equality_helper
34
+ [
35
+ "static bool mt_str_equal(mt_str left, mt_str right) {",
36
+ "#{INDENT}if (left.len != right.len) return false;",
37
+ "#{INDENT}for (uintptr_t index = 0; index < left.len; index++) {",
38
+ "#{INDENT * 2}if (left.data[index] != right.data[index]) return false;",
39
+ "#{INDENT}}",
40
+ "#{INDENT}return true;",
41
+ "}",
42
+ ]
43
+ end
44
+
45
+ def emit_variant_equality_helpers
46
+ emitted_aggregate_variants.flat_map { |variant_decl| emit_variant_equality_helper(variant_decl) }
47
+ end
48
+
49
+ def emit_variant_equality_helper(variant_decl)
50
+ outer_c = variant_decl.linkage_name
51
+ lines = ["static bool mt_variant_eq_#{outer_c}(struct #{outer_c} left, struct #{outer_c} right) {"]
52
+ lines << "#{INDENT}if (left.kind != right.kind) return false;"
53
+ lines << "#{INDENT}switch (left.kind) {"
54
+
55
+ variant_decl.arms.each do |arm|
56
+ lines << "#{INDENT * 2}case #{outer_c}_kind_#{arm.name}:"
57
+ if arm.fields.empty?
58
+ lines << "#{INDENT * 3}return true;"
59
+ else
60
+ arm.fields.each do |field|
61
+ field_type = field.type
62
+ left_expr = "left.data.#{sanitize_c_identifier(arm.name)}.#{sanitize_c_identifier(field.name)}"
63
+ right_expr = "right.data.#{sanitize_c_identifier(arm.name)}.#{sanitize_c_identifier(field.name)}"
64
+ if field_type.is_a?(Types::StringView)
65
+ lines << "#{INDENT * 3}if (!mt_str_equal(#{left_expr}, #{right_expr})) return false;"
66
+ elsif field_type.is_a?(Types::Variant)
67
+ lines << "#{INDENT * 3}if (!mt_variant_eq_#{named_type_c_name(field_type)}(#{left_expr}, #{right_expr})) return false;"
68
+ elsif field_type.is_a?(Types::Nullable)
69
+ if c_backend_pointer_like_type?(field_type.base)
70
+ lines << "#{INDENT * 3}if (#{left_expr} != #{right_expr}) return false;"
71
+ else
72
+ lines << "#{INDENT * 3}if (#{left_expr}.has_value != #{right_expr}.has_value) return false;"
73
+ lines << "#{INDENT * 3}if (#{left_expr}.has_value && #{left_expr}.value != #{right_expr}.value) return false;"
75
74
  end
76
- lines << "#{INDENT * 3}return true;"
75
+ elsif field_type.is_a?(Types::Primitive) || field_type.is_a?(Types::EnumBase)
76
+ lines << "#{INDENT * 3}if (#{left_expr} != #{right_expr}) return false;"
77
+ else
78
+ lines << "#{INDENT * 3}if (#{left_expr} != #{right_expr}) return false;"
77
79
  end
78
80
  end
79
-
80
- lines << "#{INDENT * 2}default: return true;"
81
- lines << "#{INDENT}}"
82
- lines << "}"
83
- lines
84
- end
85
-
86
- def emit_async_memory_helpers
87
- [
88
- "#define MT_ASYNC_HEADER_SIZE (sizeof(uint64_t) + sizeof(uintptr_t))",
89
- "#define MT_ASYNC_MAGIC UINT64_C(0x4D5441464D454D00)",
90
- "",
91
- "static void* mt_async_alloc(uintptr_t size) {",
92
- "#{INDENT}char* raw = (char*) calloc(1, (size_t)(MT_ASYNC_HEADER_SIZE + size));",
93
- "#{INDENT}if (raw == NULL) {",
94
- "#{INDENT * 2}abort();",
95
- "#{INDENT}}",
96
- "#{INDENT}*(uint64_t*)raw = MT_ASYNC_MAGIC;",
97
- "#{INDENT}*(uintptr_t*)(raw + sizeof(uint64_t)) = 1;",
98
- "#{INDENT}return raw + MT_ASYNC_HEADER_SIZE;",
99
- "}",
100
- "",
101
- "static void mt_async_retain(void* frame) {",
102
- "#{INDENT}char* raw = (char*)frame - MT_ASYNC_HEADER_SIZE;",
103
- "#{INDENT}if (*(uint64_t*)raw != MT_ASYNC_MAGIC) return;",
104
- "#{INDENT}uintptr_t* ref = (uintptr_t*)(raw + sizeof(uint64_t));",
105
- "#{INDENT}(*ref)++;",
106
- "}",
107
- "",
108
- "static void mt_async_free(void* frame) {",
109
- "#{INDENT}char* raw = (char*)frame - MT_ASYNC_HEADER_SIZE;",
110
- "#{INDENT}if (*(uint64_t*)raw != MT_ASYNC_MAGIC) return;",
111
- "#{INDENT}uintptr_t* ref = (uintptr_t*)(raw + sizeof(uint64_t));",
112
- "#{INDENT}if (--(*ref) == 0) {",
113
- "#{INDENT * 2}free(raw);",
114
- "#{INDENT}}",
115
- "}",
116
- ]
117
- end
118
-
119
- def emit_parallel_for_helper
120
- [
121
- "typedef struct {",
122
- "#{INDENT}void (*work)(void* data, int64_t start, int64_t end);",
123
- "#{INDENT}void* data;",
124
- "#{INDENT}int64_t start;",
125
- "#{INDENT}int64_t end;",
126
- "} mt_pfor_chunk;",
127
- "",
128
- "static void mt_pfor_runner(void* arg) {",
129
- "#{INDENT}mt_pfor_chunk* chunk = (mt_pfor_chunk*)arg;",
130
- "#{INDENT}chunk->work(chunk->data, chunk->start, chunk->end);",
131
- "}",
132
- "",
133
- "static void mt_parallel_for(void (*work)(void* data, int64_t start, int64_t end), void* data, int64_t count) {",
134
- "#{INDENT}if (count <= 0) return;",
135
- "#{INDENT}uv_cpu_info_t* cpu_info;",
136
- "#{INDENT}int ncpu = 1;",
137
- "#{INDENT}if (uv_cpu_info(&cpu_info, &ncpu) == 0) {",
138
- "#{INDENT * 2}uv_free_cpu_info(cpu_info, ncpu);",
139
- "#{INDENT}}",
140
- "#{INDENT}if (ncpu < 1) ncpu = 1;",
141
- "#{INDENT}if (ncpu > 64) ncpu = 64;",
142
- "#{INDENT}if (count < (int64_t)ncpu) ncpu = (int)count;",
143
- "#{INDENT}int64_t chunk_size = (count + ncpu - 1) / ncpu;",
144
- "#{INDENT}mt_pfor_chunk chunks[64];",
145
- "#{INDENT}uv_thread_t threads[64];",
146
- "#{INDENT}int nworkers = 0;",
147
- "#{INDENT}for (int t = 1; t < ncpu; t++) {",
148
- "#{INDENT * 2}int64_t s = t * chunk_size;",
149
- "#{INDENT * 2}int64_t e = s + chunk_size;",
150
- "#{INDENT * 2}if (e > count) e = count;",
151
- "#{INDENT * 2}if (s >= count) break;",
152
- "#{INDENT * 2}chunks[nworkers].work = work;",
153
- "#{INDENT * 2}chunks[nworkers].data = data;",
154
- "#{INDENT * 2}chunks[nworkers].start = s;",
155
- "#{INDENT * 2}chunks[nworkers].end = e;",
156
- "#{INDENT * 2}uv_thread_create(&threads[nworkers], mt_pfor_runner, &chunks[nworkers]);",
157
- "#{INDENT * 2}nworkers++;",
158
- "#{INDENT}}",
159
- "#{INDENT}int64_t first_end = chunk_size < count ? chunk_size : count;",
160
- "#{INDENT}work(data, 0, first_end);",
161
- "#{INDENT}for (int t = 0; t < nworkers; t++) {",
162
- "#{INDENT * 2}uv_thread_join(&threads[t]);",
163
- "#{INDENT}}",
164
- "}",
165
- ]
166
- end
167
-
168
- def emit_spawn_all_helper
169
- [
170
- "typedef struct {",
171
- "#{INDENT}void (*work)(void* data);",
172
- "#{INDENT}void* data;",
173
- "} mt_spawn_item;",
174
- "",
175
- "static void mt_spawn_item_runner(void* arg) {",
176
- "#{INDENT}mt_spawn_item* item = (mt_spawn_item*)arg;",
177
- "#{INDENT}item->work(item->data);",
178
- "}",
179
- "",
180
- "static void mt_spawn_all(mt_spawn_item* items, int count) {",
181
- "#{INDENT}if (count <= 0) return;",
182
- "#{INDENT}uv_thread_t threads[64];",
183
- "#{INDENT}int nworkers = 0;",
184
- "#{INDENT}for (int t = 1; t < count && nworkers < 63; t++) {",
185
- "#{INDENT * 2}uv_thread_create(&threads[nworkers], mt_spawn_item_runner, &items[t]);",
186
- "#{INDENT * 2}nworkers++;",
187
- "#{INDENT}}",
188
- "#{INDENT}items[0].work(items[0].data);",
189
- "#{INDENT}for (int t = 0; t < nworkers; t++) {",
190
- "#{INDENT * 2}uv_thread_join(&threads[t]);",
191
- "#{INDENT}}",
192
- "}",
193
- ]
194
- end
195
-
196
- def emit_detach_helpers
197
- [
198
- "typedef struct {",
199
- "#{INDENT}uv_thread_t thread;",
200
- "} mt_detach_handle;",
201
- "",
202
- "static void* mt_detach_run(void (*work)(void*), void* cap) {",
203
- "#{INDENT}mt_detach_handle* h = (mt_detach_handle*)malloc(sizeof(mt_detach_handle));",
204
- "#{INDENT}uv_thread_create(&h->thread, work, cap);",
205
- "#{INDENT}return h;",
206
- "}",
207
- "",
208
- "static void mt_detach_join(void* handle) {",
209
- "#{INDENT}if (!handle) return;",
210
- "#{INDENT}mt_detach_handle* h = (mt_detach_handle*)handle;",
211
- "#{INDENT}uv_thread_join(&h->thread);",
212
- "#{INDENT}free(h);",
213
- "}",
214
- ]
215
- end
216
-
217
- def emit_foreign_temp_cstr_helpers
218
- lines = []
219
-
220
- if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_foreign_str_to_cstr_temp]) }
221
- lines.concat([
222
- "static const char* mt_foreign_str_to_cstr_temp(mt_str value) {",
223
- "#{INDENT}char* data = (char*)malloc(value.len + 1);",
224
- "#{INDENT}uintptr_t index = 0;",
225
- "#{INDENT}if (data == NULL) mt_fatal(\"foreign str temporary allocation failed\");",
226
- "#{INDENT}while (index < value.len) {",
227
- "#{INDENT * 2}data[index] = value.data[index];",
228
- "#{INDENT * 2}index++;",
229
- "#{INDENT}}",
230
- "#{INDENT}data[value.len] = '\\0';",
231
- "#{INDENT}return data;",
232
- "}",
233
- ])
234
- end
235
-
236
- if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_free_foreign_cstr_temp]) }
237
- lines << "" unless lines.empty?
238
- lines.concat([
239
- "static void mt_free_foreign_cstr_temp(const char* value) {",
240
- "#{INDENT}free((void*)value);",
241
- "}",
242
- ])
243
- end
244
-
245
- if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_foreign_strs_to_cstrs_temp]) }
246
- lines << "" unless lines.empty?
247
- lines.concat([
248
- "static void mt_foreign_strs_to_cstrs_temp(mt_span_str values, char*** items_out, char** data_out, uintptr_t* len_out) {",
249
- "#{INDENT}uintptr_t total_bytes = 0;",
250
- "#{INDENT}uintptr_t index = 0;",
251
- "#{INDENT}uintptr_t offset = 0;",
252
- "#{INDENT}char** items = NULL;",
253
- "#{INDENT}char* data = NULL;",
254
- "#{INDENT}while (index < values.len) {",
255
- "#{INDENT * 2}total_bytes += values.data[index].len + 1;",
256
- "#{INDENT * 2}index++;",
257
- "#{INDENT}}",
258
- "#{INDENT}if (values.len > 0) {",
259
- "#{INDENT * 2}items = (char**)malloc(values.len * sizeof(char*));",
260
- "#{INDENT * 2}if (items == NULL) mt_fatal(\"foreign string-list temporary allocation failed\");",
261
- "#{INDENT}}",
262
- "#{INDENT}if (total_bytes > 0) {",
263
- "#{INDENT * 2}data = (char*)malloc(total_bytes);",
264
- "#{INDENT * 2}if (data == NULL) {",
265
- "#{INDENT * 3}free(items);",
266
- "#{INDENT * 3}mt_fatal(\"foreign string-list temporary allocation failed\");",
267
- "#{INDENT * 2}}",
268
- "#{INDENT}}",
269
- "#{INDENT}index = 0;",
270
- "#{INDENT}while (index < values.len) {",
271
- "#{INDENT * 2}mt_str value = values.data[index];",
272
- "#{INDENT * 2}uintptr_t byte_index = 0;",
273
- "#{INDENT * 2}items[index] = data + offset;",
274
- "#{INDENT * 2}while (byte_index < value.len) {",
275
- "#{INDENT * 3}data[offset + byte_index] = value.data[byte_index];",
276
- "#{INDENT * 3}byte_index++;",
277
- "#{INDENT * 2}}",
278
- "#{INDENT * 2}data[offset + value.len] = '\\0';",
279
- "#{INDENT * 2}offset += value.len + 1;",
280
- "#{INDENT * 2}index++;",
281
- "#{INDENT}}",
282
- "#{INDENT}*items_out = items;",
283
- "#{INDENT}*data_out = data;",
284
- "#{INDENT}*len_out = values.len;",
285
- "}",
286
- ])
287
- end
288
-
289
- if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_free_foreign_cstrs_temp]) }
290
- lines << "" unless lines.empty?
291
- lines.concat([
292
- "static void mt_free_foreign_cstrs_temp(char** items, char* data) {",
293
- "#{INDENT}free(items);",
294
- "#{INDENT}free(data);",
295
- "}",
296
- ])
297
- end
298
-
299
- lines
300
- end
301
-
302
- def emit_entrypoint_argv_helpers
303
- lines = []
304
-
305
- if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_entry_argv_to_span_str]) }
306
- lines.concat([
307
- "static mt_span_str mt_entry_argv_to_span_str(int32_t argc, char** argv, mt_str** items_out) {",
308
- "#{INDENT}uintptr_t len = argc > 1 ? (uintptr_t)(argc - 1) : 0;",
309
- "#{INDENT}mt_str* items = NULL;",
310
- "#{INDENT}uintptr_t index = 0;",
311
- "#{INDENT}if (len > 0) {",
312
- "#{INDENT * 2}items = (mt_str*)malloc(len * sizeof(mt_str));",
313
- "#{INDENT * 2}if (items == NULL) abort();",
314
- "#{INDENT}}",
315
- "#{INDENT}while (index < len) {",
316
- "#{INDENT * 2}char* value = argv[index + 1];",
317
- "#{INDENT * 2}items[index] = (mt_str){ .data = value, .len = (uintptr_t)strlen(value) };",
318
- "#{INDENT * 2}index++;",
319
- "#{INDENT}}",
320
- "#{INDENT}*items_out = items;",
321
- "#{INDENT}return (mt_span_str){ .data = items, .len = len };",
322
- "}",
323
- ])
324
- end
325
-
326
- if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_free_entry_argv_strs]) }
327
- lines << "" unless lines.empty?
328
- lines.concat([
329
- "static void mt_free_entry_argv_strs(mt_str* items) {",
330
- "#{INDENT}free(items);",
331
- "}",
332
- ])
333
- end
334
-
335
- lines
336
- end
337
-
338
- def emit_text_buffer_helpers
339
- [
340
- "static bool mt_is_utf8_continuation_byte(unsigned char byte) {",
341
- "#{INDENT}return (byte & 0xC0u) == 0x80u;",
342
- "}",
343
- "",
344
- "static bool mt_is_valid_utf8(const char* data, uintptr_t len) {",
345
- "#{INDENT}uintptr_t index = 0;",
346
- "#{INDENT}while (index < len) {",
347
- "#{INDENT * 2}unsigned char lead = (unsigned char) data[index];",
348
- "#{INDENT * 2}if (lead < 0x80u) {",
349
- "#{INDENT * 3}index++;",
350
- "#{INDENT * 3}continue;",
351
- "#{INDENT * 2}}",
352
- "#{INDENT * 2}if (lead < 0xC2u) return false;",
353
- "#{INDENT * 2}if (lead < 0xE0u) {",
354
- "#{INDENT * 3}if (index + 1 >= len) return false;",
355
- "#{INDENT * 3}unsigned char byte1 = (unsigned char) data[index + 1];",
356
- "#{INDENT * 3}if (!mt_is_utf8_continuation_byte(byte1)) return false;",
357
- "#{INDENT * 3}index += 2;",
358
- "#{INDENT * 3}continue;",
359
- "#{INDENT * 2}}",
360
- "#{INDENT * 2}if (lead < 0xF0u) {",
361
- "#{INDENT * 3}if (index + 2 >= len) return false;",
362
- "#{INDENT * 3}unsigned char byte1 = (unsigned char) data[index + 1];",
363
- "#{INDENT * 3}unsigned char byte2 = (unsigned char) data[index + 2];",
364
- "#{INDENT * 3}if (lead == 0xE0u) {",
365
- "#{INDENT * 4}if (byte1 < 0xA0u || byte1 > 0xBFu) return false;",
366
- "#{INDENT * 3}} else if (lead == 0xEDu) {",
367
- "#{INDENT * 4}if (byte1 < 0x80u || byte1 > 0x9Fu) return false;",
368
- "#{INDENT * 3}} else if (!mt_is_utf8_continuation_byte(byte1)) {",
369
- "#{INDENT * 4}return false;",
370
- "#{INDENT * 3}}",
371
- "#{INDENT * 3}if (!mt_is_utf8_continuation_byte(byte2)) return false;",
372
- "#{INDENT * 3}index += 3;",
373
- "#{INDENT * 3}continue;",
374
- "#{INDENT * 2}}",
375
- "#{INDENT * 2}if (lead < 0xF5u) {",
376
- "#{INDENT * 3}if (index + 3 >= len) return false;",
377
- "#{INDENT * 3}unsigned char byte1 = (unsigned char) data[index + 1];",
378
- "#{INDENT * 3}unsigned char byte2 = (unsigned char) data[index + 2];",
379
- "#{INDENT * 3}unsigned char byte3 = (unsigned char) data[index + 3];",
380
- "#{INDENT * 3}if (lead == 0xF0u) {",
381
- "#{INDENT * 4}if (byte1 < 0x90u || byte1 > 0xBFu) return false;",
382
- "#{INDENT * 3}} else if (lead == 0xF4u) {",
383
- "#{INDENT * 4}if (byte1 < 0x80u || byte1 > 0x8Fu) return false;",
384
- "#{INDENT * 3}} else if (!mt_is_utf8_continuation_byte(byte1)) {",
385
- "#{INDENT * 4}return false;",
386
- "#{INDENT * 3}}",
387
- "#{INDENT * 3}if (!mt_is_utf8_continuation_byte(byte2) || !mt_is_utf8_continuation_byte(byte3)) return false;",
388
- "#{INDENT * 3}index += 4;",
389
- "#{INDENT * 3}continue;",
390
- "#{INDENT * 2}}",
391
- "#{INDENT * 2}return false;",
392
- "#{INDENT}}",
393
- "#{INDENT}return true;",
394
- "}",
395
- ]
396
- end
397
-
398
- def emit_str_buffer_helpers
399
- lines = []
400
-
401
- if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_str_buffer_len mt_str_buffer_as_cstr mt_str_buffer_append]) }
402
- lines.concat([
403
- "static uintptr_t mt_str_buffer_len(char* data, uintptr_t cap, uintptr_t* len, bool* dirty) {",
404
- "#{INDENT}if (*dirty) {",
405
- "#{INDENT * 2}uintptr_t current = 0;",
406
- "#{INDENT * 2}while (current < cap + 1 && data[current] != '\\0') {",
407
- "#{INDENT * 3}current++;",
408
- "#{INDENT * 2}}",
409
- "#{INDENT * 2}if (current > cap) mt_fatal(\"str_buffer text requires a trailing NUL within capacity\");",
410
- "#{INDENT * 2}if (!mt_is_valid_utf8(data, current)) mt_fatal(\"str_buffer text must be valid UTF-8\");",
411
- "#{INDENT * 2}*len = current;",
412
- "#{INDENT * 2}*dirty = false;",
413
- "#{INDENT}}",
414
- "#{INDENT}return *len;",
415
- "}",
416
- ])
417
- end
418
-
419
- if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_str_buffer_as_cstr]) }
420
- lines << "" unless lines.empty?
421
- lines.concat([
422
- "static const char* mt_str_buffer_as_cstr(char* data, uintptr_t cap, uintptr_t* len, bool* dirty) {",
423
- "#{INDENT}(void)mt_str_buffer_len(data, cap, len, dirty);",
424
- "#{INDENT}return data;",
425
- "}",
426
- ])
427
- end
428
-
429
- if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_str_buffer_clear]) }
430
- lines << "" unless lines.empty?
431
- lines.concat([
432
- "static void mt_str_buffer_clear(char* data, uintptr_t cap, uintptr_t* len, bool* dirty) {",
433
- "#{INDENT}memset(data, 0, cap + 1);",
434
- "#{INDENT}*len = 0;",
435
- "#{INDENT}*dirty = false;",
436
- "}",
437
- ])
438
- end
439
-
440
- if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_str_buffer_assign]) }
441
- lines << "" unless lines.empty?
442
- lines.concat([
443
- "static void mt_str_buffer_assign(mt_str value, char* data, uintptr_t cap, uintptr_t* len, bool* dirty) {",
444
- "#{INDENT}if (value.len > cap) mt_fatal(\"str_buffer.assign exceeds capacity\");",
445
- "#{INDENT}memcpy(data, value.data, value.len);",
446
- "#{INDENT}data[value.len] = '\\0';",
447
- "#{INDENT}if (value.len < cap + 1) memset(data + value.len + 1, 0, cap - value.len);",
448
- "#{INDENT}*len = value.len;",
449
- "#{INDENT}*dirty = false;",
450
- "}",
451
- ])
452
- end
453
-
454
- if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_str_buffer_append]) }
455
- lines << "" unless lines.empty?
456
- lines.concat([
457
- "static void mt_str_buffer_append(mt_str value, char* data, uintptr_t cap, uintptr_t* len, bool* dirty) {",
458
- "#{INDENT}uintptr_t current = mt_str_buffer_len(data, cap, len, dirty);",
459
- "#{INDENT}if (value.len > cap - current) mt_fatal(\"str_buffer.append exceeds capacity\");",
460
- "#{INDENT}memcpy(data + current, value.data, value.len);",
461
- "#{INDENT}current += value.len;",
462
- "#{INDENT}data[current] = '\\0';",
463
- "#{INDENT}*len = current;",
464
- "#{INDENT}*dirty = false;",
465
- "}",
466
- ])
467
- end
468
-
469
- if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_str_buffer_prepare_write]) }
470
- lines << "" unless lines.empty?
471
- lines.concat([
472
- "static char* mt_str_buffer_prepare_write(char* data, uintptr_t cap, bool* dirty) {",
473
- "#{INDENT}data[cap] = '\\0';",
474
- "#{INDENT}*dirty = true;",
475
- "#{INDENT}return data;",
476
- "}",
477
- ])
478
- end
479
-
480
- lines
481
- end
482
-
483
- def emit_checked_array_index_helper(type)
484
- helper_name = checked_array_index_helper_name(type)
485
- params = [c_declaration(type, '(*array)'), c_declaration(Types::Registry.primitive('ptr_uint'), 'index')].join(', ')
486
- [
487
- "static inline #{c_function_declaration(pointer_to(array_element_type(type)), helper_name, params)} {",
488
- "#{INDENT}if (index >= #{array_length(type)}) mt_fatal(\"array index out of bounds\");",
489
- "#{INDENT}return &(*array)[index];",
490
- "}",
491
- ]
492
- end
493
-
494
- def emit_checked_span_index_helper(type)
495
- helper_name = checked_span_index_helper_name(type)
496
- params = [c_declaration(type, 'span'), c_declaration(Types::Registry.primitive('ptr_uint'), 'index')].join(', ')
497
- [
498
- "static inline #{c_function_declaration(pointer_to(type.element_type), helper_name, params)} {",
499
- "#{INDENT}if (index >= span.len) mt_fatal(\"span index out of bounds\");",
500
- "#{INDENT}return &span.data[index];",
501
- "}",
502
- ]
503
- end
504
-
505
- def emit_nullable_array_index_helper(type)
506
- helper_name = nullable_array_index_helper_name(type)
507
- params = [c_declaration(type, '(*array)'), c_declaration(Types::Registry.primitive('ptr_uint'), 'index')].join(', ')
508
- [
509
- "static inline #{c_function_declaration(pointer_to(array_element_type(type)), helper_name, params)} {",
510
- "#{INDENT}if (index >= #{array_length(type)}) return NULL;",
511
- "#{INDENT}return &(*array)[index];",
512
- "}",
513
- ]
514
- end
515
-
516
- def emit_nullable_span_index_helper(type)
517
- helper_name = nullable_span_index_helper_name(type)
518
- params = [c_declaration(type, 'span'), c_declaration(Types::Registry.primitive('ptr_uint'), 'index')].join(', ')
519
- [
520
- "static inline #{c_function_declaration(pointer_to(type.element_type), helper_name, params)} {",
521
- "#{INDENT}if (index >= span.len) return NULL;",
522
- "#{INDENT}return &span.data[index];",
523
- "}",
524
- ]
525
- end
526
-
527
- def emit_str_literal_constants(literals)
528
- literals.each_with_index.map do |value, i|
529
- "static const mt_str #{str_literal_name(i)} = { .data = #{value.inspect}, .len = #{value.bytesize} };"
530
- end
531
- end
532
-
533
- def str_literal_name(index)
534
- "mt_str_lit_#{index}"
81
+ lines << "#{INDENT * 3}return true;"
535
82
  end
83
+ end
84
+
85
+ lines << "#{INDENT * 2}default: return true;"
86
+ lines << "#{INDENT}}"
87
+ lines << "}"
88
+ lines
89
+ end
90
+
91
+ def emit_async_memory_helpers
92
+ [
93
+ "#define MT_ASYNC_HEADER_SIZE (sizeof(uint64_t) + sizeof(uintptr_t))",
94
+ "#define MT_ASYNC_MAGIC UINT64_C(0x4D5441464D454D00)",
95
+ "",
96
+ "static void* mt_async_alloc(uintptr_t size) {",
97
+ "#{INDENT}char* raw = (char*) calloc(1, (size_t)(MT_ASYNC_HEADER_SIZE + size));",
98
+ "#{INDENT}if (raw == NULL) {",
99
+ "#{INDENT * 2}abort();",
100
+ "#{INDENT}}",
101
+ "#{INDENT}*(uint64_t*)raw = MT_ASYNC_MAGIC;",
102
+ "#{INDENT}*(uintptr_t*)(raw + sizeof(uint64_t)) = 1;",
103
+ "#{INDENT}return raw + MT_ASYNC_HEADER_SIZE;",
104
+ "}",
105
+ "",
106
+ "static void mt_async_retain(void* frame) {",
107
+ "#{INDENT}char* raw = (char*)frame - MT_ASYNC_HEADER_SIZE;",
108
+ "#{INDENT}if (*(uint64_t*)raw != MT_ASYNC_MAGIC) return;",
109
+ "#{INDENT}uintptr_t* ref = (uintptr_t*)(raw + sizeof(uint64_t));",
110
+ "#{INDENT}(*ref)++;",
111
+ "}",
112
+ "",
113
+ "static void mt_async_free(void* frame) {",
114
+ "#{INDENT}char* raw = (char*)frame - MT_ASYNC_HEADER_SIZE;",
115
+ "#{INDENT}if (*(uint64_t*)raw != MT_ASYNC_MAGIC) return;",
116
+ "#{INDENT}uintptr_t* ref = (uintptr_t*)(raw + sizeof(uint64_t));",
117
+ "#{INDENT}if (--(*ref) == 0) {",
118
+ "#{INDENT * 2}free(raw);",
119
+ "#{INDENT}}",
120
+ "}",
121
+ ]
122
+ end
123
+
124
+ def emit_parallel_for_helper
125
+ [
126
+ "typedef struct {",
127
+ "#{INDENT}void (*work)(void* data, int64_t start, int64_t end);",
128
+ "#{INDENT}void* data;",
129
+ "#{INDENT}int64_t start;",
130
+ "#{INDENT}int64_t end;",
131
+ "} mt_pfor_chunk;",
132
+ "",
133
+ "static void mt_pfor_runner(void* arg) {",
134
+ "#{INDENT}mt_pfor_chunk* chunk = (mt_pfor_chunk*)arg;",
135
+ "#{INDENT}chunk->work(chunk->data, chunk->start, chunk->end);",
136
+ "}",
137
+ "",
138
+ "static void mt_parallel_for(void (*work)(void* data, int64_t start, int64_t end), void* data, int64_t count) {",
139
+ "#{INDENT}if (count <= 0) return;",
140
+ "#{INDENT}uv_cpu_info_t* cpu_info;",
141
+ "#{INDENT}int ncpu = 1;",
142
+ "#{INDENT}if (uv_cpu_info(&cpu_info, &ncpu) == 0) {",
143
+ "#{INDENT * 2}uv_free_cpu_info(cpu_info, ncpu);",
144
+ "#{INDENT}}",
145
+ "#{INDENT}if (ncpu < 1) ncpu = 1;",
146
+ "#{INDENT}if (ncpu > 64) ncpu = 64;",
147
+ "#{INDENT}if (count < (int64_t)ncpu) ncpu = (int)count;",
148
+ "#{INDENT}int64_t chunk_size = (count + ncpu - 1) / ncpu;",
149
+ "#{INDENT}mt_pfor_chunk chunks[64];",
150
+ "#{INDENT}uv_thread_t threads[64];",
151
+ "#{INDENT}int nworkers = 0;",
152
+ "#{INDENT}for (int t = 1; t < ncpu; t++) {",
153
+ "#{INDENT * 2}int64_t s = t * chunk_size;",
154
+ "#{INDENT * 2}int64_t e = s + chunk_size;",
155
+ "#{INDENT * 2}if (e > count) e = count;",
156
+ "#{INDENT * 2}if (s >= count) break;",
157
+ "#{INDENT * 2}chunks[nworkers].work = work;",
158
+ "#{INDENT * 2}chunks[nworkers].data = data;",
159
+ "#{INDENT * 2}chunks[nworkers].start = s;",
160
+ "#{INDENT * 2}chunks[nworkers].end = e;",
161
+ "#{INDENT * 2}uv_thread_create(&threads[nworkers], mt_pfor_runner, &chunks[nworkers]);",
162
+ "#{INDENT * 2}nworkers++;",
163
+ "#{INDENT}}",
164
+ "#{INDENT}int64_t first_end = chunk_size < count ? chunk_size : count;",
165
+ "#{INDENT}work(data, 0, first_end);",
166
+ "#{INDENT}for (int t = 0; t < nworkers; t++) {",
167
+ "#{INDENT * 2}uv_thread_join(&threads[t]);",
168
+ "#{INDENT}}",
169
+ "}",
170
+ ]
171
+ end
172
+
173
+ def emit_spawn_all_helper
174
+ [
175
+ "typedef struct {",
176
+ "#{INDENT}void (*work)(void* data);",
177
+ "#{INDENT}void* data;",
178
+ "} mt_spawn_item;",
179
+ "",
180
+ "static void mt_spawn_item_runner(void* arg) {",
181
+ "#{INDENT}mt_spawn_item* item = (mt_spawn_item*)arg;",
182
+ "#{INDENT}item->work(item->data);",
183
+ "}",
184
+ "",
185
+ "static void mt_spawn_all(mt_spawn_item* items, int count) {",
186
+ "#{INDENT}if (count <= 0) return;",
187
+ "#{INDENT}uv_thread_t threads[64];",
188
+ "#{INDENT}int nworkers = 0;",
189
+ "#{INDENT}for (int t = 1; t < count && nworkers < 63; t++) {",
190
+ "#{INDENT * 2}uv_thread_create(&threads[nworkers], mt_spawn_item_runner, &items[t]);",
191
+ "#{INDENT * 2}nworkers++;",
192
+ "#{INDENT}}",
193
+ "#{INDENT}items[0].work(items[0].data);",
194
+ "#{INDENT}for (int t = 0; t < nworkers; t++) {",
195
+ "#{INDENT * 2}uv_thread_join(&threads[t]);",
196
+ "#{INDENT}}",
197
+ "}",
198
+ ]
199
+ end
200
+
201
+ def emit_detach_helpers
202
+ [
203
+ "typedef struct {",
204
+ "#{INDENT}uv_thread_t thread;",
205
+ "} mt_detach_handle;",
206
+ "",
207
+ "static void* mt_detach_run(void (*work)(void*), void* cap) {",
208
+ "#{INDENT}mt_detach_handle* h = (mt_detach_handle*)malloc(sizeof(mt_detach_handle));",
209
+ "#{INDENT}uv_thread_create(&h->thread, work, cap);",
210
+ "#{INDENT}return h;",
211
+ "}",
212
+ "",
213
+ "static void mt_detach_join(void* handle) {",
214
+ "#{INDENT}if (!handle) return;",
215
+ "#{INDENT}mt_detach_handle* h = (mt_detach_handle*)handle;",
216
+ "#{INDENT}uv_thread_join(&h->thread);",
217
+ "#{INDENT}free(h);",
218
+ "}",
219
+ ]
220
+ end
221
+
222
+ def emit_foreign_temp_cstr_helpers
223
+ lines = []
224
+
225
+ if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_foreign_str_to_cstr_temp]) }
226
+ lines.concat([
227
+ "static const char* mt_foreign_str_to_cstr_temp(mt_str value) {",
228
+ "#{INDENT}char* data = (char*)malloc(value.len + 1);",
229
+ "#{INDENT}uintptr_t index = 0;",
230
+ "#{INDENT}if (data == NULL) mt_fatal(\"foreign str temporary allocation failed\");",
231
+ "#{INDENT}while (index < value.len) {",
232
+ "#{INDENT * 2}data[index] = value.data[index];",
233
+ "#{INDENT * 2}index++;",
234
+ "#{INDENT}}",
235
+ "#{INDENT}data[value.len] = '\\0';",
236
+ "#{INDENT}return data;",
237
+ "}",
238
+ ])
239
+ end
240
+
241
+ if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_free_foreign_cstr_temp]) }
242
+ lines << "" unless lines.empty?
243
+ lines.concat([
244
+ "static void mt_free_foreign_cstr_temp(const char* value) {",
245
+ "#{INDENT}free((void*)value);",
246
+ "}",
247
+ ])
248
+ end
249
+
250
+ if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_foreign_strs_to_cstrs_temp]) }
251
+ lines << "" unless lines.empty?
252
+ lines.concat([
253
+ "static void mt_foreign_strs_to_cstrs_temp(mt_span_str values, char*** items_out, char** data_out, uintptr_t* len_out) {",
254
+ "#{INDENT}uintptr_t total_bytes = 0;",
255
+ "#{INDENT}uintptr_t index = 0;",
256
+ "#{INDENT}uintptr_t offset = 0;",
257
+ "#{INDENT}char** items = NULL;",
258
+ "#{INDENT}char* data = NULL;",
259
+ "#{INDENT}while (index < values.len) {",
260
+ "#{INDENT * 2}total_bytes += values.data[index].len + 1;",
261
+ "#{INDENT * 2}index++;",
262
+ "#{INDENT}}",
263
+ "#{INDENT}if (values.len > 0) {",
264
+ "#{INDENT * 2}items = (char**)malloc(values.len * sizeof(char*));",
265
+ "#{INDENT * 2}if (items == NULL) mt_fatal(\"foreign string-list temporary allocation failed\");",
266
+ "#{INDENT}}",
267
+ "#{INDENT}if (total_bytes > 0) {",
268
+ "#{INDENT * 2}data = (char*)malloc(total_bytes);",
269
+ "#{INDENT * 2}if (data == NULL) {",
270
+ "#{INDENT * 3}free(items);",
271
+ "#{INDENT * 3}mt_fatal(\"foreign string-list temporary allocation failed\");",
272
+ "#{INDENT * 2}}",
273
+ "#{INDENT}}",
274
+ "#{INDENT}index = 0;",
275
+ "#{INDENT}while (index < values.len) {",
276
+ "#{INDENT * 2}mt_str value = values.data[index];",
277
+ "#{INDENT * 2}uintptr_t byte_index = 0;",
278
+ "#{INDENT * 2}items[index] = data + offset;",
279
+ "#{INDENT * 2}while (byte_index < value.len) {",
280
+ "#{INDENT * 3}data[offset + byte_index] = value.data[byte_index];",
281
+ "#{INDENT * 3}byte_index++;",
282
+ "#{INDENT * 2}}",
283
+ "#{INDENT * 2}data[offset + value.len] = '\\0';",
284
+ "#{INDENT * 2}offset += value.len + 1;",
285
+ "#{INDENT * 2}index++;",
286
+ "#{INDENT}}",
287
+ "#{INDENT}*items_out = items;",
288
+ "#{INDENT}*data_out = data;",
289
+ "#{INDENT}*len_out = values.len;",
290
+ "}",
291
+ ])
292
+ end
293
+
294
+ if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_free_foreign_cstrs_temp]) }
295
+ lines << "" unless lines.empty?
296
+ lines.concat([
297
+ "static void mt_free_foreign_cstrs_temp(char** items, char* data) {",
298
+ "#{INDENT}free(items);",
299
+ "#{INDENT}free(data);",
300
+ "}",
301
+ ])
302
+ end
303
+
304
+ lines
305
+ end
306
+
307
+ def emit_entrypoint_argv_helpers
308
+ lines = []
309
+
310
+ if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_entry_argv_to_span_str]) }
311
+ lines.concat([
312
+ "static mt_span_str mt_entry_argv_to_span_str(int32_t argc, char** argv, mt_str** items_out) {",
313
+ "#{INDENT}uintptr_t len = argc > 0 ? (uintptr_t)argc : 0;",
314
+ "#{INDENT}mt_str* items = NULL;",
315
+ "#{INDENT}uintptr_t index = 0;",
316
+ "#{INDENT}if (len > 0) {",
317
+ "#{INDENT * 2}items = (mt_str*)malloc(len * sizeof(mt_str));",
318
+ "#{INDENT * 2}if (items == NULL) abort();",
319
+ "#{INDENT}}",
320
+ "#{INDENT}while (index < len) {",
321
+ "#{INDENT * 2}char* value = argv[index];",
322
+ "#{INDENT * 2}items[index] = (mt_str){ .data = value, .len = (uintptr_t)strlen(value) };",
323
+ "#{INDENT * 2}index++;",
324
+ "#{INDENT}}",
325
+ "#{INDENT}*items_out = items;",
326
+ "#{INDENT}return (mt_span_str){ .data = items, .len = len };",
327
+ "}",
328
+ ])
329
+ end
330
+
331
+ if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_free_entry_argv_strs]) }
332
+ lines << "" unless lines.empty?
333
+ lines.concat([
334
+ "static void mt_free_entry_argv_strs(mt_str* items) {",
335
+ "#{INDENT}free(items);",
336
+ "}",
337
+ ])
338
+ end
339
+
340
+ lines
341
+ end
342
+
343
+ def emit_text_buffer_helpers
344
+ [
345
+ "static bool mt_is_utf8_continuation_byte(unsigned char byte) {",
346
+ "#{INDENT}return (byte & 0xC0u) == 0x80u;",
347
+ "}",
348
+ "",
349
+ "static bool mt_is_valid_utf8(const char* data, uintptr_t len) {",
350
+ "#{INDENT}uintptr_t index = 0;",
351
+ "#{INDENT}while (index < len) {",
352
+ "#{INDENT * 2}unsigned char lead = (unsigned char) data[index];",
353
+ "#{INDENT * 2}if (lead < 0x80u) {",
354
+ "#{INDENT * 3}index++;",
355
+ "#{INDENT * 3}continue;",
356
+ "#{INDENT * 2}}",
357
+ "#{INDENT * 2}if (lead < 0xC2u) return false;",
358
+ "#{INDENT * 2}if (lead < 0xE0u) {",
359
+ "#{INDENT * 3}if (index + 1 >= len) return false;",
360
+ "#{INDENT * 3}unsigned char byte1 = (unsigned char) data[index + 1];",
361
+ "#{INDENT * 3}if (!mt_is_utf8_continuation_byte(byte1)) return false;",
362
+ "#{INDENT * 3}index += 2;",
363
+ "#{INDENT * 3}continue;",
364
+ "#{INDENT * 2}}",
365
+ "#{INDENT * 2}if (lead < 0xF0u) {",
366
+ "#{INDENT * 3}if (index + 2 >= len) return false;",
367
+ "#{INDENT * 3}unsigned char byte1 = (unsigned char) data[index + 1];",
368
+ "#{INDENT * 3}unsigned char byte2 = (unsigned char) data[index + 2];",
369
+ "#{INDENT * 3}if (lead == 0xE0u) {",
370
+ "#{INDENT * 4}if (byte1 < 0xA0u || byte1 > 0xBFu) return false;",
371
+ "#{INDENT * 3}} else if (lead == 0xEDu) {",
372
+ "#{INDENT * 4}if (byte1 < 0x80u || byte1 > 0x9Fu) return false;",
373
+ "#{INDENT * 3}} else if (!mt_is_utf8_continuation_byte(byte1)) {",
374
+ "#{INDENT * 4}return false;",
375
+ "#{INDENT * 3}}",
376
+ "#{INDENT * 3}if (!mt_is_utf8_continuation_byte(byte2)) return false;",
377
+ "#{INDENT * 3}index += 3;",
378
+ "#{INDENT * 3}continue;",
379
+ "#{INDENT * 2}}",
380
+ "#{INDENT * 2}if (lead < 0xF5u) {",
381
+ "#{INDENT * 3}if (index + 3 >= len) return false;",
382
+ "#{INDENT * 3}unsigned char byte1 = (unsigned char) data[index + 1];",
383
+ "#{INDENT * 3}unsigned char byte2 = (unsigned char) data[index + 2];",
384
+ "#{INDENT * 3}unsigned char byte3 = (unsigned char) data[index + 3];",
385
+ "#{INDENT * 3}if (lead == 0xF0u) {",
386
+ "#{INDENT * 4}if (byte1 < 0x90u || byte1 > 0xBFu) return false;",
387
+ "#{INDENT * 3}} else if (lead == 0xF4u) {",
388
+ "#{INDENT * 4}if (byte1 < 0x80u || byte1 > 0x8Fu) return false;",
389
+ "#{INDENT * 3}} else if (!mt_is_utf8_continuation_byte(byte1)) {",
390
+ "#{INDENT * 4}return false;",
391
+ "#{INDENT * 3}}",
392
+ "#{INDENT * 3}if (!mt_is_utf8_continuation_byte(byte2) || !mt_is_utf8_continuation_byte(byte3)) return false;",
393
+ "#{INDENT * 3}index += 4;",
394
+ "#{INDENT * 3}continue;",
395
+ "#{INDENT * 2}}",
396
+ "#{INDENT * 2}return false;",
397
+ "#{INDENT}}",
398
+ "#{INDENT}return true;",
399
+ "}",
400
+ ]
401
+ end
402
+
403
+ def emit_str_buffer_helpers
404
+ lines = []
405
+
406
+ if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_str_buffer_len mt_str_buffer_as_cstr mt_str_buffer_append]) }
407
+ lines.concat([
408
+ "static uintptr_t mt_str_buffer_len(char* data, uintptr_t cap, uintptr_t* len, bool* dirty) {",
409
+ "#{INDENT}if (*dirty) {",
410
+ "#{INDENT * 2}uintptr_t current = 0;",
411
+ "#{INDENT * 2}while (current < cap + 1 && data[current] != '\\0') {",
412
+ "#{INDENT * 3}current++;",
413
+ "#{INDENT * 2}}",
414
+ "#{INDENT * 2}if (current > cap) mt_fatal(\"str_buffer text requires a trailing NUL within capacity\");",
415
+ "#{INDENT * 2}if (!mt_is_valid_utf8(data, current)) mt_fatal(\"str_buffer text must be valid UTF-8\");",
416
+ "#{INDENT * 2}*len = current;",
417
+ "#{INDENT * 2}*dirty = false;",
418
+ "#{INDENT}}",
419
+ "#{INDENT}return *len;",
420
+ "}",
421
+ ])
422
+ end
423
+
424
+ if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_str_buffer_as_cstr]) }
425
+ lines << "" unless lines.empty?
426
+ lines.concat([
427
+ "static const char* mt_str_buffer_as_cstr(char* data, uintptr_t cap, uintptr_t* len, bool* dirty) {",
428
+ "#{INDENT}(void)mt_str_buffer_len(data, cap, len, dirty);",
429
+ "#{INDENT}return data;",
430
+ "}",
431
+ ])
432
+ end
433
+
434
+ if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_str_buffer_clear]) }
435
+ lines << "" unless lines.empty?
436
+ lines.concat([
437
+ "static void mt_str_buffer_clear(char* data, uintptr_t cap, uintptr_t* len, bool* dirty) {",
438
+ "#{INDENT}memset(data, 0, cap + 1);",
439
+ "#{INDENT}*len = 0;",
440
+ "#{INDENT}*dirty = false;",
441
+ "}",
442
+ ])
443
+ end
444
+
445
+ if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_str_buffer_assign]) }
446
+ lines << "" unless lines.empty?
447
+ lines.concat([
448
+ "static void mt_str_buffer_assign(mt_str value, char* data, uintptr_t cap, uintptr_t* len, bool* dirty) {",
449
+ "#{INDENT}if (value.len > cap) mt_fatal(\"str_buffer.assign exceeds capacity\");",
450
+ "#{INDENT}memcpy(data, value.data, value.len);",
451
+ "#{INDENT}data[value.len] = '\\0';",
452
+ "#{INDENT}if (value.len < cap + 1) memset(data + value.len + 1, 0, cap - value.len);",
453
+ "#{INDENT}*len = value.len;",
454
+ "#{INDENT}*dirty = false;",
455
+ "}",
456
+ ])
457
+ end
458
+
459
+ if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_str_buffer_append]) }
460
+ lines << "" unless lines.empty?
461
+ lines.concat([
462
+ "static void mt_str_buffer_append(mt_str value, char* data, uintptr_t cap, uintptr_t* len, bool* dirty) {",
463
+ "#{INDENT}uintptr_t current = mt_str_buffer_len(data, cap, len, dirty);",
464
+ "#{INDENT}if (value.len > cap - current) mt_fatal(\"str_buffer.append exceeds capacity\");",
465
+ "#{INDENT}memcpy(data + current, value.data, value.len);",
466
+ "#{INDENT}current += value.len;",
467
+ "#{INDENT}data[current] = '\\0';",
468
+ "#{INDENT}*len = current;",
469
+ "#{INDENT}*dirty = false;",
470
+ "}",
471
+ ])
472
+ end
473
+
474
+ if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_str_buffer_prepare_write]) }
475
+ lines << "" unless lines.empty?
476
+ lines.concat([
477
+ "static char* mt_str_buffer_prepare_write(char* data, uintptr_t cap, bool* dirty) {",
478
+ "#{INDENT}data[cap] = '\\0';",
479
+ "#{INDENT}*dirty = true;",
480
+ "#{INDENT}return data;",
481
+ "}",
482
+ ])
483
+ end
484
+
485
+ lines
486
+ end
487
+
488
+ def emit_checked_array_index_helper(type)
489
+ helper_name = checked_array_index_helper_name(type)
490
+ params = [c_declaration(type, '(*array)'), c_declaration(Types::Registry.primitive('ptr_uint'), 'index')].join(', ')
491
+ [
492
+ "static inline #{c_function_declaration(pointer_to(array_element_type(type)), helper_name, params)} {",
493
+ "#{INDENT}if (index >= #{array_length(type)}) mt_fatal(\"array index out of bounds\");",
494
+ "#{INDENT}return &(*array)[index];",
495
+ "}",
496
+ ]
497
+ end
498
+
499
+ def emit_checked_span_index_helper(type)
500
+ helper_name = checked_span_index_helper_name(type)
501
+ params = [c_declaration(type, 'span'), c_declaration(Types::Registry.primitive('ptr_uint'), 'index')].join(', ')
502
+ [
503
+ "static inline #{c_function_declaration(pointer_to(type.element_type), helper_name, params)} {",
504
+ "#{INDENT}if (index >= span.len) mt_fatal(\"span index out of bounds\");",
505
+ "#{INDENT}return &span.data[index];",
506
+ "}",
507
+ ]
508
+ end
509
+
510
+ def emit_nullable_array_index_helper(type)
511
+ helper_name = nullable_array_index_helper_name(type)
512
+ params = [c_declaration(type, '(*array)'), c_declaration(Types::Registry.primitive('ptr_uint'), 'index')].join(', ')
513
+ [
514
+ "static inline #{c_function_declaration(pointer_to(array_element_type(type)), helper_name, params)} {",
515
+ "#{INDENT}if (index >= #{array_length(type)}) return NULL;",
516
+ "#{INDENT}return &(*array)[index];",
517
+ "}",
518
+ ]
519
+ end
520
+
521
+ def emit_nullable_span_index_helper(type)
522
+ helper_name = nullable_span_index_helper_name(type)
523
+ params = [c_declaration(type, 'span'), c_declaration(Types::Registry.primitive('ptr_uint'), 'index')].join(', ')
524
+ [
525
+ "static inline #{c_function_declaration(pointer_to(type.element_type), helper_name, params)} {",
526
+ "#{INDENT}if (index >= span.len) return NULL;",
527
+ "#{INDENT}return &span.data[index];",
528
+ "}",
529
+ ]
530
+ end
531
+
532
+ def emit_str_literal_constants(literals)
533
+ literals.each_with_index.map do |value, i|
534
+ "static const mt_str #{str_literal_name(i)} = { .data = #{value.inspect}, .len = #{value.bytesize} };"
535
+ end
536
+ end
537
+
538
+ def str_literal_name(index)
539
+ "mt_str_lit_#{index}"
540
+ end
536
541
  end
537
542
  end
538
543
  end