@scriptc/cli-darwin-x64 0.2.3 → 0.2.5

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 (141) hide show
  1. package/dist/THIRD_PARTY_NOTICES +1 -1
  2. package/dist/bin/scriptc +0 -0
  3. package/dist/bin/scriptc.json +1 -1
  4. package/dist/lib/declarations/scriptc-node-fallback.d.ts +915 -215
  5. package/dist/lib/declarations/scriptc-overrides.d.ts +5 -1
  6. package/dist/lib/declarations/scriptc.d.ts +22 -2
  7. package/dist/lib/llvm/bin/scriptc-llvm-codegen +0 -0
  8. package/dist/lib/llvm/package.json +1 -1
  9. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_array.o +0 -0
  10. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_bigint.o +0 -0
  11. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_bytes.o +0 -0
  12. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_bytes_io.o +0 -0
  13. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_copying.o +0 -0
  14. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_cycle.o +0 -0
  15. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_json.o +0 -0
  16. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_lib.o +0 -0
  17. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_map.o +0 -0
  18. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_number.o +0 -0
  19. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_regex.o +0 -0
  20. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_string.o +0 -0
  21. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_util_compare.o +0 -0
  22. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_array.o +0 -0
  23. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_bigint.o +0 -0
  24. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_bytes.o +0 -0
  25. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_bytes_io.o +0 -0
  26. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_copying.o +0 -0
  27. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_cycle.o +0 -0
  28. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_json.o +0 -0
  29. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_lib.o +0 -0
  30. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_map.o +0 -0
  31. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_number.o +0 -0
  32. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_regex.o +0 -0
  33. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_string.o +0 -0
  34. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_util_compare.o +0 -0
  35. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic-text-decoder-legacy/scr_bytes.o +0 -0
  36. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/text-decoder-legacy/scr_bytes.o +0 -0
  37. package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_array.o +0 -0
  38. package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_bigint.o +0 -0
  39. package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_bytes.o +0 -0
  40. package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_bytes_io.o +0 -0
  41. package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_copying.o +0 -0
  42. package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_cycle.o +0 -0
  43. package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_json.o +0 -0
  44. package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_lib.o +0 -0
  45. package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_map.o +0 -0
  46. package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_number.o +0 -0
  47. package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_regex.o +0 -0
  48. package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_string.o +0 -0
  49. package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/text-decoder-legacy/scr_bytes.o +0 -0
  50. package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_array.o +0 -0
  51. package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_bigint.o +0 -0
  52. package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_bytes.o +0 -0
  53. package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_bytes_io.o +0 -0
  54. package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_copying.o +0 -0
  55. package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_cycle.o +0 -0
  56. package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_json.o +0 -0
  57. package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_lib.o +0 -0
  58. package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_map.o +0 -0
  59. package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_number.o +0 -0
  60. package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_regex.o +0 -0
  61. package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_string.o +0 -0
  62. package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/text-decoder-legacy/scr_bytes.o +0 -0
  63. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_array.o +0 -0
  64. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_bigint.o +0 -0
  65. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_bytes.o +0 -0
  66. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_bytes_io.o +0 -0
  67. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_copying.o +0 -0
  68. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_cycle.o +0 -0
  69. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_json.o +0 -0
  70. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_lib.o +0 -0
  71. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_map.o +0 -0
  72. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_number.o +0 -0
  73. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_regex.o +0 -0
  74. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_string.o +0 -0
  75. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/text-decoder-legacy/scr_bytes.o +0 -0
  76. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_array.o +0 -0
  77. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_bigint.o +0 -0
  78. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_bytes.o +0 -0
  79. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_bytes_io.o +0 -0
  80. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_copying.o +0 -0
  81. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_cycle.o +0 -0
  82. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_json.o +0 -0
  83. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_lib.o +0 -0
  84. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_map.o +0 -0
  85. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_number.o +0 -0
  86. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_regex.o +0 -0
  87. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_string.o +0 -0
  88. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/text-decoder-legacy/scr_bytes.o +0 -0
  89. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_array.o +0 -0
  90. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_bigint.o +0 -0
  91. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_bytes.o +0 -0
  92. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_bytes_io.o +0 -0
  93. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_copying.o +0 -0
  94. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_cycle.o +0 -0
  95. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_json.o +0 -0
  96. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_lib.o +0 -0
  97. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_map.o +0 -0
  98. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_number.o +0 -0
  99. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_regex.o +0 -0
  100. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_string.o +0 -0
  101. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_util_compare.o +0 -0
  102. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_array.o +0 -0
  103. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_bigint.o +0 -0
  104. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_bytes.o +0 -0
  105. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_bytes_io.o +0 -0
  106. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_copying.o +0 -0
  107. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_cycle.o +0 -0
  108. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_json.o +0 -0
  109. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_lib.o +0 -0
  110. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_map.o +0 -0
  111. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_number.o +0 -0
  112. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_regex.o +0 -0
  113. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_string.o +0 -0
  114. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_util_compare.o +0 -0
  115. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic-text-decoder-legacy/scr_bytes.o +0 -0
  116. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/text-decoder-legacy/scr_bytes.o +0 -0
  117. package/dist/lib/runtime-darwin-x64/package.json +1 -1
  118. package/dist/lib/runtime-darwin-x64/runtime-pack.json +215 -215
  119. package/dist/lib/runtime-sources/package.json +1 -1
  120. package/dist/lib/runtime-sources/src/scr_array.c +317 -40
  121. package/dist/lib/runtime-sources/src/scr_async.test.ts +16 -6
  122. package/dist/lib/runtime-sources/src/scr_bigint.c +15 -0
  123. package/dist/lib/runtime-sources/src/scr_bytes.c +437 -216
  124. package/dist/lib/runtime-sources/src/scr_bytes.test.c +85 -0
  125. package/dist/lib/runtime-sources/src/scr_bytes_io.c +0 -34
  126. package/dist/lib/runtime-sources/src/scr_copying.c +12 -2
  127. package/dist/lib/runtime-sources/src/scr_cycle.c +84 -60
  128. package/dist/lib/runtime-sources/src/scr_json.c +494 -161
  129. package/dist/lib/runtime-sources/src/scr_json.test.c +323 -1
  130. package/dist/lib/runtime-sources/src/scr_json.test.ts +58 -25
  131. package/dist/lib/runtime-sources/src/scr_key.h +68 -0
  132. package/dist/lib/runtime-sources/src/scr_lib.c +177 -89
  133. package/dist/lib/runtime-sources/src/scr_map.c +268 -96
  134. package/dist/lib/runtime-sources/src/scr_number.c +2 -4
  135. package/dist/lib/runtime-sources/src/scr_numeric.h +25 -0
  136. package/dist/lib/runtime-sources/src/scr_numeric.test.c +37 -0
  137. package/dist/lib/runtime-sources/src/scr_numeric.test.ts +33 -0
  138. package/dist/lib/runtime-sources/src/scr_regex.c +255 -145
  139. package/dist/lib/runtime-sources/src/scr_runtime.h +76 -19
  140. package/dist/lib/runtime-sources/src/scr_string.c +184 -42
  141. package/package.json +1 -1
@@ -1,6 +1,13 @@
1
1
  #include "scr_runtime.h"
2
2
  #include <assert.h>
3
+ #include <math.h>
3
4
  #include <stdio.h>
5
+ #include <string.h>
6
+
7
+ #ifdef SCR_RC_AUDIT
8
+ long scr_str_live_count(void);
9
+ long scr_dyn_key_live_count(void);
10
+ #endif
4
11
 
5
12
  static void put(ScrDyn *map, ScrDyn *key, ScrDyn *value) {
6
13
  ScrDyn *args[] = {key, value};
@@ -20,8 +27,323 @@ static ScrDyn *nothing(ScrClosure *closure, ScrDyn *const *args, size_t argc) {
20
27
  return scr_dyn_retain(scr_dyn_undefined());
21
28
  }
22
29
 
30
+ static void json_string_boundaries(void) {
31
+ /* Every escape/control at every word alignment, followed by raw UTF-8.
32
+ * ASan also checks short tails that cannot supply a complete word. */
33
+ const char suffix[] = "tail\xF0\x9F\x99\x82\xE9\x9B\xAA";
34
+ for (size_t prefix = 0; prefix < 64; prefix++) {
35
+ for (unsigned special = 0; special < 34; special++) {
36
+ char bytes[96];
37
+ memset(bytes, 'a', prefix);
38
+ bytes[prefix] = special < 32 ? (char)special : special == 32 ? '"' : '\\';
39
+ memcpy(bytes + prefix + 1, suffix, sizeof suffix - 1);
40
+ size_t length = prefix + sizeof suffix;
41
+ ScrStr *original = scr_str_new(bytes, length);
42
+ ScrJsonBuf buffer;
43
+ scr_jb_init(&buffer);
44
+ scr_jb_put_json_str(&buffer, original);
45
+ ScrStr *encoded = scr_jb_finish(&buffer);
46
+ ScrDyn *parsed = scr_json_parse(encoded);
47
+ assert(parsed && parsed->kind == SCR_DYN_STR && !scr_exc_pending());
48
+ assert(scr_str_eq(original, parsed->v.str));
49
+ scr_dyn_release(parsed);
50
+ scr_str_release(encoded);
51
+ scr_str_release(original);
52
+
53
+ if (special < 32) {
54
+ char invalid[68];
55
+ invalid[0] = '"';
56
+ memset(invalid + 1, 'a', prefix);
57
+ invalid[prefix + 1] = (char)special;
58
+ invalid[prefix + 2] = '"';
59
+ ScrStr *input = scr_str_new(invalid, prefix + 3);
60
+ assert(!scr_json_parse(input) && scr_exc_pending());
61
+ scr_exc_clear();
62
+ scr_str_release(input);
63
+ }
64
+ }
65
+ char plain[68];
66
+ plain[0] = '"';
67
+ memset(plain + 1, 'z', prefix);
68
+ plain[prefix + 1] = '"';
69
+ ScrStr *input = scr_str_new(plain, prefix + 2);
70
+ ScrDyn *parsed = scr_json_parse(input);
71
+ assert(parsed && parsed->v.str->len == prefix && !scr_exc_pending());
72
+ scr_dyn_release(parsed);
73
+ scr_str_release(input);
74
+ input = scr_str_new(plain, prefix + 1);
75
+ assert(!scr_json_parse(input) && scr_exc_pending());
76
+ scr_exc_clear();
77
+ scr_str_release(input);
78
+ }
79
+ }
80
+
81
+ static void checked_storage(void) {
82
+ /* Reuse after deletion, changing field order, embedded NUL and escaped
83
+ * duplicate names must leave independently owned keys and values. */
84
+ for (int round = 0; round < 100; round++) {
85
+ const char *text = round % 2 ? "{\"alpha\":1,\"beta\":2,\"gamma\":3}"
86
+ : "{\"gamma\":3,\"alpha\":0,\"\\u0061lpha\":1,\"beta\":2}";
87
+ ScrStr *input = scr_str_new(text, strlen(text));
88
+ ScrDyn *object = scr_json_parse(input);
89
+ scr_str_release(input);
90
+ assert(object && object->v.obj.len == 3);
91
+ assert(scr_dyn_obj_get(object, "alpha", 5)->v.num == 1);
92
+ ScrStr *key = scr_str_new("beta", 4);
93
+ scr_dyn_key_delete(object, key, true);
94
+ scr_str_release(key);
95
+ assert(!scr_dyn_obj_get(object, "beta", 4));
96
+ assert(scr_dyn_obj_get(object, "gamma", 5)->v.num == 3);
97
+ char mutable_key[] = "new\0key";
98
+ scr_dyn_obj_set(object, mutable_key, sizeof mutable_key - 1, scr_dyn_new_num(4));
99
+ mutable_key[0] = 'x';
100
+ assert(scr_dyn_obj_get(object, "new\0key", 7)->v.num == 4);
101
+ scr_dyn_release(object);
102
+ }
103
+ /* Larger objects and long names leave no stale pooled tail keys when a
104
+ * later small object grows through the same capacity. */
105
+ for (int round = 0; round < 4; round++) {
106
+ ScrDyn *object = scr_dyn_new_obj();
107
+ for (int i = 0; i < 80; i++) {
108
+ char key[160];
109
+ memset(key, 'a' + i % 20, sizeof key);
110
+ int prefix = snprintf(key, sizeof key, "%d:", i);
111
+ memset(key + prefix, 'a' + i % 20, sizeof key - (size_t)prefix);
112
+ scr_dyn_obj_set(object, key, sizeof key, scr_dyn_new_num(i));
113
+ assert(scr_dyn_obj_get(object, key, sizeof key)->v.num == i);
114
+ }
115
+ scr_dyn_release(object);
116
+ }
117
+ /* Cross index growth boundaries, invalidate moved entry positions, then
118
+ * recreate the index from survivors. Repeat after pool reuse. */
119
+ for (int round = 0; round < 4; round++) {
120
+ ScrDyn *object = scr_dyn_new_obj();
121
+ assert(!object->v.obj.index);
122
+ for (int i = 0; i < 129; i++) {
123
+ char key[16];
124
+ int length = snprintf(key, sizeof key, "key-%d", i);
125
+ scr_dyn_obj_set(object, key, (size_t)length, scr_dyn_new_num(i));
126
+ assert((object->v.obj.index != NULL) == (i >= 31));
127
+ }
128
+ for (int i = 0; i < 129; i++) {
129
+ char key[16];
130
+ int length = snprintf(key, sizeof key, "key-%d", i);
131
+ assert(scr_dyn_obj_get(object, key, (size_t)length)->v.num == i);
132
+ if (i % 3 == 0) {
133
+ ScrStr *name = scr_str_new(key, (size_t)length);
134
+ scr_dyn_key_delete(object, name, true);
135
+ scr_str_release(name);
136
+ assert(!scr_dyn_obj_get(object, key, (size_t)length));
137
+ }
138
+ }
139
+ scr_dyn_obj_set(object, "last", 4, scr_dyn_new_num(129));
140
+ assert(object->v.obj.index);
141
+ for (int i = 0; i < 129; i++) {
142
+ char key[16];
143
+ int length = snprintf(key, sizeof key, "key-%d", i);
144
+ ScrDyn *value = scr_dyn_obj_get(object, key, (size_t)length);
145
+ assert(i % 3 == 0 ? value == NULL : value && value->v.num == i);
146
+ }
147
+ scr_dyn_obj_set(object, "key-128", 7, scr_dyn_new_num(900));
148
+ assert(object->v.obj.len == 87);
149
+ assert(scr_dyn_obj_get(object, "key-128", 7)->v.num == 900);
150
+ scr_dyn_release(object);
151
+ }
152
+ /* A direct step is authorized only by the captured builtin method. */
153
+ ScrDyn *source = scr_dyn_new_arr();
154
+ scr_dyn_arr_push(source, scr_dyn_new_num(7));
155
+ scr_dyn_arr_push(source, scr_dyn_retain(scr_dyn_undefined()));
156
+ ScrStr *spell = scr_str_new("source", 6);
157
+ ScrDyn *iterator = scr_dyn_iterator(source, spell);
158
+ scr_str_release(spell);
159
+ ScrDyn *next = scr_dyn_handle_ops_of(iterator)->get(iterator->v.handle.ptr, "next", 4);
160
+ assert(scr_dyn_iterator_can_step(iterator, next));
161
+ assert(!scr_dyn_iterator_can_step(source, next));
162
+ assert(!scr_dyn_iterator_can_step(iterator, scr_dyn_undefined()));
163
+ ScrDyn *item = scr_dyn_iterator_step(iterator);
164
+ assert(item->v.num == 7 && !scr_dyn_iterator_step_done(iterator));
165
+ scr_dyn_release(item);
166
+ item = scr_dyn_iterator_step(iterator);
167
+ assert(item->kind == SCR_DYN_UNDEF && !scr_dyn_iterator_step_done(iterator));
168
+ scr_dyn_release(item);
169
+ scr_dyn_arr_push(source, scr_dyn_new_num(9));
170
+ item = scr_dyn_iterator_step(iterator);
171
+ assert(item->v.num == 9 && !scr_dyn_iterator_step_done(iterator));
172
+ scr_dyn_release(item);
173
+ item = scr_dyn_iterator_step(iterator);
174
+ assert(item->kind == SCR_DYN_UNDEF && scr_dyn_iterator_step_done(iterator));
175
+ scr_dyn_release(item);
176
+ scr_dyn_arr_push(source, scr_dyn_new_num(11));
177
+ item = scr_dyn_iterator_step(iterator);
178
+ assert(item->kind == SCR_DYN_UNDEF && scr_dyn_iterator_step_done(iterator));
179
+ scr_dyn_release(item);
180
+ scr_dyn_release(next);
181
+ scr_dyn_release(iterator);
182
+ scr_dyn_release(source);
183
+ assert(!scr_exc_pending());
184
+ }
185
+
186
+ static void shared_property_keys(void) {
187
+ #ifdef SCR_RC_AUDIT
188
+ long before = scr_dyn_key_live_count();
189
+ #endif
190
+ /* Distinct objects share bytes, not values or property attributes. Key
191
+ * deletion, a collected owner and a caller-owned input cannot invalidate
192
+ * the surviving entry. */
193
+ ScrDyn *first = scr_dyn_new_obj(), *second = scr_dyn_new_obj();
194
+ char key[] = "shared\0name";
195
+ scr_dyn_obj_set(first, key, sizeof key - 1, scr_dyn_new_num(1));
196
+ scr_dyn_obj_set(second, key, sizeof key - 1, scr_dyn_new_num(2));
197
+ assert(first->v.obj.entries[0].key == second->v.obj.entries[0].key);
198
+ key[0] = 'x';
199
+ ScrStr *name = scr_str_new("shared\0name", sizeof key - 1);
200
+ scr_dyn_key_delete(first, name, true);
201
+ scr_str_release(name);
202
+ assert(scr_dyn_obj_get(second, "shared\0name", sizeof key - 1)->v.num == 2);
203
+ scr_dyn_obj_set(first, "shared\0name", sizeof key - 1, scr_dyn_new_num(3));
204
+ scr_dyn_obj_set(first, "self", 4, scr_dyn_retain(first));
205
+ scr_dyn_release(first);
206
+ scr_collect_cycles();
207
+ assert(scr_dyn_obj_get(second, "shared\0name", sizeof key - 1)->v.num == 2);
208
+ scr_dyn_release(second);
209
+
210
+ /* More distinct live names than weak slots force collisions. Releasing
211
+ * an evicted owner must not clear or free the replacement's storage. */
212
+ ScrDyn *objects[600];
213
+ for (int round = 0; round < 2; round++) {
214
+ for (int i = 0; i < 600; i++) {
215
+ char bytes[32];
216
+ int length = snprintf(bytes, sizeof bytes, "field-%d", i);
217
+ objects[i] = scr_dyn_new_obj();
218
+ scr_dyn_obj_set(objects[i], bytes, (size_t)length, scr_dyn_new_num(i));
219
+ }
220
+ for (int i = 0; i < 600; i++) {
221
+ char bytes[32];
222
+ int length = snprintf(bytes, sizeof bytes, "field-%d", i);
223
+ assert(scr_dyn_obj_get(objects[i], bytes, (size_t)length)->v.num == i);
224
+ scr_dyn_release(objects[i]);
225
+ }
226
+ }
227
+ #ifdef SCR_RC_AUDIT
228
+ assert(scr_dyn_key_live_count() == before);
229
+ #endif
230
+ }
231
+
232
+ static void checked_leaf_cycles(void) {
233
+ /* Object properties omit primitives from trial deletion, while arrays and
234
+ * captured unknown values still trace them. Exercise shared leaves through
235
+ * both edge kinds, with and without an external owner, in either generation. */
236
+ for (int old = 0; old < 2; old++) {
237
+ for (int external = 0; external < 2; external++) {
238
+ #ifdef SCR_RC_AUDIT
239
+ long before_dyns = scr_dyn_live_count();
240
+ long before_strings = scr_str_live_count();
241
+ #endif
242
+ ScrStr *text = scr_str_new("survives either edge", 20);
243
+ ScrDyn *leaf = scr_dyn_new_str(text);
244
+ scr_str_release(text);
245
+ if (old) scr_cyc_hdr(leaf)->gen = SCR_CYC_MATURE;
246
+ ScrDyn *first = scr_dyn_new_obj(), *second = scr_dyn_new_obj();
247
+ ScrDyn *array = scr_dyn_new_arr();
248
+ ScrClosure *closure = scr_closure_new(NULL, 1);
249
+ closure->caps[0] = scr_box_new_obj(scr_dyn_retain_v, scr_dyn_release_v, scr_dyn_trace_v);
250
+ scr_box_set_ref(closure->caps[0], scr_dyn_retain(leaf));
251
+ scr_dyn_obj_set(first, "callback", 8, scr_dyn_new_func(closure, nothing, 0, "func()=>dyn", "leaf"));
252
+ scr_dyn_obj_set(first, "leaf", 4, scr_dyn_retain(leaf));
253
+ scr_dyn_obj_set(second, "leaf", 4, scr_dyn_retain(leaf));
254
+ scr_dyn_obj_set(second, "number", 6, scr_dyn_new_num(42));
255
+ scr_dyn_obj_set(second, "empty", 5, scr_dyn_new_null());
256
+ scr_dyn_obj_set(second, "missing", 7, scr_dyn_retain(scr_dyn_undefined()));
257
+ scr_dyn_arr_push(array, scr_dyn_retain(leaf));
258
+ scr_dyn_arr_push(array, scr_dyn_retain(first));
259
+ scr_dyn_obj_set(first, "next", 4, scr_dyn_retain(second));
260
+ scr_dyn_obj_set(second, "back", 4, scr_dyn_retain(first));
261
+ scr_dyn_obj_set(second, "array", 5, array);
262
+ if (!external) scr_dyn_release(leaf);
263
+ scr_dyn_release(first);
264
+ scr_dyn_release(second);
265
+ scr_cyc_collect_scheduled();
266
+ scr_collect_cycles();
267
+ if (external) {
268
+ assert(leaf->rc == 1 && leaf->v.str->len == 20);
269
+ scr_dyn_release(leaf);
270
+ }
271
+ #ifdef SCR_RC_AUDIT
272
+ assert(scr_dyn_live_count() == before_dyns);
273
+ assert(scr_str_live_count() == before_strings);
274
+ #endif
275
+ }
276
+ }
277
+
278
+ /* A previous trace snapshot must not survive a property mutation: the
279
+ * next collection must trace a new object edge or release a new leaf. */
280
+ for (int leaf_after = 0; leaf_after < 2; leaf_after++) {
281
+ #ifdef SCR_RC_AUDIT
282
+ long before = scr_dyn_live_count();
283
+ #endif
284
+ ScrDyn *object = scr_dyn_new_obj(), *child = scr_dyn_new_obj();
285
+ scr_dyn_obj_set(object, "self", 4, scr_dyn_retain(object));
286
+ scr_dyn_obj_set(object, "value", 5, leaf_after ? scr_dyn_retain(child) : scr_dyn_new_num(1));
287
+ scr_dyn_release(scr_dyn_retain(object));
288
+ scr_collect_cycles();
289
+ scr_dyn_obj_set(object, "value", 5, leaf_after ? scr_dyn_new_num(2) : scr_dyn_retain(child));
290
+ scr_dyn_obj_set(child, "parent", 6, scr_dyn_retain(object));
291
+ scr_dyn_release(child);
292
+ scr_dyn_release(object);
293
+ scr_collect_cycles();
294
+ #ifdef SCR_RC_AUDIT
295
+ assert(scr_dyn_live_count() == before);
296
+ #endif
297
+ }
298
+ }
299
+
300
+ static void checked_number_storage(void) {
301
+ #ifdef SCR_RC_AUDIT
302
+ long before = scr_dyn_live_count();
303
+ #endif
304
+ for (int value = -130; value < 258; value++) {
305
+ ScrDyn *first = scr_dyn_new_num(value), *second = scr_dyn_new_num(value);
306
+ assert(first->kind == SCR_DYN_NUM && first->v.num == value);
307
+ assert(scr_dyn_strict_eq(first, second));
308
+ if (value >= -128 && value <= 255) assert(first == second && first->rc == SIZE_MAX);
309
+ ScrDyn *array = scr_dyn_new_arr();
310
+ scr_dyn_arr_push(array, first);
311
+ scr_dyn_arr_push(array, second);
312
+ scr_dyn_arr_push(array, scr_dyn_retain(array));
313
+ scr_dyn_release(array);
314
+ scr_collect_cycles();
315
+ }
316
+ const double special[] = {-0.0, 0.5, -0.5, NAN, INFINITY, -INFINITY, 9007199254740991.0};
317
+ for (size_t i = 0; i < sizeof special / sizeof special[0]; i++) {
318
+ ScrDyn *value = scr_dyn_new_num(special[i]);
319
+ assert(value->rc != SIZE_MAX);
320
+ assert(isnan(special[i]) ? isnan(value->v.num) : value->v.num == special[i]);
321
+ if (special[i] == 0) assert(signbit(value->v.num));
322
+ scr_dyn_release(value);
323
+ }
324
+ const char raw[] = "[-0,0,1.0,1e2,-128,255,256,1e400]";
325
+ ScrStr *input = scr_str_new(raw, sizeof raw - 1);
326
+ ScrDyn *parsed = scr_json_parse(input);
327
+ assert(parsed && parsed->v.arr.len == 8);
328
+ assert(signbit(parsed->v.arr.items[0]->v.num));
329
+ assert(!signbit(parsed->v.arr.items[1]->v.num));
330
+ assert(parsed->v.arr.items[2] == scr_dyn_new_num(1));
331
+ assert(parsed->v.arr.items[3] == scr_dyn_new_num(100));
332
+ assert(isinf(parsed->v.arr.items[7]->v.num));
333
+ scr_dyn_release(parsed);
334
+ scr_str_release(input);
335
+ #ifdef SCR_RC_AUDIT
336
+ assert(scr_dyn_live_count() == before);
337
+ #endif
338
+ }
339
+
23
340
  int main(void) {
341
+ json_string_boundaries();
24
342
  scr_init();
343
+ checked_storage();
344
+ shared_property_keys();
345
+ checked_leaf_cycles();
346
+ checked_number_storage();
25
347
  /* Native Set boxes may own headerless scalar/string maps. Collecting an
26
348
  * enclosing cycle must neither trace those leaves nor skip their release. */
27
349
  for (int i = 0; i < 2000; i++) {
@@ -177,6 +499,6 @@ int main(void) {
177
499
  scr_dyn_release(key);
178
500
  scr_dyn_release(first);
179
501
  assert(!scr_exc_pending());
180
- puts("weak metadata lifetime checks passed");
502
+ puts("checked storage and weak metadata lifetime checks passed");
181
503
  return 0;
182
504
  }
@@ -7,28 +7,61 @@ import { expect, test } from "vitest";
7
7
 
8
8
  const exec = promisify(execFile);
9
9
 
10
- test.each([false, true])("weak metadata releases values when native keys die (audit=%s)", async (audit) => {
11
- const scratch = await mkdtemp(join(tmpdir(), "scriptc-weak-map-"));
12
- const src = import.meta.dirname;
13
- const binary = join(scratch, "weak-map");
14
- try {
15
- await exec("clang", [
16
- "-std=c11", "-O1", "-Wall", "-Wextra", "-fsanitize=address,undefined", ...(audit ? ["-DSCR_RC_AUDIT"] : []),
17
- "-I", src, "-o", binary,
18
- ...[
19
- "scr_json.test.c", "scr_json.c", "scr_string.c", "scr_array.c", "scr_map.c",
20
- "scr_exception.c", "scr_error.c", "scr_number.c", "scr_console.c", "scr_closure.c",
21
- "scr_object.c", "scr_union.c", "scr_cycle.c", "scr_lib.c", "scr_bytes.c", "scr_bigint.c",
22
- "scr_url.c", "scr_url_params.c", "scr_path.c",
23
- ].map((file) => join(src, file)),
24
- ...(process.platform === "linux" ? ["-D_GNU_SOURCE", "-lm"] : []),
25
- ]);
26
- const result = await exec(binary, [], {
27
- env: { ...process.env, ASAN_OPTIONS: "detect_leaks=0:halt_on_error=1", UBSAN_OPTIONS: "halt_on_error=1" },
28
- });
29
- expect(result.stdout).toBe("weak metadata lifetime checks passed\n");
30
- expect(result.stderr).toBe("");
31
- } finally {
32
- await rm(scratch, { recursive: true, force: true });
33
- }
34
- });
10
+ test.each([false, true])(
11
+ "checked storage and weak metadata preserve ownership (audit=%s)",
12
+ async (audit) => {
13
+ const scratch = await mkdtemp(join(tmpdir(), "scriptc-weak-map-"));
14
+ const src = import.meta.dirname;
15
+ const binary = join(scratch, "weak-map");
16
+ try {
17
+ await exec("clang", [
18
+ "-std=c11",
19
+ "-O1",
20
+ "-Wall",
21
+ "-Wextra",
22
+ "-fsanitize=address,undefined",
23
+ ...(audit ? ["-DSCR_RC_AUDIT"] : []),
24
+ "-I",
25
+ src,
26
+ "-o",
27
+ binary,
28
+ ...[
29
+ "scr_json.test.c",
30
+ "scr_json.c",
31
+ "scr_string.c",
32
+ "scr_array.c",
33
+ "scr_map.c",
34
+ "scr_exception.c",
35
+ "scr_error.c",
36
+ "scr_number.c",
37
+ "scr_console.c",
38
+ "scr_closure.c",
39
+ "scr_object.c",
40
+ "scr_union.c",
41
+ "scr_cycle.c",
42
+ "scr_lib.c",
43
+ "scr_bytes.c",
44
+ "scr_bigint.c",
45
+ "scr_url.c",
46
+ "scr_url_params.c",
47
+ "scr_path.c",
48
+ ].map((file) => join(src, file)),
49
+ ...(process.platform === "linux" ? ["-D_GNU_SOURCE", "-lm"] : []),
50
+ ]);
51
+ for (const threshold of ["1", "7", "256"]) {
52
+ const result = await exec(binary, [], {
53
+ env: {
54
+ ...process.env,
55
+ SCR_CYCLE_THRESHOLD: threshold,
56
+ ASAN_OPTIONS: "detect_leaks=0:halt_on_error=1",
57
+ UBSAN_OPTIONS: "halt_on_error=1",
58
+ },
59
+ });
60
+ expect(result.stdout).toBe("checked storage and weak metadata lifetime checks passed\n");
61
+ expect(result.stderr).toBe("");
62
+ }
63
+ } finally {
64
+ await rm(scratch, { recursive: true, force: true });
65
+ }
66
+ },
67
+ );
@@ -0,0 +1,68 @@
1
+ /* Internal key hashing and canonical array-index ordering shared by native
2
+ * maps and checked objects. Hashes never escape into JavaScript values. */
3
+ #ifndef SCR_KEY_H
4
+ #define SCR_KEY_H
5
+ #include <stdbool.h>
6
+ #include <stddef.h>
7
+ #include <stdint.h>
8
+ #include <stdlib.h>
9
+ #include <string.h>
10
+
11
+ static inline uint64_t scr_key_mix(uint64_t value) {
12
+ value ^= value >> 30;
13
+ value *= UINT64_C(0xbf58476d1ce4e5b9);
14
+ value ^= value >> 27;
15
+ value *= UINT64_C(0x94d049bb133111eb);
16
+ return value ^ (value >> 31);
17
+ }
18
+
19
+ /* Unaligned loads stay inside the input. Hashing is process-local, so host
20
+ * byte order is immaterial; equal byte strings always follow the same path. */
21
+ static inline uint64_t scr_key_hash(const char *bytes, size_t length) {
22
+ uint64_t hash = UINT64_C(0x9e3779b97f4a7c15) ^ length;
23
+ size_t i = 0;
24
+ while (length - i >= sizeof(uint64_t)) {
25
+ uint64_t word;
26
+ memcpy(&word, bytes + i, sizeof word);
27
+ hash = scr_key_mix(hash ^ word);
28
+ i += sizeof word;
29
+ }
30
+ uint64_t tail = 0;
31
+ if (i < length) memcpy(&tail, bytes + i, length - i);
32
+ return scr_key_mix(hash ^ tail);
33
+ }
34
+
35
+ static inline bool scr_key_array_index(const char *key, size_t length, uint32_t *out) {
36
+ if (length == 0 || length > 10 || (length > 1 && key[0] == '0')) return false;
37
+ uint64_t value = 0;
38
+ for (size_t i = 0; i < length; i++) {
39
+ if (key[i] < '0' || key[i] > '9') return false;
40
+ value = value * 10 + (unsigned)(key[i] - '0');
41
+ }
42
+ if (value >= UINT32_MAX) return false;
43
+ *out = (uint32_t)value;
44
+ return true;
45
+ }
46
+
47
+ typedef struct {
48
+ uint32_t index;
49
+ size_t entry;
50
+ } ScrKeyIndex;
51
+
52
+ static inline int scr_key_index_compare(const void *left, const void *right) {
53
+ uint32_t a = ((const ScrKeyIndex *)left)->index;
54
+ uint32_t b = ((const ScrKeyIndex *)right)->index;
55
+ return (a > b) - (a < b);
56
+ }
57
+
58
+ /* Keys are unique. Already ordered inputs need no sort, while arbitrary
59
+ * numeric property insertion must not require quadratic enumeration. */
60
+ static inline void scr_key_index_sort(ScrKeyIndex *keys, size_t count) {
61
+ for (size_t i = 1; i < count; i++) {
62
+ if (keys[i - 1].index > keys[i].index) {
63
+ qsort(keys, count, sizeof *keys, scr_key_index_compare);
64
+ return;
65
+ }
66
+ }
67
+ }
68
+ #endif