Skip to content

Commit ff03372

Browse files
AugustHagedalclaude
andcommitted
fix: escape single quotes and backslashes in IR string output
Strings containing single quotes or backslashes would break the IR parser because appendQuotedString emitted them unescaped inside single-quote delimiters. - Add escapeIRString() helper that escapes \, ', \n, \r, \t - Apply it in appendQuotedString() (removes the long-standing TODO) - Fix the LiteralString IR case to route through appendQuotedString instead of its own inline template literal - Add two regression tests (single-quote and backslash in string literals) Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
1 parent f4c3ba5 commit ff03372

File tree

2 files changed

+35
-3
lines changed

2 files changed

+35
-3
lines changed

compiler/companion/src/native/IRtoString.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,15 @@ function variableToString(variable: NativeCompilerBuilderVariableID): string {
9393
return variable.variable.toString();
9494
}
9595

96+
function escapeIRString(str: string): string {
97+
return str
98+
.replace(/\\/g, '\\\\') // backslash first
99+
.replace(/'/g, "\\'") // single quote
100+
.replace(/\n/g, '\\n') // newline
101+
.replace(/\r/g, '\\r') // carriage return
102+
.replace(/\t/g, '\\t'); // tab
103+
}
104+
96105
class IRStringWriter {
97106
constructor(readonly writer: OutputWriter) {}
98107

@@ -105,8 +114,7 @@ class IRStringWriter {
105114
if (str === undefined) {
106115
return this.append(' <null>');
107116
} else {
108-
// TODO(simon): Escape quotes
109-
return this.append(` '${str}'`);
117+
return this.append(` '${escapeIRString(str)}'`);
110118
}
111119
}
112120

@@ -355,7 +363,7 @@ function outputIR(ir: NativeCompilerIR.Base, output: IRStringWriter): IRStringWr
355363

356364
// storestring '<string_value>' <output_variable>
357365

358-
return output.append(`storestring '${typedIR.value}'`).appendVariable(typedIR.variable);
366+
return output.append('storestring').appendQuotedString(typedIR.value).appendVariable(typedIR.variable);
359367
}
360368
case NativeCompilerIR.Kind.LiteralBool: {
361369
const typedIR = ir as NativeCompilerIR.LiteralBool;

compiler/companion/src/native/NativeCompiler.spec.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4035,4 +4035,28 @@ function_end @1
40354035
`.trim(),
40364036
);
40374037
});
4038+
4039+
it('escapes single quotes in string literals', () => {
4040+
const result = configuredCompile(
4041+
`
4042+
const enum Foo { Value = "it's a test" }
4043+
const s = Foo.Value;
4044+
`,
4045+
{ optimizeSlots: false, optimizeVarRefs: true },
4046+
false, false, false, false, undefined,
4047+
);
4048+
expect(result).toContain("storestring 'it\\'s a test'");
4049+
});
4050+
4051+
it('escapes backslashes in string literals', () => {
4052+
const result = configuredCompile(
4053+
String.raw`
4054+
const enum Foo { Value = "C:\\Users\\file" }
4055+
const s = Foo.Value;
4056+
`,
4057+
{ optimizeSlots: false, optimizeVarRefs: true },
4058+
false, false, false, false, undefined,
4059+
);
4060+
expect(result).toContain("storestring 'C:\\\\Users\\\\file'");
4061+
});
40384062
});

0 commit comments

Comments
 (0)