ShaderCodeCursor Semicolon Truncation Corrupting Loop Statements
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
- Domain
- compilers, computer-graphics
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
- Package:
@babylonjs/core - Subsystem:
ShaderProcessor, Shader Lexical Preprocessing (GLSL & WGSL) - Affected File:
packages/dev/core/src/Engines/Processors/shaderCodeCursor.ts(Lines 55–72) - Status: Preprocessor bug on
masterbranch
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 (;;)becomesfor (;)(missing the second semicolon)for (int i = 0;; i++)becomesfor (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 (;;):
split = ["for (", "", ")"]index = 0:subLine = "for ("-> pushes"for (;"index = 1:subLine = ""->if (!subLine) continue;(skipped completely!)index = 2:subLine = ")"-> pushes")"- 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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from BabylonJS/Babylon.js
-
Difficulty 1/5 Under an hour Newbie friendliness 88/100
BabylonJS/Babylon.js#18926 ·
-
Difficulty 3/5 1-2 days Newbie friendliness 65/100
BabylonJS/Babylon.js#18929 ·
-
Difficulty 3/5 1-2 days Newbie friendliness 70/100
BabylonJS/Babylon.js#18927 ·
-
bug
BabylonJS/Babylon.js#18765 · 1 reaction · 1 assignee ·
-
frame graph stale
BabylonJS/Babylon.js#18108 · 3 comments · 1 reaction · 1 assignee ·
All issues in BabylonJS/Babylon.js
Similar issues
-
calcite-components needs triage refactor
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
Esri/calcite-design-system#15203 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 91/100
-
community first-timers-only good first issue hacktoberfest help wanted low hanging fruit up-for-grabs
Difficulty 1/5 Under an hour Newbie friendliness 95/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Automattic/studio#4908 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 90/100