mt-lang 0.3.9 → 0.3.11

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 (204) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -0
  3. data/docs/language-design.md +1 -2
  4. data/docs/language-manual.md +12 -1
  5. data/lib/milk_tea/base.rb +1 -2
  6. data/lib/milk_tea/bindings/bindgen/ast_parser.rb +0 -2
  7. data/lib/milk_tea/bindings/bindgen/declaration.rb +0 -2
  8. data/lib/milk_tea/bindings/bindgen/emitter.rb +0 -2
  9. data/lib/milk_tea/bindings/bindgen/overrides.rb +0 -2
  10. data/lib/milk_tea/bindings/bindgen/type_mapper.rb +51 -53
  11. data/lib/milk_tea/bindings/imported_bindings/generator.rb +7 -9
  12. data/lib/milk_tea/bindings/imported_bindings/method_source.rb +0 -2
  13. data/lib/milk_tea/bindings/imported_bindings/naming.rb +6 -8
  14. data/lib/milk_tea/bindings/imported_bindings.rb +0 -2
  15. data/lib/milk_tea/bindings/opengl_registry.rb +3 -7
  16. data/lib/milk_tea/bindings/steamworks.rb +12 -16
  17. data/lib/milk_tea/bindings/upstream_sources.rb +10 -14
  18. data/lib/milk_tea/bindings/vendored_box2d.rb +10 -12
  19. data/lib/milk_tea/bindings/vendored_cjson.rb +6 -8
  20. data/lib/milk_tea/bindings/vendored_flecs.rb +10 -12
  21. data/lib/milk_tea/bindings/vendored_glfw.rb +11 -13
  22. data/lib/milk_tea/bindings/vendored_libuv.rb +11 -13
  23. data/lib/milk_tea/bindings/vendored_pcre2.rb +11 -13
  24. data/lib/milk_tea/bindings/vendored_raylib.rb +6 -10
  25. data/lib/milk_tea/bindings/vendored_sdl3.rb +11 -13
  26. data/lib/milk_tea/bindings/vendored_steamworks.rb +1 -5
  27. data/lib/milk_tea/core/ast.rb +109 -51
  28. data/lib/milk_tea/core/async_runtime_installer.rb +0 -2
  29. data/lib/milk_tea/core/bindings/attribute_binding.rb +1 -3
  30. data/lib/milk_tea/core/c_backend/aggregate_utils.rb +147 -84
  31. data/lib/milk_tea/core/c_backend/control_flow_emission.rb +394 -396
  32. data/lib/milk_tea/core/c_backend/expressions.rb +489 -476
  33. data/lib/milk_tea/core/c_backend/feature_detection.rb +446 -437
  34. data/lib/milk_tea/core/c_backend/format_helpers.rb +0 -2
  35. data/lib/milk_tea/core/c_backend/reachability.rb +339 -341
  36. data/lib/milk_tea/core/c_backend/reinterpret.rb +231 -217
  37. data/lib/milk_tea/core/c_backend/runtime_helpers.rb +531 -526
  38. data/lib/milk_tea/core/c_backend/statements.rb +528 -530
  39. data/lib/milk_tea/core/c_backend/type_collectors.rb +1344 -1346
  40. data/lib/milk_tea/core/c_backend/type_declaration.rb +215 -213
  41. data/lib/milk_tea/core/c_backend/type_system.rb +347 -349
  42. data/lib/milk_tea/core/c_backend.rb +17 -9
  43. data/lib/milk_tea/core/compatibility_helpers.rb +0 -2
  44. data/lib/milk_tea/core/compile_time/const_eval.rb +7 -12
  45. data/lib/milk_tea/core/compile_time.rb +32 -38
  46. data/lib/milk_tea/core/control_flow/builder.rb +3 -5
  47. data/lib/milk_tea/core/control_flow/constant_propagation.rb +14 -17
  48. data/lib/milk_tea/core/control_flow/definite_assignment.rb +0 -1
  49. data/lib/milk_tea/core/ir.rb +10 -10
  50. data/lib/milk_tea/core/lexer/character_classes.rb +5 -7
  51. data/lib/milk_tea/core/lexer/format_strings.rb +7 -9
  52. data/lib/milk_tea/core/lexer/heredocs.rb +22 -22
  53. data/lib/milk_tea/core/lexer/indentation.rb +0 -2
  54. data/lib/milk_tea/core/lexer/numbers.rb +0 -2
  55. data/lib/milk_tea/core/lexer/recovery.rb +0 -2
  56. data/lib/milk_tea/core/lexer/strings.rb +0 -2
  57. data/lib/milk_tea/core/lexer/symbols.rb +0 -2
  58. data/lib/milk_tea/core/lexer/trivia.rb +0 -2
  59. data/lib/milk_tea/core/lexer.rb +0 -2
  60. data/lib/milk_tea/core/lowering/async/analysis.rb +0 -2
  61. data/lib/milk_tea/core/lowering/async/lowering.rb +1264 -1265
  62. data/lib/milk_tea/core/lowering/async/normalization.rb +5 -3
  63. data/lib/milk_tea/core/lowering/async.rb +627 -630
  64. data/lib/milk_tea/core/lowering/block.rb +413 -416
  65. data/lib/milk_tea/core/lowering/calls.rb +1381 -1384
  66. data/lib/milk_tea/core/lowering/declarations.rb +180 -183
  67. data/lib/milk_tea/core/lowering/dyn.rb +48 -50
  68. data/lib/milk_tea/core/lowering/events.rb +983 -985
  69. data/lib/milk_tea/core/lowering/expressions.rb +1513 -1518
  70. data/lib/milk_tea/core/lowering/foreign_cstr.rb +181 -184
  71. data/lib/milk_tea/core/lowering/functions.rb +185 -188
  72. data/lib/milk_tea/core/lowering/loops.rb +1028 -1031
  73. data/lib/milk_tea/core/lowering/proc.rb +363 -366
  74. data/lib/milk_tea/core/lowering/resolve.rb +2133 -2135
  75. data/lib/milk_tea/core/lowering/scans.rb +179 -181
  76. data/lib/milk_tea/core/lowering/str_buffer.rb +102 -105
  77. data/lib/milk_tea/core/lowering/utils.rb +1040 -1044
  78. data/lib/milk_tea/core/lowering.rb +5 -7
  79. data/lib/milk_tea/core/module_binder.rb +0 -2
  80. data/lib/milk_tea/core/module_loader/errors.rb +4 -2
  81. data/lib/milk_tea/core/module_loader.rb +113 -20
  82. data/lib/milk_tea/core/module_path_resolver.rb +0 -2
  83. data/lib/milk_tea/core/module_roots.rb +6 -8
  84. data/lib/milk_tea/core/parser/attributes.rb +0 -2
  85. data/lib/milk_tea/core/parser/blocks.rb +0 -2
  86. data/lib/milk_tea/core/parser/declarations.rb +7 -9
  87. data/lib/milk_tea/core/parser/expressions.rb +15 -10
  88. data/lib/milk_tea/core/parser/recovery.rb +0 -2
  89. data/lib/milk_tea/core/parser/statements.rb +8 -2
  90. data/lib/milk_tea/core/parser/types.rb +12 -14
  91. data/lib/milk_tea/core/parser.rb +0 -2
  92. data/lib/milk_tea/core/pretty_printer/ast_formatter.rb +31 -33
  93. data/lib/milk_tea/core/pretty_printer/base_formatter.rb +0 -2
  94. data/lib/milk_tea/core/pretty_printer/ir_formatter.rb +0 -2
  95. data/lib/milk_tea/core/semantic_analyzer/analysis_context.rb +0 -2
  96. data/lib/milk_tea/core/semantic_analyzer/attributes.rb +14 -16
  97. data/lib/milk_tea/core/semantic_analyzer/calls.rb +0 -2
  98. data/lib/milk_tea/core/semantic_analyzer/expressions.rb +1 -14
  99. data/lib/milk_tea/core/semantic_analyzer/flow_refinement.rb +18 -21
  100. data/lib/milk_tea/core/semantic_analyzer/foreign_functions.rb +0 -3
  101. data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +14 -17
  102. data/lib/milk_tea/core/semantic_analyzer/generics.rb +57 -59
  103. data/lib/milk_tea/core/semantic_analyzer/interface_conformance.rb +0 -2
  104. data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +5 -9
  105. data/lib/milk_tea/core/semantic_analyzer/nullability.rb +0 -3
  106. data/lib/milk_tea/core/semantic_analyzer/statements.rb +35 -40
  107. data/lib/milk_tea/core/semantic_analyzer/top_level.rb +10 -12
  108. data/lib/milk_tea/core/semantic_analyzer/type_compatibility.rb +0 -6
  109. data/lib/milk_tea/core/semantic_analyzer/type_declaration.rb +106 -109
  110. data/lib/milk_tea/core/semantic_analyzer.rb +17 -8
  111. data/lib/milk_tea/core/types/layout.rb +21 -22
  112. data/lib/milk_tea/core/types/predicates.rb +0 -2
  113. data/lib/milk_tea/core/types/registry.rb +16 -18
  114. data/lib/milk_tea/core/types/visitor.rb +0 -4
  115. data/lib/milk_tea/dap/backends/lldb_dap.rb +0 -2
  116. data/lib/milk_tea/dap/protocol.rb +1 -1
  117. data/lib/milk_tea/dap/server/breakpoints.rb +0 -2
  118. data/lib/milk_tea/dap/server/debug_map.rb +0 -2
  119. data/lib/milk_tea/dap/server/handlers.rb +0 -2
  120. data/lib/milk_tea/dap/server/launch.rb +0 -2
  121. data/lib/milk_tea/dap/server/lldb_backend.rb +8 -10
  122. data/lib/milk_tea/dap/server/pause_diagnostics.rb +14 -16
  123. data/lib/milk_tea/dap/server/utilities.rb +0 -2
  124. data/lib/milk_tea/dap/server/wire.rb +0 -2
  125. data/lib/milk_tea/dap/server.rb +0 -2
  126. data/lib/milk_tea/lsp/diagnostics.rb +16 -16
  127. data/lib/milk_tea/lsp/server/call_hierarchy.rb +11 -12
  128. data/lib/milk_tea/lsp/server/code_actions.rb +355 -357
  129. data/lib/milk_tea/lsp/server/code_lens.rb +23 -25
  130. data/lib/milk_tea/lsp/server/completion.rb +873 -875
  131. data/lib/milk_tea/lsp/server/configuration.rb +129 -131
  132. data/lib/milk_tea/lsp/server/debug_info.rb +0 -2
  133. data/lib/milk_tea/lsp/server/definition.rb +564 -565
  134. data/lib/milk_tea/lsp/server/diagnostics_scheduling.rb +191 -193
  135. data/lib/milk_tea/lsp/server/execute_command.rb +0 -2
  136. data/lib/milk_tea/lsp/server/folding_range.rb +0 -4
  137. data/lib/milk_tea/lsp/server/formatting.rb +478 -480
  138. data/lib/milk_tea/lsp/server/hover.rb +1282 -1284
  139. data/lib/milk_tea/lsp/server/inlay_hints.rb +179 -181
  140. data/lib/milk_tea/lsp/server/lifecycle.rb +192 -194
  141. data/lib/milk_tea/lsp/server/linked_editing_range.rb +0 -2
  142. data/lib/milk_tea/lsp/server/on_type_formatting.rb +0 -2
  143. data/lib/milk_tea/lsp/server/progress.rb +0 -2
  144. data/lib/milk_tea/lsp/server/references.rb +316 -318
  145. data/lib/milk_tea/lsp/server/rename.rb +445 -447
  146. data/lib/milk_tea/lsp/server/selection_range.rb +0 -4
  147. data/lib/milk_tea/lsp/server/semantic_tokens.rb +1376 -1314
  148. data/lib/milk_tea/lsp/server/signature_help.rb +146 -148
  149. data/lib/milk_tea/lsp/server/text_documents.rb +99 -101
  150. data/lib/milk_tea/lsp/server/type_hierarchy.rb +2 -4
  151. data/lib/milk_tea/lsp/server/utilities.rb +397 -399
  152. data/lib/milk_tea/lsp/server.rb +0 -3
  153. data/lib/milk_tea/lsp/workspace/analysis.rb +40 -42
  154. data/lib/milk_tea/lsp/workspace/dependency_graph.rb +4 -10
  155. data/lib/milk_tea/lsp/workspace/utilities.rb +19 -19
  156. data/lib/milk_tea/packages/atomic_write.rb +4 -5
  157. data/lib/milk_tea/packages/dependency_solver.rb +15 -15
  158. data/lib/milk_tea/packages/lock.rb +4 -4
  159. data/lib/milk_tea/packages/manager_cli.rb +1 -1
  160. data/lib/milk_tea/packages/manifest.rb +3 -3
  161. data/lib/milk_tea/packages/registry_store.rb +4 -6
  162. data/lib/milk_tea/packages/services.rb +16 -16
  163. data/lib/milk_tea/packages/source_cache.rb +4 -6
  164. data/lib/milk_tea/packages/source_resolver.rb +44 -44
  165. data/lib/milk_tea/packages/version.rb +8 -8
  166. data/lib/milk_tea/tooling/build.rb +3 -7
  167. data/lib/milk_tea/tooling/build_cache.rb +0 -2
  168. data/lib/milk_tea/tooling/cli.rb +10 -4
  169. data/lib/milk_tea/tooling/debug_info_formatter.rb +31 -33
  170. data/lib/milk_tea/tooling/debug_map.rb +8 -10
  171. data/lib/milk_tea/tooling/docs_app.rb +0 -2
  172. data/lib/milk_tea/tooling/linter/doc_tags.rb +25 -27
  173. data/lib/milk_tea/tooling/linter/fix_engine.rb +87 -24
  174. data/lib/milk_tea/tooling/linter/flow_rules.rb +44 -46
  175. data/lib/milk_tea/tooling/linter/imports_platform.rb +14 -16
  176. data/lib/milk_tea/tooling/linter/release_rules.rb +3 -5
  177. data/lib/milk_tea/tooling/linter/reserved_names.rb +23 -25
  178. data/lib/milk_tea/tooling/linter/rules.rb +87 -89
  179. data/lib/milk_tea/tooling/linter/source_helpers.rb +40 -42
  180. data/lib/milk_tea/tooling/linter/trailing_comma.rb +17 -19
  181. data/lib/milk_tea/tooling/linter/visitors.rb +112 -54
  182. data/lib/milk_tea/tooling/linter.rb +0 -2
  183. data/lib/milk_tea/tooling/project_scaffold.rb +0 -2
  184. data/lib/milk_tea/tooling/run.rb +0 -2
  185. data/lib/milk_tea/tooling/toolchain_cli.rb +0 -2
  186. data/std/cli.mt +13 -32
  187. data/std/color.mt +28 -28
  188. data/std/encoding.mt +4 -4
  189. data/std/graph.mt +1 -2
  190. data/std/hash.mt +14 -14
  191. data/std/http/server.mt +3 -3
  192. data/std/input.mt +1 -1
  193. data/std/json.mt +1 -1
  194. data/std/linear_algebra.mt +6 -6
  195. data/std/net/mux.mt +1 -1
  196. data/std/noise.mt +23 -23
  197. data/std/parse.mt +2 -2
  198. data/std/process.mt +3 -4
  199. data/std/sparse_set.mt +1 -1
  200. data/std/steering.mt +5 -5
  201. data/std/str.mt +1 -1
  202. data/std/tween.mt +4 -4
  203. data/std/utility.mt +3 -3
  204. 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