ShaderCodeCursor Semicolon Truncation Corrupting Loop Statements

Open Beginner friendly
#18,928 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
85/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
typescript

Research direction

Start with packages/dev/core/src/Engines/Processors/shaderCodeCursor.ts, especially the semicolon-splitting logic around lines 55–72, and reproduce the issue with the for-loop examples in the report. Verify that empty fragments preserve omitted loop expressions and consecutive semicolons, while ordinary semicolon-separated lines remain unchanged.

Written by the indexing model from the issue text.

Description

Issue : ShaderCodeCursor Semicolon Truncation Corrupting Loop Statements

1. Problem Description

Babylon.js includes an internal shader preprocessor that standardizes shader code before feeding it to GLSL or WGSL compilers. The line cursor splits shader lines on semicolons (;) to handle multiple statements on a single line.

However, when a shader contains standard C-style loops with omitted expressions—such as for (;;), for (int i = 0;; i++), or for (int i = 0; i < 10;), or consecutive semicolons ;;—the tokenizer discards empty fragments without preserving their semicolon delimiters.

Consequently:

  • for (;;) becomes for (;) (missing the second semicolon)
  • for (int i = 0;; i++) becomes for (int i = 0; i++)

Both result in invalid shader syntax, causing shader compilation failures. This routinely breaks shaders transpiled by Slang, SPIRV-Cross, or DirectXShaderCompiler (DXC).

2. Root Cause Analysis

In packages/dev/core/src/Engines/Processors/shaderCodeCursor.ts:

} else {
    // Semicolon in the middle of the line
    const split = line.split(";");

    for (let index = 0; index < split.length; index++) {
        let subLine = split[index];

        if (!subLine) {
            continue; // BUG: Drops the delimiter token for empty fragments!
        }

        subLine = subLine.trim();

        if (!subLine) {
            continue; // BUG: Drops empty whitespace-only fragments!
        }

        this._lines.push(subLine + (index !== split.length - 1 ? ";" : ""));
    }
}

Tracing for (;;):

  1. split = ["for (", "", ")"]
  2. index = 0: subLine = "for (" -> pushes "for (;"
  3. index = 1: subLine = "" -> if (!subLine) continue; (skipped completely!)
  4. index = 2: subLine = ")" -> pushes ")"
  5. Recombined output: for (;) — an illegal statement in GLSL and WGSL.
3. Step-by-Step Reproduction
import { ShaderCodeCursor } from "@babylonjs/core/Engines/Processors/shaderCodeCursor";

const cursor = new ShaderCodeCursor();
cursor.lines = [
    "for (;;)",
    "for (int i = 0;; i++)"
];

// Read back processed lines
const output: string[] = [];
while (cursor.canRead) {
    cursor.lineIndex++;
    output.push(cursor.currentLine);
}

console.log("Processed output:", output);
// Actual:   ["for (;", ")", "for (int i = 0;", " i++)"]
// Expected: preserved dual semicolons suitable for loop header syntax
4. Code Fix & Patch
--- a/packages/dev/core/src/Engines/Processors/shaderCodeCursor.ts
+++ b/packages/dev/core/src/Engines/Processors/shaderCodeCursor.ts
@@ -57,17 +57,11 @@ export class ShaderCodeCursor {
                 for (let index = 0; index < split.length; index++) {
                     let subLine = split[index];
 
-                    if (!subLine) {
-                        continue;
-                    }
-
-                    subLine = subLine.trim();
-
-                    if (!subLine) {
-                        continue;
+                    if (subLine !== undefined) {
+                        subLine = subLine.trim();
                     }
 
-                    this._lines.push(subLine + (index !== split.length - 1 ? ";" : ""));
+                    this._lines.push((subLine || "") + (index !== split.length - 1 ? ";" : ""));
                 }
             }
         }
5. Contributor Value & Profile Impact
  • Why it matters: Modern shader pipelines rely heavily on transpilation tools (e.g. WebGPU Slang pipelines). A bug in core line tokenization silently corrupts transpiled shaders.
  • Skills demonstrated: Compiler front-end tokenization, shader preprocessing pipelines, cross-compiler compatibility.

Dominant language
TypeScript
Stars
26.1k
Forks
3.7k
Avg merge
1d 11h
Merged PRs (30d)
86

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from BabylonJS/Babylon.js

All issues in BabylonJS/Babylon.js

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.