xbintsc 0.1.8 → 0.1.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 (93) hide show
  1. package/README.md +12 -7
  2. package/dist/src/binder/binder.d.ts +35 -1
  3. package/dist/src/binder/binder.js +138 -0
  4. package/dist/src/binder/binder.js.map +1 -1
  5. package/dist/src/codegen/generator/calls.d.ts +4 -0
  6. package/dist/src/codegen/generator/calls.js +114 -32
  7. package/dist/src/codegen/generator/calls.js.map +1 -1
  8. package/dist/src/codegen/generator/context.d.ts +2 -0
  9. package/dist/src/codegen/generator/context.js +36 -0
  10. package/dist/src/codegen/generator/context.js.map +1 -1
  11. package/dist/src/codegen/generator/expressions.d.ts +4 -1
  12. package/dist/src/codegen/generator/expressions.js +73 -2
  13. package/dist/src/codegen/generator/expressions.js.map +1 -1
  14. package/dist/src/codegen/generator/module.d.ts +5 -1
  15. package/dist/src/codegen/generator/module.js +107 -6
  16. package/dist/src/codegen/generator/module.js.map +1 -1
  17. package/dist/src/codegen/generator/state.d.ts +2 -0
  18. package/dist/src/codegen/generator/state.js +46 -0
  19. package/dist/src/codegen/generator/state.js.map +1 -1
  20. package/dist/src/codegen/generator/statements.js +9 -1
  21. package/dist/src/codegen/generator/statements.js.map +1 -1
  22. package/dist/src/codegen/generator/tables.d.ts +6 -0
  23. package/dist/src/codegen/generator/tables.js +40 -0
  24. package/dist/src/codegen/generator/tables.js.map +1 -1
  25. package/dist/src/driver/compiler.js +23 -7
  26. package/dist/src/driver/compiler.js.map +1 -1
  27. package/dist/src/driver/modules.d.ts +24 -0
  28. package/dist/src/driver/modules.js +347 -0
  29. package/dist/src/driver/modules.js.map +1 -0
  30. package/dist/src/extensions/node/fs/fs-ops.d.ts +6 -0
  31. package/dist/src/extensions/node/fs/fs-ops.js +18 -0
  32. package/dist/src/extensions/node/fs/fs-ops.js.map +1 -0
  33. package/dist/src/extensions/node/fs/index.d.ts +3 -1
  34. package/dist/src/extensions/node/fs/index.js +11 -3
  35. package/dist/src/extensions/node/fs/index.js.map +1 -1
  36. package/dist/src/extensions/node/fs/write-file.d.ts +5 -0
  37. package/dist/src/extensions/node/fs/write-file.js +8 -0
  38. package/dist/src/extensions/node/fs/write-file.js.map +1 -0
  39. package/dist/src/extensions/node/index.d.ts +2 -1
  40. package/dist/src/extensions/node/index.js +7 -3
  41. package/dist/src/extensions/node/index.js.map +1 -1
  42. package/dist/src/extensions/node/os/index.d.ts +9 -0
  43. package/dist/src/extensions/node/os/index.js +14 -0
  44. package/dist/src/extensions/node/os/index.js.map +1 -0
  45. package/dist/src/extensions/node/path/index.d.ts +10 -0
  46. package/dist/src/extensions/node/path/index.js +15 -0
  47. package/dist/src/extensions/node/path/index.js.map +1 -0
  48. package/dist/src/extensions/node/process/index.d.ts +10 -0
  49. package/dist/src/extensions/node/process/index.js +15 -0
  50. package/dist/src/extensions/node/process/index.js.map +1 -0
  51. package/dist/src/parser/expressions.js +3 -0
  52. package/dist/src/parser/expressions.js.map +1 -1
  53. package/dist/tests/codegen/llvm.test.js +1 -1
  54. package/dist/tests/codegen/llvm.test.js.map +1 -1
  55. package/dist/tests/e2e/compile.test.js +141 -0
  56. package/dist/tests/e2e/compile.test.js.map +1 -1
  57. package/dist/tests/extensions/registry.test.js +9 -3
  58. package/dist/tests/extensions/registry.test.js.map +1 -1
  59. package/package.json +1 -1
  60. package/runtime/ext_node/fs/fs_common.h +166 -0
  61. package/runtime/ext_node/fs/fs_ops.c +342 -0
  62. package/runtime/ext_node/fs/read_file.c +12 -6
  63. package/runtime/ext_node/fs/write_file.c +47 -0
  64. package/runtime/ext_node/os/os.c +165 -0
  65. package/runtime/ext_node/path/path.c +324 -0
  66. package/runtime/ext_node/process/process.c +137 -0
  67. package/runtime/rt.h +61 -4
  68. package/runtime/rt_internal.h +63 -0
  69. package/runtime/xt_builtins.c +47 -1
  70. package/runtime/xt_containers.c +95 -8
  71. package/runtime/xt_io.c +122 -0
  72. package/runtime/xt_promise.c +292 -0
  73. package/runtime/xt_stdlib.c +59 -5
  74. package/runtime/xt_stdlib2.c +1043 -0
  75. package/scripts/build-runtime.mjs +5 -0
  76. package/src/binder/binder.ts +196 -0
  77. package/src/codegen/generator/calls.ts +124 -33
  78. package/src/codegen/generator/context.ts +35 -1
  79. package/src/codegen/generator/expressions.ts +79 -2
  80. package/src/codegen/generator/module.ts +112 -6
  81. package/src/codegen/generator/state.ts +48 -0
  82. package/src/codegen/generator/statements.ts +7 -1
  83. package/src/codegen/generator/tables.ts +43 -0
  84. package/src/driver/compiler.ts +28 -7
  85. package/src/driver/modules.ts +377 -0
  86. package/src/extensions/node/fs/fs-ops.ts +20 -0
  87. package/src/extensions/node/fs/index.ts +11 -3
  88. package/src/extensions/node/fs/write-file.ts +10 -0
  89. package/src/extensions/node/index.ts +7 -3
  90. package/src/extensions/node/os/index.ts +16 -0
  91. package/src/extensions/node/path/index.ts +17 -0
  92. package/src/extensions/node/process/index.ts +17 -0
  93. package/src/parser/expressions.ts +3 -0
@@ -160,6 +160,7 @@ xt_value xt_closure_new(void *fn, int32_t env_count, xt_value *env) {
160
160
  function->arity = -1;
161
161
  function->properties = NULL;
162
162
  function->environment = NULL;
163
+ function->prototype = XT_UNDEFINED;
163
164
  if (env_count > 0) {
164
165
  function->environment = (xt_value *)malloc(sizeof(xt_value) * (size_t)env_count);
165
166
  for (int32_t i = 0; i < env_count; i++) function->environment[i] = env[i];
@@ -168,6 +169,10 @@ xt_value xt_closure_new(void *fn, int32_t env_count, xt_value *env) {
168
169
  }
169
170
 
170
171
  xt_value xt_closure_call(xt_value value, int32_t argc, xt_value *argv) {
172
+ return xt_call_with_this(value, XT_UNDEFINED, argc, argv);
173
+ }
174
+
175
+ xt_value xt_call_with_this(xt_value value, xt_value thisValue, int32_t argc, xt_value *argv) {
171
176
  if (!XT_IS_FUNCTION(value)) {
172
177
  xt_throw(xt_string_from_cstr("TypeError: value is not a function"));
173
178
  return XT_UNDEFINED;
@@ -175,7 +180,48 @@ xt_value xt_closure_call(xt_value value, int32_t argc, xt_value *argv) {
175
180
  xt_function *function = (xt_function *)XT_GET_PTR(value);
176
181
  /* The closure value itself is threaded through as `env` so the callee can
177
182
  * read its captures with xt_closure_env(env, i). */
178
- return function->code(value, argc, argv);
183
+ return function->code(thisValue, value, argc, argv);
184
+ }
185
+
186
+ xt_value xt_this(void) { return XT_UNDEFINED; }
187
+
188
+ xt_value xt_function_set_prototype(xt_value value, xt_value proto) {
189
+ if (XT_IS_FUNCTION(value)) ((xt_function *)XT_GET_PTR(value))->prototype = proto;
190
+ return value;
191
+ }
192
+
193
+ xt_value xt_function_get_prototype(xt_value value) {
194
+ if (XT_IS_FUNCTION(value)) return ((xt_function *)XT_GET_PTR(value))->prototype;
195
+ return XT_UNDEFINED;
196
+ }
197
+
198
+ xt_value xt_new(xt_value ctor, int32_t argc, xt_value *argv) {
199
+ if (!XT_IS_FUNCTION(ctor)) {
200
+ xt_throw(xt_string_from_cstr("TypeError: constructor is not a function"));
201
+ return XT_UNDEFINED;
202
+ }
203
+ xt_function *function = (xt_function *)XT_GET_PTR(ctor);
204
+ xt_value proto = function->prototype;
205
+ xt_value instance = xt_object_new_with_proto(XT_IS_OBJECT(proto) ? proto : XT_UNDEFINED);
206
+ xt_value result = function->code(instance, ctor, argc, argv);
207
+ if (XT_IS_OBJECT(result) || XT_IS_ARRAY(result)) return result;
208
+ return instance;
209
+ }
210
+
211
+ xt_value xt_instance_of(xt_value value, xt_value ctor) {
212
+ if (!XT_IS_FUNCTION(ctor)) return XT_FALSE;
213
+ xt_value proto = ((xt_function *)XT_GET_PTR(ctor))->prototype;
214
+ if (!XT_IS_OBJECT(proto)) return XT_FALSE;
215
+ if (!XT_IS_OBJECT(value)) return XT_FALSE;
216
+ xt_object *cur = (xt_object *)XT_GET_PTR(value);
217
+ xt_object *target = (xt_object *)XT_GET_PTR(proto);
218
+ cur = (xt_object *)XT_GET_PTR(XT_IS_OBJECT(cur->prototype) ? cur->prototype : XT_UNDEFINED);
219
+ while (cur) {
220
+ if (cur == target) return XT_TRUE;
221
+ if (!XT_IS_OBJECT(cur->prototype)) break;
222
+ cur = (xt_object *)XT_GET_PTR(cur->prototype);
223
+ }
224
+ return XT_FALSE;
179
225
  }
180
226
 
181
227
  xt_value xt_closure_env(xt_value value, int32_t index) {
@@ -28,14 +28,30 @@ static void xt_object_reserve(xt_object *obj, uint32_t needed) {
28
28
  obj->capacity = capacity;
29
29
  }
30
30
 
31
- xt_value xt_object_new(void) {
31
+ xt_value xt_object_new(void) { return xt_object_new_with_proto(XT_UNDEFINED); }
32
+
33
+ xt_value xt_object_new_with_proto(xt_value proto) {
32
34
  xt_object *obj = (xt_object *)xt_alloc(sizeof(xt_object), XT_OBJECT_KIND_OBJECT);
33
35
  obj->count = 0;
34
36
  obj->capacity = 0;
35
37
  obj->properties = NULL;
38
+ obj->prototype = proto;
39
+ obj->frozen = 0;
40
+ obj->is_array_base = 0;
36
41
  return XT_FROM_PTR(XT_TAG_OBJECT, obj);
37
42
  }
38
43
 
44
+ xt_object *xt_as_object(xt_value value) {
45
+ if (XT_IS_OBJECT(value)) return (xt_object *)XT_GET_PTR(value);
46
+ if (XT_IS_FUNCTION(value)) {
47
+ /* Functions carry their own property bag (used for static members). */
48
+ xt_function *fn = (xt_function *)XT_GET_PTR(value);
49
+ if (!fn->properties) fn->properties = (xt_object *)XT_GET_PTR(xt_object_new_with_proto(XT_UNDEFINED));
50
+ return fn->properties;
51
+ }
52
+ return NULL;
53
+ }
54
+
39
55
  static xt_property *xt_object_find(xt_object *obj, xt_string *key) {
40
56
  for (uint32_t i = 0; i < obj->count; i++) {
41
57
  if (xt_string_equals(obj->properties[i].key, key)) return &obj->properties[i];
@@ -43,17 +59,30 @@ static xt_property *xt_object_find(xt_object *obj, xt_string *key) {
43
59
  return NULL;
44
60
  }
45
61
 
62
+ xt_property *xt_object_find_property(xt_object *obj, xt_string *key) {
63
+ return obj ? xt_object_find(obj, key) : NULL;
64
+ }
65
+
46
66
  xt_value xt_object_get(xt_value value, xt_value key) {
47
- if (!XT_IS_OBJECT(value)) return XT_UNDEFINED;
48
- xt_object *obj = (xt_object *)XT_GET_PTR(value);
67
+ xt_object *obj = xt_as_object(value);
68
+ if (!obj) return XT_UNDEFINED;
49
69
  xt_value keyString = xt_to_string(key);
50
- xt_property *prop = xt_object_find(obj, xt_as_string(keyString));
51
- return prop ? prop->value : XT_UNDEFINED;
70
+ xt_string *k = xt_as_string(keyString);
71
+ /* Walk the prototype chain like JavaScript property lookup. */
72
+ xt_object *cur = obj;
73
+ while (cur) {
74
+ xt_property *prop = xt_object_find(cur, k);
75
+ if (prop) return prop->value;
76
+ if (!XT_IS_OBJECT(cur->prototype)) break;
77
+ cur = (xt_object *)XT_GET_PTR(cur->prototype);
78
+ }
79
+ return XT_UNDEFINED;
52
80
  }
53
81
 
54
82
  xt_value xt_object_set(xt_value value, xt_value key, xt_value newValue) {
55
- if (!XT_IS_OBJECT(value)) return newValue;
56
- xt_object *obj = (xt_object *)XT_GET_PTR(value);
83
+ xt_object *obj = xt_as_object(value);
84
+ if (!obj) return newValue;
85
+ if (obj->frozen) return newValue;
57
86
  xt_value keyString = xt_to_string(key);
58
87
  xt_property *prop = xt_object_find(obj, xt_as_string(keyString));
59
88
  if (prop) {
@@ -67,6 +96,48 @@ xt_value xt_object_set(xt_value value, xt_value key, xt_value newValue) {
67
96
  return newValue;
68
97
  }
69
98
 
99
+ xt_value xt_object_get_prototype(xt_value value) {
100
+ xt_object *obj = xt_as_object(value);
101
+ if (obj) return obj->prototype;
102
+ return XT_UNDEFINED;
103
+ }
104
+
105
+ xt_value xt_object_set_prototype(xt_value value, xt_value proto) {
106
+ xt_object *obj = xt_as_object(value);
107
+ if (obj && !obj->frozen) obj->prototype = proto;
108
+ return value;
109
+ }
110
+
111
+ xt_value xt_object_freeze(xt_value value) {
112
+ xt_object *obj = xt_as_object(value);
113
+ if (obj) obj->frozen = 1;
114
+ return value;
115
+ }
116
+
117
+ int xt_object_is_frozen(xt_value value) {
118
+ xt_object *obj = xt_as_object(value);
119
+ return obj ? obj->frozen : 0;
120
+ }
121
+
122
+ xt_value xt_object_has_own(xt_value value, xt_value key) {
123
+ xt_object *obj = xt_as_object(value);
124
+ if (!obj) return XT_FALSE;
125
+ xt_value keyString = xt_to_string(key);
126
+ return xt_bool(xt_object_find(obj, xt_as_string(keyString)) != NULL);
127
+ }
128
+
129
+ xt_value xt_object_from_entries(xt_value entries) {
130
+ xt_value result = xt_object_new();
131
+ xt_array *array = xt_as_array(entries);
132
+ if (!array) return result;
133
+ for (uint32_t i = 0; i < array->length; i++) {
134
+ xt_array *pair = xt_as_array(array->items[i]);
135
+ if (!pair || pair->length < 2) continue;
136
+ xt_object_set(result, pair->items[0], pair->items[1]);
137
+ }
138
+ return result;
139
+ }
140
+
70
141
  xt_value xt_object_has(xt_value value, xt_value key) {
71
142
  if (!XT_IS_OBJECT(value)) return XT_FALSE;
72
143
  xt_object *obj = (xt_object *)XT_GET_PTR(value);
@@ -190,7 +261,22 @@ xt_value xt_array_spread(xt_value target, xt_value source) {
190
261
 
191
262
  xt_value xt_get(xt_value target, xt_value key) {
192
263
  if (XT_IS_ARRAY(target)) return xt_array_get(target, key);
193
- if (XT_IS_OBJECT(target)) return xt_object_get(target, key);
264
+ if (XT_IS_FUNCTION(target)) return xt_object_get(target, key);
265
+ if (XT_IS_OBJECT(target)) {
266
+ /* Map/Set expose `.size`; promises, dates and regexps have their own
267
+ * getters implemented in the standard library. */
268
+ xt_object *obj = (xt_object *)XT_GET_PTR(target);
269
+ if (obj->header.kind == XT_OBJECT_KIND_MAP || obj->header.kind == XT_OBJECT_KIND_SET) {
270
+ if (XT_IS_STRING(key)) {
271
+ xt_string *k = xt_as_string(key);
272
+ if (k->length == 4 && memcmp(k->data, "size", 4) == 0) {
273
+ int32_t size = obj->header.kind == XT_OBJECT_KIND_MAP ? xt_map_size(target) : xt_set_size(target);
274
+ return xt_number((double)size);
275
+ }
276
+ }
277
+ }
278
+ return xt_object_get(target, key);
279
+ }
194
280
  if (XT_IS_STRING(target)) {
195
281
  /* Property access on a string: `.length` and numeric indexing. */
196
282
  xt_string *s = xt_as_string(target);
@@ -207,6 +293,7 @@ xt_value xt_get(xt_value target, xt_value key) {
207
293
 
208
294
  xt_value xt_set(xt_value target, xt_value key, xt_value value) {
209
295
  if (XT_IS_ARRAY(target)) return xt_array_set(target, key, value);
296
+ if (XT_IS_FUNCTION(target)) return xt_object_set(target, key, value);
210
297
  if (XT_IS_OBJECT(target)) return xt_object_set(target, key, value);
211
298
  return value;
212
299
  }
package/runtime/xt_io.c CHANGED
@@ -12,6 +12,7 @@
12
12
  #include <stdio.h>
13
13
  #include <stdlib.h>
14
14
  #include <string.h>
15
+ #include <time.h>
15
16
 
16
17
  /* ------------------------------------------------------------------------- */
17
18
  /* Exceptions and output */
@@ -31,6 +32,15 @@ typedef struct xt_try_frame {
31
32
 
32
33
  static xt_try_frame *g_try_top = NULL;
33
34
 
35
+ /* Program arguments, captured once by `xt_set_program_args` from `main`. */
36
+ int32_t xt_program_argc = 0;
37
+ char **xt_program_argv = NULL;
38
+
39
+ void xt_set_program_args(int32_t argc, char **argv) {
40
+ xt_program_argc = argc;
41
+ xt_program_argv = argv;
42
+ }
43
+
34
44
  void *xt_try_enter(void) {
35
45
  xt_try_frame *frame = (xt_try_frame *)calloc(1, sizeof(xt_try_frame));
36
46
  if (!frame) abort();
@@ -131,3 +141,115 @@ static void xt_console_write(int32_t argc, xt_value *argv, FILE *out) {
131
141
  void xt_console_info(int32_t argc, xt_value *argv) { xt_console_write(argc, argv, stdout); }
132
142
  void xt_console_warn(int32_t argc, xt_value *argv) { xt_console_write(argc, argv, stderr); }
133
143
  void xt_console_error(int32_t argc, xt_value *argv) { xt_console_write(argc, argv, stderr); }
144
+
145
+ xt_value xt_console_dir(int32_t argc, xt_value *argv) {
146
+ xt_console_write(argc, argv, stdout);
147
+ return XT_UNDEFINED;
148
+ }
149
+
150
+ xt_value xt_console_trace(int32_t argc, xt_value *argv) {
151
+ fputs("Trace", stderr);
152
+ for (int32_t i = 0; i < argc; i++) {
153
+ fputc(' ', stderr);
154
+ xt_inspect(argv[i], stderr);
155
+ }
156
+ fputc('\n', stderr);
157
+ return XT_UNDEFINED;
158
+ }
159
+
160
+ xt_value xt_console_assert(int32_t argc, xt_value *argv) {
161
+ if (argc == 0 || !xt_truthy(argv[0])) {
162
+ fputs("Assertion failed", stderr);
163
+ if (argc > 1) fputs(":", stderr);
164
+ for (int32_t i = 1; i < argc; i++) {
165
+ fputc(' ', stderr);
166
+ xt_inspect(argv[i], stderr);
167
+ }
168
+ fputc('\n', stderr);
169
+ }
170
+ return XT_UNDEFINED;
171
+ }
172
+
173
+ #define XT_CONSOLE_MAX_LABELS 32
174
+ static char *xt_console_labels[XT_CONSOLE_MAX_LABELS];
175
+ static long xt_console_counts[XT_CONSOLE_MAX_LABELS];
176
+ static clock_t xt_console_timers[XT_CONSOLE_MAX_LABELS];
177
+ static int xt_console_label_count = 0;
178
+ static int xt_console_depth = 0;
179
+
180
+ static int xt_console_find_label(const char *label) {
181
+ for (int i = 0; i < xt_console_label_count; i++) {
182
+ if (strcmp(xt_console_labels[i], label) == 0) return i;
183
+ }
184
+ if (xt_console_label_count >= XT_CONSOLE_MAX_LABELS) return -1;
185
+ int index = xt_console_label_count++;
186
+ xt_console_labels[index] = (char *)malloc(strlen(label) + 1);
187
+ strcpy(xt_console_labels[index], label);
188
+ xt_console_counts[index] = 0;
189
+ xt_console_timers[index] = 0;
190
+ return index;
191
+ }
192
+
193
+ static const char *xt_console_label(int32_t argc, xt_value *argv) {
194
+ if (argc == 0) return "default";
195
+ return xt_string_data(xt_to_string(argv[0]));
196
+ }
197
+
198
+ xt_value xt_console_count(int32_t argc, xt_value *argv) {
199
+ const char *label = xt_console_label(argc, argv);
200
+ int index = xt_console_find_label(label ? label : "default");
201
+ if (index < 0) return XT_UNDEFINED;
202
+ xt_console_counts[index]++;
203
+ printf("%s: %ld\n", xt_console_labels[index], xt_console_counts[index]);
204
+ return XT_UNDEFINED;
205
+ }
206
+
207
+ xt_value xt_console_count_reset(int32_t argc, xt_value *argv) {
208
+ const char *label = xt_console_label(argc, argv);
209
+ int index = xt_console_find_label(label ? label : "default");
210
+ if (index >= 0) xt_console_counts[index] = 0;
211
+ return XT_UNDEFINED;
212
+ }
213
+
214
+ xt_value xt_console_group(int32_t argc, xt_value *argv) {
215
+ xt_console_write(argc, argv, stdout);
216
+ xt_console_depth++;
217
+ return XT_UNDEFINED;
218
+ }
219
+
220
+ xt_value xt_console_group_end(int32_t argc, xt_value *argv) {
221
+ (void)argc;
222
+ (void)argv;
223
+ if (xt_console_depth > 0) xt_console_depth--;
224
+ return XT_UNDEFINED;
225
+ }
226
+
227
+ xt_value xt_console_table(int32_t argc, xt_value *argv) {
228
+ xt_console_write(argc, argv, stdout);
229
+ return XT_UNDEFINED;
230
+ }
231
+
232
+ xt_value xt_console_time(int32_t argc, xt_value *argv) {
233
+ const char *label = xt_console_label(argc, argv);
234
+ int index = xt_console_find_label(label ? label : "default");
235
+ if (index >= 0) xt_console_timers[index] = clock();
236
+ return XT_UNDEFINED;
237
+ }
238
+
239
+ xt_value xt_console_time_log(int32_t argc, xt_value *argv) {
240
+ const char *label = xt_console_label(argc, argv);
241
+ int index = xt_console_find_label(label ? label : "default");
242
+ if (index >= 0 && xt_console_timers[index] != 0) {
243
+ double ms = (double)(clock() - xt_console_timers[index]) * 1000.0 / CLOCKS_PER_SEC;
244
+ printf("%s: %.3fms\n", xt_console_labels[index], ms);
245
+ }
246
+ return XT_UNDEFINED;
247
+ }
248
+
249
+ xt_value xt_console_time_end(int32_t argc, xt_value *argv) {
250
+ xt_console_time_log(argc, argv);
251
+ const char *label = xt_console_label(argc, argv);
252
+ int index = xt_console_find_label(label ? label : "default");
253
+ if (index >= 0) xt_console_timers[index] = 0;
254
+ return XT_UNDEFINED;
255
+ }
@@ -0,0 +1,292 @@
1
+ /*
2
+ * xbintsc runtime — promises and async support.
3
+ *
4
+ * There is no event loop in the runtime, so promises use a microtask queue
5
+ * that the compiler drains when the program body finishes (and that `await`
6
+ * drains on demand). This gives synchronous-looking `async`/`await` semantics
7
+ * for the common case where every awaited value is already settled.
8
+ */
9
+
10
+ #include "rt_internal.h"
11
+
12
+ #include <stdlib.h>
13
+ #include <string.h>
14
+
15
+ #define XT_PROMISE_PENDING 0
16
+ #define XT_PROMISE_FULFILLED 1
17
+ #define XT_PROMISE_REJECTED 2
18
+
19
+ typedef struct xt_promise xt_promise;
20
+
21
+ typedef struct {
22
+ xt_promise *promise;
23
+ xt_value on_fulfilled;
24
+ xt_value on_rejected;
25
+ xt_promise *result;
26
+ } xt_reaction;
27
+
28
+ struct xt_promise {
29
+ xt_header header;
30
+ int state;
31
+ xt_value value;
32
+ xt_reaction **reactions;
33
+ uint32_t reaction_count;
34
+ uint32_t reaction_capacity;
35
+ };
36
+
37
+ /* -- microtask queue ------------------------------------------------------ */
38
+
39
+ static xt_reaction **xt_microtasks = NULL;
40
+ static uint32_t xt_microtask_count = 0;
41
+ static uint32_t xt_microtask_head = 0;
42
+ static uint32_t xt_microtask_capacity = 0;
43
+
44
+ static void xt_microtask_push(xt_reaction *reaction) {
45
+ if (xt_microtask_count >= xt_microtask_capacity) {
46
+ xt_microtask_capacity = xt_microtask_capacity == 0 ? 16 : xt_microtask_capacity * 2;
47
+ xt_microtasks = (xt_reaction **)realloc(xt_microtasks, sizeof(xt_reaction *) * xt_microtask_capacity);
48
+ if (!xt_microtasks) abort();
49
+ }
50
+ xt_microtasks[xt_microtask_count++] = reaction;
51
+ }
52
+
53
+ int xt_is_promise(xt_value value) {
54
+ return XT_IS_OBJECT(value) && ((xt_object *)XT_GET_PTR(value))->header.kind == XT_OBJECT_KIND_PROMISE;
55
+ }
56
+
57
+ static xt_promise *xt_as_promise(xt_value value) {
58
+ if (!xt_is_promise(value)) return NULL;
59
+ return (xt_promise *)XT_GET_PTR(value);
60
+ }
61
+
62
+ static xt_value xt_promise_value(xt_promise *promise) {
63
+ return XT_FROM_PTR(XT_TAG_OBJECT, promise);
64
+ }
65
+
66
+ static xt_promise *xt_promise_new(void) {
67
+ xt_promise *promise = (xt_promise *)xt_alloc(sizeof(xt_promise), XT_OBJECT_KIND_PROMISE);
68
+ promise->state = XT_PROMISE_PENDING;
69
+ promise->value = XT_UNDEFINED;
70
+ promise->reactions = NULL;
71
+ promise->reaction_count = 0;
72
+ promise->reaction_capacity = 0;
73
+ return promise;
74
+ }
75
+
76
+ static void xt_promise_settle(xt_promise *promise, int state, xt_value value);
77
+
78
+ static void xt_promise_resolve_into(xt_promise *result, xt_value value) {
79
+ xt_promise *other = xt_as_promise(value);
80
+ if (!other) {
81
+ xt_promise_settle(result, XT_PROMISE_FULFILLED, value);
82
+ return;
83
+ }
84
+ if (other->state == XT_PROMISE_PENDING) {
85
+ xt_reaction *reaction = (xt_reaction *)malloc(sizeof(xt_reaction));
86
+ if (!reaction) abort();
87
+ reaction->promise = other;
88
+ reaction->on_fulfilled = XT_UNDEFINED;
89
+ reaction->on_rejected = XT_UNDEFINED;
90
+ reaction->result = result;
91
+ if (other->reaction_count >= other->reaction_capacity) {
92
+ other->reaction_capacity = other->reaction_capacity == 0 ? 4 : other->reaction_capacity * 2;
93
+ other->reactions = (xt_reaction **)realloc(other->reactions, sizeof(xt_reaction *) * other->reaction_capacity);
94
+ if (!other->reactions) abort();
95
+ }
96
+ other->reactions[other->reaction_count++] = reaction;
97
+ } else {
98
+ xt_promise_settle(result, other->state, other->value);
99
+ }
100
+ }
101
+
102
+ static void xt_promise_settle(xt_promise *promise, int state, xt_value value) {
103
+ if (promise->state != XT_PROMISE_PENDING) return;
104
+ promise->state = state;
105
+ promise->value = value;
106
+ for (uint32_t i = 0; i < promise->reaction_count; i++) xt_microtask_push(promise->reactions[i]);
107
+ promise->reaction_count = 0;
108
+ }
109
+
110
+ static xt_value xt_promise_run_reactions(void) {
111
+ while (xt_microtask_head < xt_microtask_count) {
112
+ xt_reaction *reaction = xt_microtasks[xt_microtask_head++];
113
+ xt_promise *promise = reaction->promise;
114
+ xt_value handler = promise->state == XT_PROMISE_FULFILLED ? reaction->on_fulfilled : reaction->on_rejected;
115
+ if (XT_IS_FUNCTION(handler)) {
116
+ xt_value arg = promise->value;
117
+ xt_value out;
118
+ out = xt_closure_call(handler, 1, &arg);
119
+ xt_promise_resolve_into(reaction->result, out);
120
+ } else if (promise->state == XT_PROMISE_FULFILLED) {
121
+ xt_promise_settle(reaction->result, XT_PROMISE_FULFILLED, promise->value);
122
+ } else {
123
+ xt_promise_settle(reaction->result, XT_PROMISE_REJECTED, promise->value);
124
+ }
125
+ }
126
+ xt_microtask_head = 0;
127
+ xt_microtask_count = 0;
128
+ return XT_UNDEFINED;
129
+ }
130
+
131
+ void xt_drain_microtasks(void) {
132
+ for (int guard = 0; guard < 100000; guard++) {
133
+ if (xt_microtask_head >= xt_microtask_count) break;
134
+ xt_promise_run_reactions();
135
+ }
136
+ }
137
+
138
+ xt_value xt_await(xt_value value) {
139
+ xt_promise *promise = xt_as_promise(value);
140
+ if (!promise) return value;
141
+ int guard = 0;
142
+ while (promise->state == XT_PROMISE_PENDING && guard++ < 100000) xt_drain_microtasks();
143
+ if (promise->state == XT_PROMISE_REJECTED) {
144
+ xt_throw(promise->value);
145
+ return XT_UNDEFINED;
146
+ }
147
+ return promise->value;
148
+ }
149
+
150
+ /* -- C-callable resolve / reject ------------------------------------------ */
151
+
152
+ static xt_value xt_promise_resolve_cb(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
153
+ (void)thisValue;
154
+ xt_value promiseValue = xt_closure_env(env, 0);
155
+ xt_promise *promise = xt_as_promise(promiseValue);
156
+ if (promise) xt_promise_resolve_into(promise, xt_arg_at(argc, argv, 0));
157
+ return XT_UNDEFINED;
158
+ }
159
+
160
+ static xt_value xt_promise_reject_cb(xt_value thisValue, xt_value env, int32_t argc, xt_value *argv) {
161
+ (void)thisValue;
162
+ xt_value promiseValue = xt_closure_env(env, 0);
163
+ xt_promise *promise = xt_as_promise(promiseValue);
164
+ if (promise) xt_promise_settle(promise, XT_PROMISE_REJECTED, xt_arg_at(argc, argv, 0));
165
+ return XT_UNDEFINED;
166
+ }
167
+
168
+ static xt_value xt_make_callback(xt_code_fn fn, xt_value promiseValue) {
169
+ xt_value env[1] = {promiseValue};
170
+ return xt_closure_new((void *)fn, 1, env);
171
+ }
172
+
173
+ xt_value xt_promise_ctor(int32_t argc, xt_value *argv) {
174
+ xt_promise *promise = xt_promise_new();
175
+ xt_value promiseValue = xt_promise_value(promise);
176
+ xt_value executor = xt_arg_at(argc, argv, 0);
177
+ if (XT_IS_FUNCTION(executor)) {
178
+ xt_value args[2];
179
+ args[0] = xt_make_callback(xt_promise_resolve_cb, promiseValue);
180
+ args[1] = xt_make_callback(xt_promise_reject_cb, promiseValue);
181
+ xt_closure_call(executor, 2, args);
182
+ }
183
+ return promiseValue;
184
+ }
185
+
186
+ xt_value xt_promise_resolve(xt_value value) {
187
+ xt_promise *existing = xt_as_promise(value);
188
+ if (existing) return value;
189
+ xt_promise *promise = xt_promise_new();
190
+ xt_promise_resolve_into(promise, value);
191
+ return xt_promise_value(promise);
192
+ }
193
+
194
+ xt_value xt_promise_reject(xt_value value) {
195
+ xt_promise *promise = xt_promise_new();
196
+ xt_promise_settle(promise, XT_PROMISE_REJECTED, value);
197
+ return xt_promise_value(promise);
198
+ }
199
+
200
+ xt_value xt_promise_static(xt_value name, int32_t argc, xt_value *argv) {
201
+ const char *fn = xt_string_data(name);
202
+ if (!fn) return XT_UNDEFINED;
203
+ if (strcmp(fn, "resolve") == 0) return xt_promise_resolve(xt_arg_at(argc, argv, 0));
204
+ if (strcmp(fn, "reject") == 0) return xt_promise_reject(xt_arg_at(argc, argv, 0));
205
+ if (strcmp(fn, "all") == 0 || strcmp(fn, "allSettled") == 0 || strcmp(fn, "race") == 0 || strcmp(fn, "any") == 0) {
206
+ xt_value iterable = xt_arg_at(argc, argv, 0);
207
+ int all = strcmp(fn, "all") == 0 || strcmp(fn, "allSettled") == 0;
208
+ xt_promise *result = xt_promise_new();
209
+ xt_value values = xt_array_new(0, NULL);
210
+ if (XT_IS_ARRAY(iterable)) {
211
+ xt_array *array = xt_as_array(iterable);
212
+ int anyRejected = 0;
213
+ for (uint32_t i = 0; i < array->length; i++) {
214
+ xt_value item = array->items[i];
215
+ xt_promise *inner = xt_is_promise(item) ? xt_as_promise(item) : NULL;
216
+ int rejected = inner && inner->state == XT_PROMISE_REJECTED;
217
+ if (strcmp(fn, "allSettled") == 0) {
218
+ xt_value record = xt_object_new();
219
+ xt_object_set(record, xt_string_from_cstr("status"), xt_string_from_cstr(rejected ? "rejected" : "fulfilled"));
220
+ xt_object_set(record, xt_string_from_cstr(rejected ? "reason" : "value"), rejected ? inner->value : item);
221
+ xt_array_push(values, record);
222
+ continue;
223
+ }
224
+ if (rejected) {
225
+ if (strcmp(fn, "any") == 0) {
226
+ anyRejected++;
227
+ continue;
228
+ }
229
+ xt_promise_settle(result, XT_PROMISE_REJECTED, inner->value);
230
+ return xt_promise_value(result);
231
+ }
232
+ xt_value settled = xt_await(item);
233
+ if (strcmp(fn, "any") == 0 || strcmp(fn, "race") == 0) {
234
+ xt_promise_settle(result, XT_PROMISE_FULFILLED, settled);
235
+ return xt_promise_value(result);
236
+ }
237
+ xt_array_push(values, settled);
238
+ }
239
+ if (strcmp(fn, "any") == 0 && anyRejected > 0) {
240
+ xt_promise_settle(result, XT_PROMISE_REJECTED, xt_string_from_cstr("AggregateError: all promises rejected"));
241
+ return xt_promise_value(result);
242
+ }
243
+ }
244
+ if (all) xt_promise_settle(result, XT_PROMISE_FULFILLED, values);
245
+ else xt_promise_settle(result, XT_PROMISE_REJECTED, xt_string_from_cstr("AggregateError: no promises"));
246
+ return xt_promise_value(result);
247
+ }
248
+ return XT_UNDEFINED;
249
+ }
250
+
251
+ /* -- instance methods ----------------------------------------------------- */
252
+
253
+ xt_value xt_promise_method(xt_value target, const char *method, int32_t argc, xt_value *argv, int *handled) {
254
+ *handled = 1;
255
+ xt_promise *promise = xt_as_promise(target);
256
+ if (!promise) {
257
+ *handled = 0;
258
+ return XT_UNDEFINED;
259
+ }
260
+ if (strcmp(method, "then") == 0) {
261
+ xt_promise *result = xt_promise_new();
262
+ xt_reaction *reaction = (xt_reaction *)malloc(sizeof(xt_reaction));
263
+ if (!reaction) abort();
264
+ reaction->promise = promise;
265
+ reaction->on_fulfilled = xt_arg_at(argc, argv, 0);
266
+ reaction->on_rejected = xt_arg_at(argc, argv, 1);
267
+ reaction->result = result;
268
+ if (promise->state == XT_PROMISE_PENDING) {
269
+ if (promise->reaction_count >= promise->reaction_capacity) {
270
+ promise->reaction_capacity = promise->reaction_capacity == 0 ? 4 : promise->reaction_capacity * 2;
271
+ promise->reactions = (xt_reaction **)realloc(promise->reactions, sizeof(xt_reaction *) * promise->reaction_capacity);
272
+ if (!promise->reactions) abort();
273
+ }
274
+ promise->reactions[promise->reaction_count++] = reaction;
275
+ } else {
276
+ xt_microtask_push(reaction);
277
+ }
278
+ return xt_promise_value(result);
279
+ }
280
+ if (strcmp(method, "catch") == 0) {
281
+ xt_value args[2] = {XT_UNDEFINED, xt_arg_at(argc, argv, 0)};
282
+ return xt_promise_method(target, "then", 2, args, handled);
283
+ }
284
+ if (strcmp(method, "finally") == 0) {
285
+ xt_value fn = xt_arg_at(argc, argv, 0);
286
+ /* Approximate: run the handler, then propagate the original value. */
287
+ if (XT_IS_FUNCTION(fn)) xt_closure_call(fn, 0, NULL);
288
+ return target;
289
+ }
290
+ *handled = 0;
291
+ return XT_UNDEFINED;
292
+ }