xbintsc 0.3.35 → 0.3.49
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.
- package/AGENTS.md +95 -0
- package/README.md +31 -3
- package/README.zh-CN.md +23 -0
- package/dist/src/cli/hints.d.ts +54 -0
- package/dist/src/cli/hints.js +165 -0
- package/dist/src/cli/hints.js.map +1 -0
- package/dist/src/cli/main.js +73 -9
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/codegen/generator/module.js +10 -0
- package/dist/src/codegen/generator/module.js.map +1 -1
- package/dist/src/codegen/generator/state.js +4 -0
- package/dist/src/codegen/generator/state.js.map +1 -1
- package/dist/src/codegen/generator/tables.d.ts +26 -0
- package/dist/src/codegen/generator/tables.js +64 -12
- package/dist/src/codegen/generator/tables.js.map +1 -1
- package/dist/src/diagnostics/source-text.d.ts +22 -0
- package/dist/src/diagnostics/source-text.js +76 -0
- package/dist/src/diagnostics/source-text.js.map +1 -0
- package/dist/src/driver/bundler/graph.js +2 -1
- package/dist/src/driver/bundler/graph.js.map +1 -1
- package/dist/src/driver/compiler.js +3 -2
- package/dist/src/driver/compiler.js.map +1 -1
- package/dist/src/lexer/scanner/strings.js +16 -3
- package/dist/src/lexer/scanner/strings.js.map +1 -1
- package/dist/tests/cli/hints.test.d.ts +9 -0
- package/dist/tests/cli/hints.test.js +143 -0
- package/dist/tests/cli/hints.test.js.map +1 -0
- package/dist/tests/cli/main.test.js +6 -4
- package/dist/tests/cli/main.test.js.map +1 -1
- package/dist/tests/codegen/llvm.test.js +17 -2
- package/dist/tests/codegen/llvm.test.js.map +1 -1
- package/dist/tests/e2e/gc.test.d.ts +1 -0
- package/dist/tests/e2e/gc.test.js +168 -0
- package/dist/tests/e2e/gc.test.js.map +1 -0
- package/dist/tests/e2e/harness.d.ts +2 -0
- package/dist/tests/e2e/harness.js +1 -0
- package/dist/tests/e2e/harness.js.map +1 -1
- package/dist/tests/helpers.js +3 -2
- package/dist/tests/helpers.js.map +1 -1
- package/dist/tests/lexer/strings.test.js +14 -2
- package/dist/tests/lexer/strings.test.js.map +1 -1
- package/doc/DESIGN.md +117 -0
- package/doc/ai/README.md +63 -0
- package/doc/ai/build-recipe.md +137 -0
- package/doc/ai/cli.md +142 -0
- package/doc/ai/contributing.md +196 -0
- package/doc/ai/extensions.md +148 -0
- package/doc/ai/language-support.md +152 -0
- package/doc/ai/troubleshooting.md +163 -0
- package/doc/ai/zh-CN/README.md +56 -0
- package/doc/ai/zh-CN/build-recipe.md +132 -0
- package/doc/ai/zh-CN/cli.md +127 -0
- package/doc/ai/zh-CN/contributing.md +173 -0
- package/doc/ai/zh-CN/extensions.md +139 -0
- package/doc/ai/zh-CN/language-support.md +147 -0
- package/doc/ai/zh-CN/troubleshooting.md +150 -0
- package/doc/gui-scripts.md +350 -0
- package/doc/gui.md +646 -0
- package/doc/icon.md +265 -0
- package/doc/implemented.md +373 -0
- package/doc/node-implemented.md +588 -0
- package/doc/node-unimplemented.md +167 -0
- package/doc/post/announce.md +43 -0
- package/doc/requirements.md +145 -0
- package/doc/unimplemented.md +286 -0
- package/doc/xbintsc.config.schema.json +67 -0
- package/doc/zh-CN/DESIGN.md +104 -0
- package/doc/zh-CN/gui-scripts.md +329 -0
- package/doc/zh-CN/gui.md +588 -0
- package/doc/zh-CN/icon.md +241 -0
- package/doc/zh-CN/implemented.md +365 -0
- package/doc/zh-CN/node-implemented.md +533 -0
- package/doc/zh-CN/node-unimplemented.md +141 -0
- package/doc/zh-CN/plan-require-node-modules.md +284 -0
- package/doc/zh-CN/post/announce.md +47 -0
- package/doc/zh-CN/requirements.md +134 -0
- package/doc/zh-CN/unimplemented.md +247 -0
- package/llms.txt +45 -0
- package/package.json +6 -2
- package/runtime/ext_gui/dom_api_proto.cpp +5 -0
- package/runtime/ext_gui/gui.cpp +3 -1
- package/runtime/ext_gui/renderer.cpp +13 -11
- package/runtime/ext_gui/renderer_image.cpp +12 -8
- package/runtime/ext_gui/renderer_shaders.h +131 -4
- package/runtime/ext_gui/renderer_shaders_data.h +1809 -0
- package/runtime/ext_gui/renderer_text.cpp +12 -8
- package/runtime/ext_gui/shaders.hlsl +98 -0
- package/runtime/ext_gui/spirv/fill.frag +19 -0
- package/runtime/ext_gui/spirv/fill.vert +42 -0
- package/runtime/ext_gui/spirv/image.frag +16 -0
- package/runtime/ext_gui/spirv/quad.vert +30 -0
- package/runtime/ext_gui/spirv/text.frag +16 -0
- package/runtime/ext_gui/window.cpp +1 -0
- package/runtime/ext_node/buffer/parts/prototype.inc +1 -0
- package/runtime/ext_node/dgram/dgram.c +1 -0
- package/runtime/ext_node/events/events.c +1 -0
- package/runtime/ext_node/fs/fs_ops.c +10 -25
- package/runtime/ext_node/fs/glob.c +13 -30
- package/runtime/ext_node/fs/promises.c +1 -0
- package/runtime/ext_node/http/parts/prototypes.inc +5 -0
- package/runtime/ext_node/net/parts/prototypes.inc +2 -0
- package/runtime/ext_node/process/process.c +9 -7
- package/runtime/ext_node/stream/stream.c +1 -0
- package/runtime/ext_node/util/util.c +6 -6
- package/runtime/rt.h +10 -0
- package/runtime/rt_internal.h +63 -2
- package/runtime/xt_alloc.c +442 -5
- package/runtime/xt_generator.c +95 -1
- package/runtime/xt_loop.c +35 -1
- package/runtime/xt_promise.c +46 -0
- package/runtime/xt_stdlib2/error.inc +1 -0
- package/runtime/xt_stdlib2/regexp-match.inc +8 -7
- package/runtime/xt_symbol.c +2 -0
- package/runtime/xt_typed_array/construction.inc +142 -0
- package/runtime/xt_typed_array/elements.inc +92 -0
- package/runtime/xt_typed_array/methods.inc +329 -0
- package/runtime/xt_typed_array.c +6 -548
- package/runtime/xt_values/number-format.inc +26 -0
- package/scripts/build-gui-shaders.mjs +204 -0
- package/scripts/build-gui.ts +35 -0
- package/scripts/check-file-length.ts +89 -0
- package/src/cli/hints.ts +194 -0
- package/src/cli/main.ts +82 -9
- package/src/codegen/generator/module.ts +10 -0
- package/src/codegen/generator/state.ts +4 -0
- package/src/codegen/generator/tables.ts +60 -14
- package/src/diagnostics/source-text.ts +78 -0
- package/src/driver/bundler/graph.ts +2 -1
- package/src/driver/compiler.ts +3 -2
- package/src/lexer/scanner/strings.ts +16 -3
|
@@ -187,22 +187,26 @@ void flushAtlas(SDL_GPUDevice *device) {
|
|
|
187
187
|
}
|
|
188
188
|
|
|
189
189
|
SDL_GPUGraphicsPipeline *createTextPipeline(SDL_GPUDevice *device, SDL_Window *reference) {
|
|
190
|
+
const ShaderSource text_vs = selectShader(device, ShaderProgram::Text, false);
|
|
191
|
+
const ShaderSource text_fs = selectShader(device, ShaderProgram::Text, true);
|
|
192
|
+
if (!text_vs.valid() || !text_fs.valid()) return nullptr;
|
|
193
|
+
|
|
190
194
|
SDL_GPUShaderCreateInfo vs_info;
|
|
191
195
|
memset(&vs_info, 0, sizeof(vs_info));
|
|
192
|
-
vs_info.code_size =
|
|
193
|
-
vs_info.code =
|
|
194
|
-
vs_info.entrypoint =
|
|
195
|
-
vs_info.format =
|
|
196
|
+
vs_info.code_size = text_vs.size;
|
|
197
|
+
vs_info.code = text_vs.code;
|
|
198
|
+
vs_info.entrypoint = text_vs.entry;
|
|
199
|
+
vs_info.format = text_vs.format;
|
|
196
200
|
vs_info.stage = SDL_GPU_SHADERSTAGE_VERTEX;
|
|
197
201
|
vs_info.num_uniform_buffers = 1;
|
|
198
202
|
SDL_GPUShader *vs = SDL_CreateGPUShader(device, &vs_info);
|
|
199
203
|
|
|
200
204
|
SDL_GPUShaderCreateInfo fs_info;
|
|
201
205
|
memset(&fs_info, 0, sizeof(fs_info));
|
|
202
|
-
fs_info.code_size =
|
|
203
|
-
fs_info.code =
|
|
204
|
-
fs_info.entrypoint =
|
|
205
|
-
fs_info.format =
|
|
206
|
+
fs_info.code_size = text_fs.size;
|
|
207
|
+
fs_info.code = text_fs.code;
|
|
208
|
+
fs_info.entrypoint = text_fs.entry;
|
|
209
|
+
fs_info.format = text_fs.format;
|
|
206
210
|
fs_info.stage = SDL_GPU_SHADERSTAGE_FRAGMENT;
|
|
207
211
|
fs_info.num_samplers = 1;
|
|
208
212
|
SDL_GPUShader *fs = SDL_CreateGPUShader(device, &fs_info);
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* xbintsc GUI engine — shader sources compiled ahead of time into the embedded
|
|
3
|
+
* blobs in `renderer_blobs.h`.
|
|
4
|
+
*
|
|
5
|
+
* Three programs, two entry points each:
|
|
6
|
+
* - `vs_main` / `fs_main` rounded-rectangle fills
|
|
7
|
+
* - `vs_text` / `fs_text` glyph quads (single-channel atlas)
|
|
8
|
+
* - `vs_text` / `fs_image` image quads (RGBA texture)
|
|
9
|
+
*
|
|
10
|
+
* `vs_main` and `vs_text` are identical in effect; they are kept separate so the
|
|
11
|
+
* Metal (MSL) path keeps its distinct entry-point names, and this file is the
|
|
12
|
+
* HLSL half of the pair. Vertex attribute semantics map 1:1 onto
|
|
13
|
+
* `SDL_GPUVertexAttribute.location`, and the push constant becomes `b0`.
|
|
14
|
+
*
|
|
15
|
+
* Compile with (Windows SDK `dxc`):
|
|
16
|
+
* dxc -T vs_6_0 -E vs_main -Fo fill_vs.dxil shaders.hlsl
|
|
17
|
+
* dxc -T ps_6_0 -E fs_main -Fo fill_fs.dxil shaders.hlsl
|
|
18
|
+
* dxc -T vs_6_0 -E vs_text -Fo text_vs.dxil shaders.hlsl
|
|
19
|
+
* dxc -T ps_6_0 -E fs_text -Fo text_fs.dxil shaders.hlsl
|
|
20
|
+
* dxc -T ps_6_0 -E fs_image -Fo image_fs.dxil shaders.hlsl
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
cbuffer Viewport : register(b0) { float2 viewport; };
|
|
24
|
+
|
|
25
|
+
/* -- fills ---------------------------------------------------------------- */
|
|
26
|
+
|
|
27
|
+
struct FillIn {
|
|
28
|
+
float2 position : TEXCOORD0;
|
|
29
|
+
float2 local : TEXCOORD1;
|
|
30
|
+
float2 halfsize : TEXCOORD2;
|
|
31
|
+
float radius : TEXCOORD3;
|
|
32
|
+
float4 color : TEXCOORD4;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
struct FillOut {
|
|
36
|
+
float4 position : SV_Position;
|
|
37
|
+
float2 local : TEXCOORD1;
|
|
38
|
+
float2 halfsize : TEXCOORD2;
|
|
39
|
+
float radius : TEXCOORD3;
|
|
40
|
+
float4 color : TEXCOORD4;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
FillOut vs_main(FillIn input) {
|
|
44
|
+
FillOut output;
|
|
45
|
+
float2 ndc = float2(input.position.x / viewport.x * 2.0 - 1.0,
|
|
46
|
+
1.0 - input.position.y / viewport.y * 2.0);
|
|
47
|
+
output.position = float4(ndc, 0.0, 1.0);
|
|
48
|
+
output.local = input.local;
|
|
49
|
+
output.halfsize = input.halfsize;
|
|
50
|
+
output.radius = input.radius;
|
|
51
|
+
output.color = input.color;
|
|
52
|
+
return output;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
float4 fs_main(FillOut input) : SV_Target {
|
|
56
|
+
float2 d = abs(input.local) - input.halfsize + input.radius;
|
|
57
|
+
float dist = min(max(d.x, d.y), 0.0) + length(max(d, 0.0)) - input.radius;
|
|
58
|
+
float aa = max(fwidth(dist), 0.0001);
|
|
59
|
+
float alpha = clamp(0.5 - dist / aa, 0.0, 1.0);
|
|
60
|
+
return float4(input.color.rgb, input.color.a * alpha);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* -- text and images ------------------------------------------------------ */
|
|
64
|
+
|
|
65
|
+
struct TextIn {
|
|
66
|
+
float2 position : TEXCOORD0;
|
|
67
|
+
float2 uv : TEXCOORD1;
|
|
68
|
+
float4 color : TEXCOORD2;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
struct TextOut {
|
|
72
|
+
float4 position : SV_Position;
|
|
73
|
+
float2 uv : TEXCOORD1;
|
|
74
|
+
float4 color : TEXCOORD2;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
Texture2D atlas : register(t0);
|
|
78
|
+
SamplerState samp : register(s0);
|
|
79
|
+
|
|
80
|
+
TextOut vs_text(TextIn input) {
|
|
81
|
+
TextOut output;
|
|
82
|
+
float2 ndc = float2(input.position.x / viewport.x * 2.0 - 1.0,
|
|
83
|
+
1.0 - input.position.y / viewport.y * 2.0);
|
|
84
|
+
output.position = float4(ndc, 0.0, 1.0);
|
|
85
|
+
output.uv = input.uv;
|
|
86
|
+
output.color = input.color;
|
|
87
|
+
return output;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
float4 fs_text(TextOut input) : SV_Target {
|
|
91
|
+
float coverage = atlas.Sample(samp, input.uv).r;
|
|
92
|
+
return float4(input.color.rgb, input.color.a * coverage);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
float4 fs_image(TextOut input) : SV_Target {
|
|
96
|
+
float4 c = atlas.Sample(samp, input.uv);
|
|
97
|
+
return float4(c.rgb * input.color.rgb, c.a * input.color.a);
|
|
98
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#version 450
|
|
2
|
+
|
|
3
|
+
/* xbintsc GUI engine — rounded-rectangle fill fragment shader (Vulkan/SPIR-V).
|
|
4
|
+
* The distance field mirrors the MSL/DXIL fill shaders. */
|
|
5
|
+
|
|
6
|
+
layout(location = 0) in vec2 v_local;
|
|
7
|
+
layout(location = 1) in vec2 v_halfsize;
|
|
8
|
+
layout(location = 2) in float v_radius;
|
|
9
|
+
layout(location = 3) in vec4 v_color;
|
|
10
|
+
|
|
11
|
+
layout(location = 0) out vec4 out_color;
|
|
12
|
+
|
|
13
|
+
void main() {
|
|
14
|
+
vec2 d = abs(v_local) - v_halfsize + v_radius;
|
|
15
|
+
float dist = min(max(d.x, d.y), 0.0) + length(max(d, 0.0)) - v_radius;
|
|
16
|
+
float aa = max(fwidth(dist), 0.0001);
|
|
17
|
+
float alpha = clamp(0.5 - dist / aa, 0.0, 1.0);
|
|
18
|
+
out_color = vec4(v_color.rgb, v_color.a * alpha);
|
|
19
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#version 450
|
|
2
|
+
|
|
3
|
+
/* xbintsc GUI engine — rounded-rectangle fill, Vulkan/SPIR-V path.
|
|
4
|
+
*
|
|
5
|
+
* The descriptor layout matches what SDL_GPU's Vulkan backend builds
|
|
6
|
+
* (`SDL_gpu_vulkan.c`, `CreateDescriptorSetLayout`): uniforms at
|
|
7
|
+
* `set=1, binding=0`, and (in the text/image shaders) samplers at
|
|
8
|
+
* `set=2, binding=0` with textures at `set=2, binding=1`.
|
|
9
|
+
*
|
|
10
|
+
* Vertex attributes (matching SDL_GPUVertexAttribute.location):
|
|
11
|
+
* 0 -> position (pixels, viewport-relative)
|
|
12
|
+
* 1 -> local (pixels, relative to the rectangle centre)
|
|
13
|
+
* 2 -> halfsize (rectangle half extents, pixels)
|
|
14
|
+
* 3 -> radius (pixels)
|
|
15
|
+
* 4 -> colour
|
|
16
|
+
* The push constant is the viewport size in logical pixels.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
layout(location = 0) in vec2 in_position;
|
|
20
|
+
layout(location = 1) in vec2 in_local;
|
|
21
|
+
layout(location = 2) in vec2 in_halfsize;
|
|
22
|
+
layout(location = 3) in float in_radius;
|
|
23
|
+
layout(location = 4) in vec4 in_color;
|
|
24
|
+
|
|
25
|
+
layout(location = 0) out vec2 v_local;
|
|
26
|
+
layout(location = 1) out vec2 v_halfsize;
|
|
27
|
+
layout(location = 2) out float v_radius;
|
|
28
|
+
layout(location = 3) out vec4 v_color;
|
|
29
|
+
|
|
30
|
+
layout(set = 1, binding = 0) uniform Viewport {
|
|
31
|
+
vec2 size;
|
|
32
|
+
} viewport;
|
|
33
|
+
|
|
34
|
+
void main() {
|
|
35
|
+
vec2 ndc = vec2(in_position.x / viewport.size.x * 2.0 - 1.0,
|
|
36
|
+
1.0 - in_position.y / viewport.size.y * 2.0);
|
|
37
|
+
gl_Position = vec4(ndc, 0.0, 1.0);
|
|
38
|
+
v_local = in_local;
|
|
39
|
+
v_halfsize = in_halfsize;
|
|
40
|
+
v_radius = in_radius;
|
|
41
|
+
v_color = in_color;
|
|
42
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#version 450
|
|
2
|
+
|
|
3
|
+
/* xbintsc GUI engine — image quads (Vulkan/SPIR-V): sample an RGBA texture and
|
|
4
|
+
* modulate it by the vertex colour. */
|
|
5
|
+
|
|
6
|
+
layout(location = 0) in vec2 v_uv;
|
|
7
|
+
layout(location = 1) in vec4 v_color;
|
|
8
|
+
|
|
9
|
+
layout(location = 0) out vec4 out_color;
|
|
10
|
+
|
|
11
|
+
layout(set = 2, binding = 0) uniform sampler2D image;
|
|
12
|
+
|
|
13
|
+
void main() {
|
|
14
|
+
vec4 c = texture(image, v_uv);
|
|
15
|
+
out_color = vec4(c.rgb * v_color.rgb, c.a * v_color.a);
|
|
16
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#version 450
|
|
2
|
+
|
|
3
|
+
/* xbintsc GUI engine — glyph and image quads, Vulkan/SPIR-V path.
|
|
4
|
+
*
|
|
5
|
+
* The vertex stage is shared by both: the fragment stage differs only in how it
|
|
6
|
+
* uses the sampled texture (`fs_text` reads the atlas' red channel, `fs_image`
|
|
7
|
+
* modulates an RGBA texture). Attributes:
|
|
8
|
+
* 0 -> position (pixels, viewport-relative)
|
|
9
|
+
* 1 -> uv (atlas coordinates in [0,1])
|
|
10
|
+
* 2 -> colour
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
layout(location = 0) in vec2 in_position;
|
|
14
|
+
layout(location = 1) in vec2 in_uv;
|
|
15
|
+
layout(location = 2) in vec4 in_color;
|
|
16
|
+
|
|
17
|
+
layout(location = 0) out vec2 v_uv;
|
|
18
|
+
layout(location = 1) out vec4 v_color;
|
|
19
|
+
|
|
20
|
+
layout(set = 1, binding = 0) uniform Viewport {
|
|
21
|
+
vec2 size;
|
|
22
|
+
} viewport;
|
|
23
|
+
|
|
24
|
+
void main() {
|
|
25
|
+
vec2 ndc = vec2(in_position.x / viewport.size.x * 2.0 - 1.0,
|
|
26
|
+
1.0 - in_position.y / viewport.size.y * 2.0);
|
|
27
|
+
gl_Position = vec4(ndc, 0.0, 1.0);
|
|
28
|
+
v_uv = in_uv;
|
|
29
|
+
v_color = in_color;
|
|
30
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#version 450
|
|
2
|
+
|
|
3
|
+
/* xbintsc GUI engine — glyph quads (Vulkan/SPIR-V): sample the single-channel
|
|
4
|
+
* atlas and modulate alpha by its coverage. */
|
|
5
|
+
|
|
6
|
+
layout(location = 0) in vec2 v_uv;
|
|
7
|
+
layout(location = 1) in vec4 v_color;
|
|
8
|
+
|
|
9
|
+
layout(location = 0) out vec4 out_color;
|
|
10
|
+
|
|
11
|
+
layout(set = 2, binding = 0) uniform sampler2D atlas;
|
|
12
|
+
|
|
13
|
+
void main() {
|
|
14
|
+
float coverage = texture(atlas, v_uv).r;
|
|
15
|
+
out_color = vec4(v_color.rgb, v_color.a * coverage);
|
|
16
|
+
}
|
|
@@ -434,6 +434,7 @@ static void define_getter(xt_value proto, const char *name, void *fn) {
|
|
|
434
434
|
}
|
|
435
435
|
|
|
436
436
|
xt_value xt_gui_window_proto(void) {
|
|
437
|
+
xt_gc_add_root(&g_window_proto);
|
|
437
438
|
if (XT_IS_OBJECT(g_window_proto)) return g_window_proto;
|
|
438
439
|
xt_value proto = xt_object_new();
|
|
439
440
|
define_method(proto, "setTitle", (void *)win_set_title);
|
|
@@ -316,6 +316,7 @@ xt_value xt_dgram_static(xt_value name, int32_t argc, xt_value *argv) {
|
|
|
316
316
|
|
|
317
317
|
static xt_value xt_dgram_socket_proto(void) {
|
|
318
318
|
static xt_value proto = 0;
|
|
319
|
+
xt_gc_add_root(&proto);
|
|
319
320
|
if (proto) return proto;
|
|
320
321
|
proto = xt_object_new();
|
|
321
322
|
xt_node_install_emitter(proto);
|
|
@@ -267,6 +267,7 @@ xt_value xt_events_static(xt_value name, int32_t argc, xt_value *argv) {
|
|
|
267
267
|
|
|
268
268
|
static xt_value xt_events_proto(void) {
|
|
269
269
|
static xt_value proto = 0;
|
|
270
|
+
xt_gc_add_root(&proto);
|
|
270
271
|
if (proto) return proto;
|
|
271
272
|
proto = xt_object_new();
|
|
272
273
|
xt_node_install_emitter(proto);
|
|
@@ -59,27 +59,18 @@ xt_value xt_node_dirent_result(const char *dir, const char *name) {
|
|
|
59
59
|
return object;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
static void xt_fs_list_push(xt_fs_list *list, xt_value value) {
|
|
69
|
-
if (list->count == list->capacity) {
|
|
70
|
-
list->capacity = list->capacity ? list->capacity * 2 : 16;
|
|
71
|
-
xt_value *grown = (xt_value *)realloc(list->items, sizeof(xt_value) * list->capacity);
|
|
72
|
-
if (!grown) return;
|
|
73
|
-
list->items = grown;
|
|
74
|
-
}
|
|
75
|
-
list->items[list->count++] = value;
|
|
76
|
-
}
|
|
62
|
+
/* Entries accumulate in an `xt_array`, not a raw `xt_value` buffer: the
|
|
63
|
+
* collector cannot see a plain malloc'd buffer, so a `Dirent` created earlier
|
|
64
|
+
* in the walk would be swept as soon as a later entry allocates (which is
|
|
65
|
+
* exactly the GC-stress failure "object has no callable method
|
|
66
|
+
* 'isDirectory'"). The array lives on the caller's stack, so the conservative
|
|
67
|
+
* stack scan keeps it, and every entry it holds, reachable. */
|
|
77
68
|
|
|
78
69
|
/* Depth-first walk used by `readdirSync(path, { recursive: true })`. `prefix`
|
|
79
70
|
is the directory path relative to `base` (empty for the root, otherwise
|
|
80
71
|
ending in `/`). */
|
|
81
72
|
static void xt_fs_readdir_into(const char *base, const char *prefix, int withFileTypes, int recursive,
|
|
82
|
-
|
|
73
|
+
xt_value list) {
|
|
83
74
|
size_t baseLength = strlen(base);
|
|
84
75
|
size_t prefixLength = strlen(prefix);
|
|
85
76
|
size_t dirSize = baseLength + prefixLength + 2;
|
|
@@ -104,7 +95,7 @@ static void xt_fs_readdir_into(const char *base, const char *prefix, int withFil
|
|
|
104
95
|
char *relative = (char *)malloc(relSize);
|
|
105
96
|
if (!relative) continue;
|
|
106
97
|
snprintf(relative, relSize, "%s%s", prefix, name);
|
|
107
|
-
|
|
98
|
+
xt_array_push(list, withFileTypes ? xt_node_dirent_result(dirPath, name) : xt_string_from_cstr(relative));
|
|
108
99
|
|
|
109
100
|
if (recursive) {
|
|
110
101
|
size_t fullSize = dirLength + nameLength + 2;
|
|
@@ -141,14 +132,8 @@ xt_value xt_node_read_dir(int32_t argc, xt_value *argv) {
|
|
|
141
132
|
int withFileTypes = XT_IS_OBJECT(options) && xt_truthy(xt_object_get_cstr(options, "withFileTypes"));
|
|
142
133
|
int recursive = XT_IS_OBJECT(options) && xt_truthy(xt_object_get_cstr(options, "recursive"));
|
|
143
134
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
list.count = 0;
|
|
147
|
-
list.capacity = 0;
|
|
148
|
-
xt_fs_readdir_into(path, "", withFileTypes, recursive, &list);
|
|
149
|
-
|
|
150
|
-
xt_value result = xt_array_new((int32_t)list.count, list.items);
|
|
151
|
-
free(list.items);
|
|
135
|
+
xt_value result = xt_array_new(0, NULL);
|
|
136
|
+
xt_fs_readdir_into(path, "", withFileTypes, recursive, result);
|
|
152
137
|
return result;
|
|
153
138
|
}
|
|
154
139
|
|
|
@@ -10,21 +10,9 @@
|
|
|
10
10
|
#include "../node_common.h"
|
|
11
11
|
#include "fs_common.h"
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
size_t capacity;
|
|
17
|
-
} xt_glob_list;
|
|
18
|
-
|
|
19
|
-
static void xt_glob_push(xt_glob_list *list, xt_value value) {
|
|
20
|
-
if (list->count == list->capacity) {
|
|
21
|
-
list->capacity = list->capacity ? list->capacity * 2 : 16;
|
|
22
|
-
xt_value *grown = (xt_value *)realloc(list->items, sizeof(xt_value) * list->capacity);
|
|
23
|
-
if (!grown) return;
|
|
24
|
-
list->items = grown;
|
|
25
|
-
}
|
|
26
|
-
list->items[list->count++] = value;
|
|
27
|
-
}
|
|
13
|
+
/* Matches accumulate in an `xt_array` rather than a raw buffer so the garbage
|
|
14
|
+
* collector can see the `Dirent`/string entries created earlier in the walk;
|
|
15
|
+
* see the same note in `fs_ops.c`. */
|
|
28
16
|
|
|
29
17
|
static char *xt_glob_join(const char *dir, const char *name) {
|
|
30
18
|
size_t dirLength = strlen(dir);
|
|
@@ -96,11 +84,11 @@ static int xt_glob_is_dir(const char *path, int follow) {
|
|
|
96
84
|
}
|
|
97
85
|
|
|
98
86
|
static void xt_glob_walk(const char *dirPath, const char *pattern, const char *resultPrefix, int withFileTypes,
|
|
99
|
-
|
|
87
|
+
xt_value list);
|
|
100
88
|
|
|
101
89
|
/* Recurse one directory level for the `**` segment. */
|
|
102
90
|
static void xt_glob_descend(const char *dirPath, const char *pattern, const char *resultPrefix, int withFileTypes,
|
|
103
|
-
|
|
91
|
+
xt_value list) {
|
|
104
92
|
xt_fs_dir dir;
|
|
105
93
|
if (!xt_fs_dir_open(&dir, dirPath)) return;
|
|
106
94
|
while (xt_fs_dir_next(&dir)) {
|
|
@@ -123,7 +111,7 @@ static void xt_glob_descend(const char *dirPath, const char *pattern, const char
|
|
|
123
111
|
}
|
|
124
112
|
|
|
125
113
|
static void xt_glob_walk(const char *dirPath, const char *pattern, const char *resultPrefix, int withFileTypes,
|
|
126
|
-
|
|
114
|
+
xt_value list) {
|
|
127
115
|
if (pattern[0] == '\0') {
|
|
128
116
|
if (resultPrefix[0] != '\0') {
|
|
129
117
|
size_t length = strlen(resultPrefix);
|
|
@@ -131,7 +119,7 @@ static void xt_glob_walk(const char *dirPath, const char *pattern, const char *r
|
|
|
131
119
|
if (!trimmed) return;
|
|
132
120
|
memcpy(trimmed, resultPrefix, length + 1);
|
|
133
121
|
while (length > 1 && trimmed[length - 1] == '/') trimmed[--length] = '\0';
|
|
134
|
-
|
|
122
|
+
xt_array_push(list, xt_string_from_cstr(trimmed));
|
|
135
123
|
free(trimmed);
|
|
136
124
|
}
|
|
137
125
|
return;
|
|
@@ -165,13 +153,13 @@ static void xt_glob_walk(const char *dirPath, const char *pattern, const char *r
|
|
|
165
153
|
char *child = xt_glob_join(dirPath, name);
|
|
166
154
|
if (!child) continue;
|
|
167
155
|
if (rest[0] == '\0') {
|
|
168
|
-
if (withFileTypes)
|
|
156
|
+
if (withFileTypes) xt_array_push(list, xt_node_dirent_result(dirPath, name));
|
|
169
157
|
else {
|
|
170
158
|
size_t size = strlen(resultPrefix) + strlen(name) + 1;
|
|
171
159
|
char *result = (char *)malloc(size);
|
|
172
160
|
if (result) {
|
|
173
161
|
snprintf(result, size, "%s%s", resultPrefix, name);
|
|
174
|
-
|
|
162
|
+
xt_array_push(list, xt_string_from_cstr(result));
|
|
175
163
|
free(result);
|
|
176
164
|
}
|
|
177
165
|
}
|
|
@@ -189,7 +177,7 @@ static void xt_glob_walk(const char *dirPath, const char *pattern, const char *r
|
|
|
189
177
|
xt_fs_dir_close(&dir);
|
|
190
178
|
}
|
|
191
179
|
|
|
192
|
-
static void xt_glob_run(const char *pattern, const char *cwd, int withFileTypes,
|
|
180
|
+
static void xt_glob_run(const char *pattern, const char *cwd, int withFileTypes, xt_value list) {
|
|
193
181
|
if (!pattern) return;
|
|
194
182
|
const char *base = cwd && cwd[0] ? cwd : ".";
|
|
195
183
|
const char *prefix = "";
|
|
@@ -209,24 +197,19 @@ xt_value xt_node_glob(int32_t argc, xt_value *argv) {
|
|
|
209
197
|
xt_value cwdValue = XT_IS_OBJECT(options) ? xt_object_get_cstr(options, "cwd") : XT_UNDEFINED;
|
|
210
198
|
if (XT_IS_STRING(cwdValue)) cwd = xt_string_data(cwdValue);
|
|
211
199
|
|
|
212
|
-
|
|
213
|
-
list.items = NULL;
|
|
214
|
-
list.count = 0;
|
|
215
|
-
list.capacity = 0;
|
|
200
|
+
xt_value result = xt_array_new(0, NULL);
|
|
216
201
|
|
|
217
202
|
xt_value patternValue = argv[0];
|
|
218
203
|
if (XT_IS_ARRAY(patternValue)) {
|
|
219
204
|
int32_t count = (int32_t)xt_to_number(xt_array_length(patternValue));
|
|
220
205
|
for (int32_t i = 0; i < count; i++) {
|
|
221
206
|
xt_value item = xt_array_get(patternValue, xt_number((double)i));
|
|
222
|
-
if (XT_IS_STRING(item)) xt_glob_run(xt_string_data(item), cwd, withFileTypes,
|
|
207
|
+
if (XT_IS_STRING(item)) xt_glob_run(xt_string_data(item), cwd, withFileTypes, result);
|
|
223
208
|
}
|
|
224
209
|
} else {
|
|
225
210
|
const char *pattern = xt_node_cstr(patternValue);
|
|
226
|
-
xt_glob_run(pattern, cwd, withFileTypes,
|
|
211
|
+
xt_glob_run(pattern, cwd, withFileTypes, result);
|
|
227
212
|
}
|
|
228
213
|
|
|
229
|
-
xt_value result = xt_array_new((int32_t)list.count, list.items);
|
|
230
|
-
free(list.items);
|
|
231
214
|
return result;
|
|
232
215
|
}
|
|
@@ -242,6 +242,7 @@ XT_FS_PROMISES_FACADE(xt_node_p_open, _)
|
|
|
242
242
|
|
|
243
243
|
xt_value xt_fs_promises(void) {
|
|
244
244
|
static xt_value cached = 0;
|
|
245
|
+
xt_gc_add_root(&cached);
|
|
245
246
|
if (XT_IS_OBJECT(cached)) return cached;
|
|
246
247
|
xt_value object = xt_object_new();
|
|
247
248
|
#define XT_FS_PROMISE_METHOD(jsName, name) \
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
static xt_value xt_http_server_proto(void) {
|
|
4
4
|
static xt_value proto = 0;
|
|
5
|
+
xt_gc_add_root(&proto);
|
|
5
6
|
if (proto) return proto;
|
|
6
7
|
proto = xt_object_new();
|
|
7
8
|
xt_node_install_emitter(proto);
|
|
@@ -13,6 +14,7 @@ static xt_value xt_http_server_proto(void) {
|
|
|
13
14
|
|
|
14
15
|
static xt_value xt_http_response_proto(void) {
|
|
15
16
|
static xt_value proto = 0;
|
|
17
|
+
xt_gc_add_root(&proto);
|
|
16
18
|
if (proto) return proto;
|
|
17
19
|
proto = xt_object_new();
|
|
18
20
|
xt_node_install_emitter(proto);
|
|
@@ -28,6 +30,7 @@ static xt_value xt_http_response_proto(void) {
|
|
|
28
30
|
|
|
29
31
|
static xt_value xt_http_request_proto(void) {
|
|
30
32
|
static xt_value proto = 0;
|
|
33
|
+
xt_gc_add_root(&proto);
|
|
31
34
|
if (proto) return proto;
|
|
32
35
|
proto = xt_object_new();
|
|
33
36
|
xt_node_install_emitter(proto);
|
|
@@ -37,6 +40,7 @@ static xt_value xt_http_request_proto(void) {
|
|
|
37
40
|
|
|
38
41
|
static xt_value xt_http_client_proto(void) {
|
|
39
42
|
static xt_value proto = 0;
|
|
43
|
+
xt_gc_add_root(&proto);
|
|
40
44
|
if (proto) return proto;
|
|
41
45
|
proto = xt_object_new();
|
|
42
46
|
xt_node_install_emitter(proto);
|
|
@@ -49,6 +53,7 @@ static xt_value xt_http_client_proto(void) {
|
|
|
49
53
|
|
|
50
54
|
static xt_value xt_http_client_response_proto(void) {
|
|
51
55
|
static xt_value proto = 0;
|
|
56
|
+
xt_gc_add_root(&proto);
|
|
52
57
|
if (proto) return proto;
|
|
53
58
|
proto = xt_object_new();
|
|
54
59
|
xt_node_install_emitter(proto);
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
static xt_value xt_net_socket_proto(void) {
|
|
4
4
|
static xt_value proto = 0;
|
|
5
|
+
xt_gc_add_root(&proto);
|
|
5
6
|
if (proto) return proto;
|
|
6
7
|
proto = xt_object_new();
|
|
7
8
|
xt_node_install_emitter(proto);
|
|
@@ -22,6 +23,7 @@ static xt_value xt_net_socket_proto(void) {
|
|
|
22
23
|
|
|
23
24
|
static xt_value xt_net_server_proto(void) {
|
|
24
25
|
static xt_value proto = 0;
|
|
26
|
+
xt_gc_add_root(&proto);
|
|
25
27
|
if (proto) return proto;
|
|
26
28
|
proto = xt_object_new();
|
|
27
29
|
xt_node_install_emitter(proto);
|
|
@@ -70,16 +70,18 @@ static xt_value xt_process_env(void) {
|
|
|
70
70
|
|
|
71
71
|
static xt_value xt_process_argv(void) {
|
|
72
72
|
int32_t count = xt_program_argc;
|
|
73
|
-
|
|
73
|
+
/* Fill an `xt_array` element by element rather than a raw `xt_value` buffer:
|
|
74
|
+
the collector cannot see a plain malloc'd buffer, so strings created here
|
|
75
|
+
would be swept as soon as a later element allocates (demonstrated by
|
|
76
|
+
`process.argv` returning recycled strings under GC stress). The array is a
|
|
77
|
+
stack local, so the conservative scan keeps it and its elements alive. */
|
|
78
|
+
xt_value result = xt_array_new(0, NULL);
|
|
79
|
+
if (count <= 0 || !xt_program_argv) return result;
|
|
74
80
|
/* Node semantics: `[execPath, scriptPath, ...args]`. A standalone binary is
|
|
75
81
|
both the executable and the script, so the executable path is repeated;
|
|
76
82
|
user arguments therefore start at index 2, matching `node script.js a b`. */
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
items[0] = xt_string_from_cstr(xt_program_argv[0]);
|
|
80
|
-
for (int32_t i = 0; i < count; i++) items[i + 1] = xt_string_from_cstr(xt_program_argv[i]);
|
|
81
|
-
xt_value result = xt_array_new(count + 1, items);
|
|
82
|
-
free(items);
|
|
83
|
+
xt_array_push(result, xt_string_from_cstr(xt_program_argv[0]));
|
|
84
|
+
for (int32_t i = 0; i < count; i++) xt_array_push(result, xt_string_from_cstr(xt_program_argv[i]));
|
|
83
85
|
return result;
|
|
84
86
|
}
|
|
85
87
|
|
|
@@ -363,6 +363,7 @@ xt_value xt_stream_static(xt_value name, int32_t argc, xt_value *argv) {
|
|
|
363
363
|
|
|
364
364
|
static xt_value xt_stream_proto(void) {
|
|
365
365
|
static xt_value proto = 0;
|
|
366
|
+
xt_gc_add_root(&proto);
|
|
366
367
|
if (proto) return proto;
|
|
367
368
|
proto = xt_object_new();
|
|
368
369
|
xt_node_install_emitter(proto);
|
|
@@ -363,13 +363,13 @@ static xt_value util_promisify_executor(xt_value thisValue, xt_value env, int32_
|
|
|
363
363
|
xt_value resolve = xt_arg(argc, argv, 0);
|
|
364
364
|
xt_value reject = xt_arg(argc, argv, 1);
|
|
365
365
|
int32_t count = (int32_t)xt_to_number(xt_array_length(args));
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
366
|
+
/* An `xt_array` keeps the arguments, and the freshly created callback
|
|
367
|
+
closure, reachable if the call below triggers a collection. */
|
|
368
|
+
xt_value argArray = xt_array_new(0, NULL);
|
|
369
|
+
for (int32_t i = 0; i < count; i++) xt_array_push(argArray, xt_array_get(args, xt_number((double)i)));
|
|
369
370
|
xt_value callbackEnv[2] = {resolve, reject};
|
|
370
|
-
|
|
371
|
-
if (XT_IS_FUNCTION(fn)) xt_closure_call(fn, count + 1,
|
|
372
|
-
free(callArgs);
|
|
371
|
+
xt_array_push(argArray, xt_closure_new((void *)util_promisify_callback, 2, callbackEnv));
|
|
372
|
+
if (XT_IS_FUNCTION(fn)) xt_closure_call(fn, count + 1, xt_as_array(argArray)->items);
|
|
373
373
|
return xt_undefined();
|
|
374
374
|
}
|
|
375
375
|
|
package/runtime/rt.h
CHANGED
|
@@ -362,6 +362,16 @@ void xt_println(xt_value v);
|
|
|
362
362
|
size_t xt_heap_bytes(void);
|
|
363
363
|
size_t xt_heap_allocations(void);
|
|
364
364
|
|
|
365
|
+
/* -- garbage collector ---------------------------------------------------- */
|
|
366
|
+
/**
|
|
367
|
+
* Register `slot` as a GC root: a static prototype cache or any value cell
|
|
368
|
+
* that lives outside the heap graph. The slot may be registered before it
|
|
369
|
+
* holds a value; it is marked on every collection and registration is
|
|
370
|
+
* idempotent. `xt_gc_add_root_range` registers `count` consecutive cells.
|
|
371
|
+
*/
|
|
372
|
+
void xt_gc_add_root(xt_value *slot);
|
|
373
|
+
void xt_gc_add_root_range(xt_value *base, size_t count);
|
|
374
|
+
|
|
365
375
|
#ifdef __cplusplus
|
|
366
376
|
}
|
|
367
377
|
#endif
|