xbintsc 0.5.38 → 0.5.41

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 (117) hide show
  1. package/dist/src/cli/help.d.ts +1 -1
  2. package/dist/src/codegen/generator/module.js +23 -7
  3. package/dist/src/codegen/generator/module.js.map +1 -1
  4. package/dist/src/driver/version.d.ts +1 -1
  5. package/dist/src/driver/version.js +1 -1
  6. package/dist/src/extensions/gui/html.js +9 -2
  7. package/dist/src/extensions/gui/html.js.map +1 -1
  8. package/dist/tests/codegen/closures.test.js +7 -0
  9. package/dist/tests/codegen/closures.test.js.map +1 -1
  10. package/dist/tests/e2e/compile-objects-classes.test.js +31 -0
  11. package/dist/tests/e2e/compile-objects-classes.test.js.map +1 -1
  12. package/dist/tests/e2e/gui-canvas-state.test.d.ts +9 -0
  13. package/dist/tests/e2e/gui-canvas-state.test.js +297 -0
  14. package/dist/tests/e2e/gui-canvas-state.test.js.map +1 -0
  15. package/dist/tests/e2e/gui-draw-perf.test.d.ts +22 -0
  16. package/dist/tests/e2e/gui-draw-perf.test.js +405 -0
  17. package/dist/tests/e2e/gui-draw-perf.test.js.map +1 -0
  18. package/dist/tests/e2e/gui-perf.test.d.ts +23 -0
  19. package/dist/tests/e2e/gui-perf.test.js +283 -0
  20. package/dist/tests/e2e/gui-perf.test.js.map +1 -0
  21. package/dist/tests/e2e/gui-script-import-order.test.js +9 -1
  22. package/dist/tests/e2e/gui-script-import-order.test.js.map +1 -1
  23. package/dist/tests/e2e/gui-webgl.test.js +4 -4
  24. package/dist/tests/e2e/gui-webgl.test.js.map +1 -1
  25. package/dist/tests/e2e/gui-webgl2-shadow-sampler.test.d.ts +8 -0
  26. package/dist/tests/e2e/gui-webgl2-shadow-sampler.test.js +336 -0
  27. package/dist/tests/e2e/gui-webgl2-shadow-sampler.test.js.map +1 -0
  28. package/dist/tests/extensions/gui.test.js +23 -0
  29. package/dist/tests/extensions/gui.test.js.map +1 -1
  30. package/doc/gui/api.md +28 -2
  31. package/doc/gui/canvas.md +110 -27
  32. package/doc/gui/known-issues.md +32 -10
  33. package/doc/gui/rendering.md +77 -0
  34. package/doc/gui/runtime.md +18 -3
  35. package/doc/gui/webgl.md +38 -19
  36. package/doc/gui.md +33 -1
  37. package/doc/zh-CN/gui/api.md +25 -2
  38. package/doc/zh-CN/gui/canvas.md +95 -26
  39. package/doc/zh-CN/gui/known-issues.md +21 -4
  40. package/doc/zh-CN/gui/rendering.md +59 -0
  41. package/doc/zh-CN/gui/runtime.md +13 -3
  42. package/doc/zh-CN/gui/webgl.md +27 -5
  43. package/doc/zh-CN/gui.md +25 -2
  44. package/package.json +7 -7
  45. package/runtime/ext_gui/browser.cpp +3 -1
  46. package/runtime/ext_gui/canvas.cpp +135 -89
  47. package/runtime/ext_gui/canvas.h +116 -0
  48. package/runtime/ext_gui/canvas_clip.cpp +143 -0
  49. package/runtime/ext_gui/canvas_composite.cpp +241 -0
  50. package/runtime/ext_gui/canvas_coverage.cpp +241 -0
  51. package/runtime/ext_gui/canvas_gradient.cpp +41 -4
  52. package/runtime/ext_gui/canvas_image_data.cpp +4 -0
  53. package/runtime/ext_gui/canvas_internal.h +34 -9
  54. package/runtime/ext_gui/canvas_raster.cpp +207 -178
  55. package/runtime/ext_gui/canvas_shadow.cpp +161 -0
  56. package/runtime/ext_gui/canvas_stroke.cpp +37 -9
  57. package/runtime/ext_gui/canvas_style.cpp +59 -0
  58. package/runtime/ext_gui/canvas_text.cpp +140 -0
  59. package/runtime/ext_gui/css.h +7 -0
  60. package/runtime/ext_gui/css_sheet.cpp +22 -0
  61. package/runtime/ext_gui/document.cpp +25 -1
  62. package/runtime/ext_gui/document.h +8 -0
  63. package/runtime/ext_gui/dom_api_canvas.cpp +16 -1
  64. package/runtime/ext_gui/dom_api_canvas_image.cpp +9 -0
  65. package/runtime/ext_gui/dom_api_canvas_state.cpp +217 -0
  66. package/runtime/ext_gui/dom_api_internal.h +36 -37
  67. package/runtime/ext_gui/dom_api_offscreen.cpp +213 -0
  68. package/runtime/ext_gui/dom_api_proto.cpp +18 -0
  69. package/runtime/ext_gui/dom_api_webgl.cpp +19 -9
  70. package/runtime/ext_gui/dom_api_webgl_internal.h +58 -0
  71. package/runtime/ext_gui/dom_api_webgl_source.cpp +18 -0
  72. package/runtime/ext_gui/gradient_values.cpp +12 -1
  73. package/runtime/ext_gui/gui.cpp +5 -0
  74. package/runtime/ext_gui/gui_canvas.cpp +4 -0
  75. package/runtime/ext_gui/gui_engine.h +30 -0
  76. package/runtime/ext_gui/gui_events.cpp +182 -58
  77. package/runtime/ext_gui/gui_media.cpp +3 -0
  78. package/runtime/ext_gui/image.cpp +69 -0
  79. package/runtime/ext_gui/image_bitmap_browser.cpp +11 -0
  80. package/runtime/ext_gui/layout.cpp +4 -0
  81. package/runtime/ext_gui/paint.cpp +19 -0
  82. package/runtime/ext_gui/paint_background.cpp +18 -1
  83. package/runtime/ext_gui/perf.cpp +171 -0
  84. package/runtime/ext_gui/perf.h +127 -0
  85. package/runtime/ext_gui/renderer.cpp +109 -41
  86. package/runtime/ext_gui/renderer.h +33 -9
  87. package/runtime/ext_gui/renderer_image.cpp +135 -65
  88. package/runtime/ext_gui/renderer_internal.h +15 -3
  89. package/runtime/ext_gui/renderer_text.cpp +24 -12
  90. package/runtime/ext_gui/style.cpp +101 -30
  91. package/runtime/ext_gui/style.h +6 -3
  92. package/runtime/ext_gui/style_print.cpp +7 -1
  93. package/runtime/ext_gui/text.cpp +23 -4
  94. package/runtime/ext_gui/text.h +29 -0
  95. package/runtime/ext_gui/text_cache.cpp +118 -0
  96. package/runtime/ext_gui/text_glyph_cache.cpp +100 -0
  97. package/runtime/ext_gui/webgl.h +48 -146
  98. package/runtime/ext_gui/webgl_blit.cpp +12 -1
  99. package/runtime/ext_gui/webgl_blit_depth.cpp +270 -0
  100. package/runtime/ext_gui/webgl_buffer.cpp +59 -1
  101. package/runtime/ext_gui/webgl_buffer_objects.cpp +106 -9
  102. package/runtime/ext_gui/webgl_clear.cpp +3 -0
  103. package/runtime/ext_gui/webgl_context.cpp +29 -3
  104. package/runtime/ext_gui/webgl_draw.cpp +75 -270
  105. package/runtime/ext_gui/webgl_draw_buffers.cpp +1 -0
  106. package/runtime/ext_gui/webgl_draw_setup.cpp +321 -0
  107. package/runtime/ext_gui/webgl_draw_state.h +130 -0
  108. package/runtime/ext_gui/webgl_glsl_scan.cpp +3 -8
  109. package/runtime/ext_gui/webgl_objects.h +148 -0
  110. package/runtime/ext_gui/webgl_pipeline.cpp +8 -8
  111. package/runtime/ext_gui/webgl_program_internal.h +35 -0
  112. package/runtime/ext_gui/webgl_shader.cpp +22 -30
  113. package/runtime/ext_gui/webgl_webgl2.cpp +36 -0
  114. package/runtime/ext_gui/window.cpp +56 -1
  115. package/src/codegen/generator/module.ts +22 -7
  116. package/src/driver/version.ts +1 -1
  117. package/src/extensions/gui/html.ts +9 -2
@@ -22,36 +22,8 @@
22
22
  namespace xtgui {
23
23
  namespace {
24
24
 
25
- int gl_type_of(const std::string &type) {
26
- if (type == "float") return GL_FLOAT;
27
- if (type == "vec2") return GL_FLOAT_VEC2;
28
- if (type == "vec3") return GL_FLOAT_VEC3;
29
- if (type == "vec4") return GL_FLOAT_VEC4;
30
- if (type == "int" || type == "uint") return GL_INT;
31
- if (type == "ivec2" || type == "uvec2") return GL_INT_VEC2;
32
- if (type == "ivec3" || type == "uvec3") return GL_INT_VEC3;
33
- if (type == "ivec4" || type == "uvec4") return GL_INT_VEC4;
34
- if (type == "bool") return GL_BOOL;
35
- if (type == "bvec2") return GL_BOOL_VEC2;
36
- if (type == "bvec3") return GL_BOOL_VEC3;
37
- if (type == "bvec4") return GL_BOOL_VEC4;
38
- if (type == "mat2") return GL_FLOAT_MAT2;
39
- if (type == "mat3") return GL_FLOAT_MAT3;
40
- if (type == "mat4") return GL_FLOAT_MAT4;
41
- if (type == "sampler2D") return GL_SAMPLER_2D;
42
- if (type == "samplerCube") return GL_SAMPLER_CUBE;
43
- if (type == "sampler3D") return GL_SAMPLER_3D;
44
- if (type == "sampler2DArray") return GL_SAMPLER_2D_ARRAY;
45
- return 0;
46
- }
47
-
48
- /** How many consecutive attribute/varying locations a type consumes. */
49
- int location_slots(const std::string &type) {
50
- if (type == "mat2") return 2;
51
- if (type == "mat3") return 3;
52
- if (type == "mat4") return 4;
53
- return 1;
54
- }
25
+ using webgl_detail::gl_type_of;
26
+ using webgl_detail::location_slots;
55
27
 
56
28
  /** Merge uniforms declared by both stages, in declaration order. */
57
29
  bool merge_uniforms(const GlslShaderInfo &vs, const GlslShaderInfo &fs,
@@ -232,6 +204,9 @@ void WebGLContext::release_program_pipelines(WebGLProgram *program) {
232
204
  if (program->fs_shader != nullptr) SDL_ReleaseGPUShader(guidev::g_device, program->fs_shader);
233
205
  program->vs_shader = nullptr;
234
206
  program->fs_shader = nullptr;
207
+ /* Prepared draws hold pipeline pointers too; a relink reuses the same program
208
+ * id, so they must not be served from the cache after this. */
209
+ clear_draw_cache();
235
210
  }
236
211
 
237
212
  void WebGLContext::delete_program(uint32_t id) {
@@ -425,6 +400,19 @@ bool WebGLContext::link_program(uint32_t id) {
425
400
  record->vs_sampler_uniforms.clear();
426
401
  record->fs_sampler_uniforms.clear();
427
402
 
403
+ /* Resolve the active attributes once: a draw then iterates this plan instead
404
+ * of re-scanning `attribs` and hashing every attribute name again. */
405
+ record->draw_attribs.clear();
406
+ record->draw_attribs.reserve(record->attribs.size());
407
+ for (const GlslUniformDecl &decl : record->attribs) {
408
+ auto found = record->attrib_locations.find(decl.name);
409
+ if (found == record->attrib_locations.end()) continue;
410
+ WebGLDrawAttribPlan plan;
411
+ plan.location = found->second;
412
+ plan.type = decl.type;
413
+ record->draw_attribs.push_back(plan);
414
+ }
415
+
428
416
  for (const GlslBlockMember &member : members) {
429
417
  WebGLUniform uniform;
430
418
  uniform.name = member.name;
@@ -442,14 +430,18 @@ bool WebGLContext::link_program(uint32_t id) {
442
430
 
443
431
  auto add_sampler = [](WebGLProgram *program, std::vector<int> *stage,
444
432
  const GlslUniformDecl &decl) {
433
+ const bool compare = decl.type == "sampler2DShadow" || decl.type == "samplerCubeShadow" ||
434
+ decl.type == "sampler2DArrayShadow";
445
435
  int index = -1;
446
436
  auto found = program->uniform_index.find(decl.name);
447
437
  if (found != program->uniform_index.end()) {
448
438
  index = found->second;
439
+ program->uniforms[(size_t)index].compare = compare;
449
440
  } else {
450
441
  WebGLUniform uniform;
451
442
  uniform.name = decl.name;
452
443
  uniform.sampler = true;
444
+ uniform.compare = compare;
453
445
  uniform.unit = 0;
454
446
  uniform.layout.gl_type = gl_type_of(decl.type);
455
447
  index = (int)program->uniforms.size();
@@ -78,6 +78,7 @@ void WebGLContext::save_vertex_array() {
78
78
  void WebGLContext::load_vertex_array(const WebGLVertexArray *array) {
79
79
  for (int i = 0; i < kWebGLMaxAttribs; i++) attribs_[i] = array->attribs[i];
80
80
  bound_element_ = array->element_buffer;
81
+ attribs_version_ += 1;
81
82
  }
82
83
 
83
84
  void WebGLContext::bind_vertex_array(uint32_t id) {
@@ -238,6 +239,11 @@ bool WebGLContext::sampler_parameter(uint32_t id, int pname, int value) {
238
239
  else if (pname == GL_TEXTURE_MAG_FILTER) record->mag_filter = value;
239
240
  else if (pname == GL_TEXTURE_WRAP_S) record->wrap_s = value;
240
241
  else if (pname == GL_TEXTURE_WRAP_T) record->wrap_t = value;
242
+ else if (pname == GL_TEXTURE_COMPARE_MODE) {
243
+ record->compare_mode = value == GL_COMPARE_REF_TO_TEXTURE;
244
+ } else if (pname == GL_TEXTURE_COMPARE_FUNC) {
245
+ record->compare_func = value;
246
+ }
241
247
  if (record->gpu != nullptr && guidev::g_device != nullptr) {
242
248
  SDL_ReleaseGPUSampler(guidev::g_device, record->gpu);
243
249
  record->gpu = nullptr;
@@ -252,6 +258,10 @@ int WebGLContext::sampler_parameter_value(uint32_t id, int pname) {
252
258
  if (pname == GL_TEXTURE_MAG_FILTER) return record->mag_filter;
253
259
  if (pname == GL_TEXTURE_WRAP_S) return record->wrap_s;
254
260
  if (pname == GL_TEXTURE_WRAP_T) return record->wrap_t;
261
+ if (pname == GL_TEXTURE_COMPARE_MODE) {
262
+ return record->compare_mode ? GL_COMPARE_REF_TO_TEXTURE : GL_NONE;
263
+ }
264
+ if (pname == GL_TEXTURE_COMPARE_FUNC) return record->compare_func;
255
265
  return 0;
256
266
  }
257
267
 
@@ -270,6 +280,32 @@ SDL_GPUSampler *WebGLContext::sampler_gpu(WebGLSampler *record) {
270
280
  /* A sampler object has no texture, so it cannot know how many levels exist;
271
281
  * textures created by this engine always carry a full mip chain. */
272
282
  info.max_lod = 1000.0f;
283
+ if (record->compare_mode) {
284
+ info.enable_compare = true;
285
+ switch (record->compare_func) {
286
+ case GL_NEVER:
287
+ info.compare_op = SDL_GPU_COMPAREOP_NEVER;
288
+ break;
289
+ case GL_LESS:
290
+ info.compare_op = SDL_GPU_COMPAREOP_LESS;
291
+ break;
292
+ case GL_EQUAL:
293
+ info.compare_op = SDL_GPU_COMPAREOP_EQUAL;
294
+ break;
295
+ case GL_GREATER:
296
+ info.compare_op = SDL_GPU_COMPAREOP_GREATER;
297
+ break;
298
+ case GL_NOTEQUAL:
299
+ info.compare_op = SDL_GPU_COMPAREOP_NOT_EQUAL;
300
+ break;
301
+ case GL_GEQUAL:
302
+ info.compare_op = SDL_GPU_COMPAREOP_GREATER_OR_EQUAL;
303
+ break;
304
+ default:
305
+ info.compare_op = SDL_GPU_COMPAREOP_LESS_OR_EQUAL;
306
+ break;
307
+ }
308
+ }
273
309
  record->gpu = SDL_CreateGPUSampler(guidev::g_device, &info);
274
310
  return record->gpu;
275
311
  }
@@ -15,6 +15,7 @@
15
15
 
16
16
  #include "inline_svg.h"
17
17
  #include "paint.h"
18
+ #include "perf.h"
18
19
  #include "text.h"
19
20
 
20
21
  static xt_value g_window_proto = XT_UNDEFINED;
@@ -72,10 +73,15 @@ static xt_value win_load_html(xt_value self, xt_value env, int32_t argc, xt_valu
72
73
  xt_gui_window_viewport(win, &width, &height);
73
74
  win->document->load(html, width, height);
74
75
  win->geometry.dirty = 1;
76
+ win->present_dirty = 1;
75
77
  /* Run the AOT-compiled `<script>` bodies now that the tree is parsed, so
76
78
  * `document.getElementById(...)` resolves, then settle the first layout. */
77
79
  xt_gui_run_scripts(win);
78
80
  xt_gui_flush_dom(win);
81
+ /* The document was replaced, so the frame loop must reconcile the media
82
+ * and canvas tables with it on the next frame. `loadHTML` already flushed
83
+ * the style/layout, so this is a sync request, not a restyle. */
84
+ win->sync_dirty = 1;
79
85
  }
80
86
  xt_object_set(self, xt_string_from_cstr("__xt_gui_html"), xt_string_from_cstr(html.c_str()));
81
87
  /* Painting the document is milestone M3b+; the parsed tree and computed
@@ -199,6 +205,7 @@ static xt_value win_set_background(xt_value self, xt_value env, int32_t argc, xt
199
205
  win->background[1] = color.g;
200
206
  win->background[2] = color.b;
201
207
  win->background[3] = color.a;
208
+ win->present_dirty = 1;
202
209
  }
203
210
  }
204
211
  xt_object_set(self, xt_string_from_cstr("__xt_gui_background"), text);
@@ -265,7 +272,10 @@ static xt_value win_advance(xt_value self, xt_value env, int32_t argc, xt_value
265
272
  XtGuiWindow *win = xt_gui_window_from_this(self);
266
273
  if (win == nullptr || win->document == nullptr) return XT_UNDEFINED;
267
274
  double ms = argc > 0 ? xt_to_number(xt_arg(argc, argv, 0)) : 0.0;
268
- if (win->document->advance(ms)) win->geometry.dirty = 1;
275
+ if (win->document->advance(ms)) {
276
+ win->geometry.dirty = 1;
277
+ win->present_dirty = 1;
278
+ }
269
279
  return XT_UNDEFINED;
270
280
  }
271
281
 
@@ -481,6 +491,51 @@ static xt_value win_stats(xt_value self, xt_value env, int32_t argc, xt_value *a
481
491
  xt_number(win != nullptr ? (double)win->node_order.size() : 0));
482
492
  xt_object_set(object, xt_string_from_cstr("listeners"),
483
493
  xt_number(win != nullptr ? (double)win->node_listeners.size() : 0));
494
+ /* -- renderer counters (see `perf.h`) ---------------------------------- */
495
+ /* The `*Ms` values describe the last completed frame; every other counter is
496
+ * cumulative (diff two calls to get the work one frame did). */
497
+ const xtgui::PerfTimings &ms = xtgui::g_perf_last;
498
+ xt_object_set(object, xt_string_from_cstr("frameMs"), xt_number(ms.frame));
499
+ xt_object_set(object, xt_string_from_cstr("restyleMs"), xt_number(ms.restyle));
500
+ xt_object_set(object, xt_string_from_cstr("layoutMs"), xt_number(ms.layout));
501
+ xt_object_set(object, xt_string_from_cstr("paintMs"), xt_number(ms.paint));
502
+ xt_object_set(object, xt_string_from_cstr("uploadMs"), xt_number(ms.upload));
503
+ xt_object_set(object, xt_string_from_cstr("drawMs"), xt_number(ms.draw));
504
+ const xtgui::PerfCounters &counters = xtgui::g_perf_total;
505
+ auto setCounter = [&object](const char *name, uint64_t value) {
506
+ xt_object_set(object, xt_string_from_cstr(name), xt_number((double)value));
507
+ };
508
+ setCounter("frames", counters.frames);
509
+ setCounter("presents", counters.presents);
510
+ setCounter("exposes", counters.exposes);
511
+ setCounter("deferred", counters.deferred);
512
+ setCounter("starvedFrames", counters.starvedFrames);
513
+ setCounter("skippedFrames", counters.skippedFrames);
514
+ setCounter("restyles", counters.restyles);
515
+ setCounter("restyleNodes", counters.restyleNodes);
516
+ setCounter("rulesEvaluated", counters.rulesEvaluated);
517
+ setCounter("mediaEvaluated", counters.mediaEvaluated);
518
+ setCounter("layouts", counters.layouts);
519
+ setCounter("layoutBoxes", counters.layoutBoxes);
520
+ setCounter("textShapeCalls", counters.textShapeCalls);
521
+ setCounter("textShapeHits", counters.textShapeHits);
522
+ setCounter("textCacheBytes", xtgui::xt_text_cache_bytes());
523
+ setCounter("textCacheEntries", xtgui::xt_text_cache_entries());
524
+ setCounter("paints", counters.paints);
525
+ setCounter("paintOps", counters.paintOps);
526
+ setCounter("drawRuns", counters.drawRuns);
527
+ setCounter("drawCalls", counters.drawCalls);
528
+ setCounter("uploadSubmits", counters.uploadSubmits);
529
+ setCounter("vertices", counters.vertices);
530
+ setCounter("syncNodes", counters.syncNodes);
531
+ setCounter("transitionFrames", counters.transitionFrames);
532
+ setCounter("canvasDraws", counters.canvasDraws);
533
+ setCounter("canvasUploads", counters.canvasUploads);
534
+ setCounter("canvasUploadBytes", counters.canvasUploadBytes);
535
+ setCounter("webglDraws", counters.webglDraws);
536
+ setCounter("webglPasses", counters.webglPasses);
537
+ setCounter("webglPipelineMisses", counters.webglPipelineMisses);
538
+ setCounter("webglBufferUploadBytes", counters.webglBufferUploadBytes);
484
539
  return object;
485
540
  }
486
541
 
@@ -160,6 +160,18 @@ export const moduleMethods: ModuleMethods = {
160
160
  );
161
161
  }
162
162
 
163
+ // Captures threaded through the environment. Declared before the
164
+ // parameters because a default parameter may read an enclosing binding
165
+ // (e.g. `constructor(wrapS = ClampToEdgeWrapping)`), and a capture has no
166
+ // other slot to read.
167
+ for (const symbol of fn.captures) {
168
+ if (this.moduleGlobals.has(symbol.id)) continue;
169
+ const index = fn.captureIndex.get(symbol.id)!;
170
+ const value = this.reg();
171
+ this.emit(` ${value} = call i64 @xt_closure_env(i64 %env, i32 ${index})`);
172
+ this.defineCaptureSlot(symbol, value);
173
+ }
174
+
163
175
  // Parameters.
164
176
  const parameterNodes = (fn.node as { parameters?: Parameter[] }).parameters ?? [];
165
177
  for (let index = 0; index < fn.params.length; index++) {
@@ -185,13 +197,16 @@ export const moduleMethods: ModuleMethods = {
185
197
  if (parameter.initializer) this.emitDefaultParameter(symbol, value, parameter.initializer);
186
198
  }
187
199
 
188
- // Captures threaded through the environment.
189
- for (const symbol of fn.captures) {
190
- if (this.moduleGlobals.has(symbol.id)) continue;
191
- const index = fn.captureIndex.get(symbol.id)!;
192
- const value = this.reg();
193
- this.emit(` ${value} = call i64 @xt_closure_env(i64 %env, i32 ${index})`);
194
- this.defineCaptureSlot(symbol, value);
200
+ // Boxed bindings declared in this frame get their box now, before any
201
+ // statement runs. A closure built earlier in the body — a class's methods
202
+ // are installed by the class statement, which may precede the `const` the
203
+ // method reads — captures the box pointer, so the later declaration fills
204
+ // the same box. Without this the closure would capture an undefined slot
205
+ // (Three.js declares `_zero`/`_one` after the class that uses them).
206
+ for (const symbol of fn.locals) {
207
+ if (!symbol.boxed || this.moduleGlobals.has(symbol.id)) continue;
208
+ if (this.current.slots.has(symbol.id)) continue;
209
+ this.declareSlot(symbol, i64(XT_UNDEFINED));
195
210
  }
196
211
 
197
212
  // Hoisted function declarations: create their closure values in this
@@ -9,4 +9,4 @@
9
9
  * `main`/`master` only (see `.githooks/pre-commit`), so feature branches never
10
10
  * change this line and merging two of them cannot conflict here.
11
11
  */
12
- export const COMPILER_VERSION = "0.5.38";
12
+ export const COMPILER_VERSION = "0.5.41";
@@ -163,7 +163,10 @@ function deferredPrelude(alias: string, declaration: ImportDeclaration): string[
163
163
 
164
164
  /** Split an external script module into hoisted imports and a body. */
165
165
  function compileModuleSource(htmlPath: string, scriptPath: string, scriptIndex: number): CompiledModule {
166
- const source = readFileSync(scriptPath, "utf8");
166
+ /* Mask the normalized text: the parser indexes the `SourceFile` text, where
167
+ * `\r\n` already became `\n`, so blanking statement spans in the raw file
168
+ * would remove the wrong characters on a CRLF checkout. */
169
+ const source = new SourceFile(scriptPath, readFileSync(scriptPath, "utf8")).text;
167
170
  const bag = new DiagnosticBag();
168
171
  const parsed = new Parser(new SourceFile(scriptPath, source), bag).parseSourceFile();
169
172
  const chars = source.split("");
@@ -256,6 +259,10 @@ function resolveScriptPath(htmlPath: string, src: string): string {
256
259
 
257
260
  /** Rewrite `<script>` bodies into registered AOT functions + an HTML string. */
258
261
  export function loadHtmlAsset(path: string, source: string): AssetLoadResult {
262
+ /* The generated module is parsed as a `SourceFile`, which rewrites `\r\n` to
263
+ * `\n`; normalize first so the module text, the offsets recorded for
264
+ * `<script src>` bodies and the exported HTML string all share one layout. */
265
+ const page = new SourceFile(path, source).text;
259
266
  /* `file` is set for an external `<script src>`: its body keeps that file as
260
267
  * the origin of every node, so a `.js` script stays untyped and diagnostics
261
268
  * name the script, not the HTML page. */
@@ -267,7 +274,7 @@ export function loadHtmlAsset(path: string, source: string): AssetLoadResult {
267
274
 
268
275
  /* The engine's cascade only reads `<style>` elements, so a linked stylesheet
269
276
  * would be dropped at runtime. Inline it (and its `@import`s) now. */
270
- const styles = inlineStylesheets(path, source);
277
+ const styles = inlineStylesheets(path, page);
271
278
  const html = styles.html.replace(SCRIPT_RE, (match, attributes: string, inlineBody: string) => {
272
279
  if (!isExecutable(attribute(attributes, "type"), attribute(attributes, "lang"))) return match;
273
280
  const src = attribute(attributes, "src");