Skip to content

Commit 07e747b

Browse files
authored
Fix escapes in raw template payloads (#8630)
* Fix escapes in raw template payloads Signed-off-by: Christoph Knittel <ck@cca.io> * Link changelog to pull request Signed-off-by: Christoph Knittel <ck@cca.io> --------- Signed-off-by: Christoph Knittel <ck@cca.io>
1 parent 30c781c commit 07e747b

9 files changed

Lines changed: 61 additions & 4 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636

3737
#### :bug: Bug fix
3838

39+
- Fix escaped backticks and interpolation openers in backquoted `%raw`, `%ffi`, and `%re` payloads leaking into emitted JavaScript. https://github.com/rescript-lang/rescript/pull/8630
3940
- Fix the side-effect analysis treating bigint exponentiation and bounds-checked array and string reads as pure, which let dead-code elimination drop an unused one that throws: `let _ = 2n ** -1n` no longer raised. https://github.com/rescript-lang/rescript/pull/8617
4041
- Preserve record field `@as` annotations when formatting object types containing spreads. https://github.com/rescript-lang/rescript/pull/8619
4142
- Fix excessive parentheses and indentation in function assignments to refs, align record and array assignment formatting across refs and fields, and preserve function return-type parentheses and consistent JSX fragment layout in callbacks. https://github.com/rescript-lang/rescript/pull/8611

compiler/ml/ast_payload.ml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,8 +193,8 @@ let raw_as_string_exp_exn ~(kind : Js_raw_info.raw_kind) ?is_function (x : t) :
193193
_ );
194194
};
195195
] ->
196-
Some
197-
(source.txt, Bs_flow_ast_utils.flow_deli_offset (Some "js"), expression)
196+
let source = String_literal.decode_raw_template_source source.txt in
197+
Some (source, Bs_flow_ast_utils.flow_deli_offset (Some "js"), expression)
198198
| PStr
199199
[
200200
{

compiler/ml/string_literal.ml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,39 @@ let decode_js_escapes =
219219
let decode_js_template_escapes =
220220
decode_js_escapes_with ~normalize_template_line_endings:true
221221

222+
let decode_raw_template_source source =
223+
let length = String.length source in
224+
let buffer = Buffer.create length in
225+
let rec loop index =
226+
if index < length then
227+
match String.unsafe_get source index with
228+
| '\\' when index + 1 < length -> (
229+
match String.unsafe_get source (index + 1) with
230+
| ('\\' | '$' | '`') as escaped ->
231+
Buffer.add_char buffer escaped;
232+
loop (index + 2)
233+
| '\n' -> loop (index + 2)
234+
| '\r' ->
235+
if index + 2 < length && String.unsafe_get source (index + 2) = '\n'
236+
then loop (index + 3)
237+
else loop (index + 2)
238+
| '\226'
239+
when index + 3 < length
240+
&& String.unsafe_get source (index + 2) = '\128'
241+
&& (String.unsafe_get source (index + 3) = '\168'
242+
|| String.unsafe_get source (index + 3) = '\169') ->
243+
loop (index + 4)
244+
| escaped ->
245+
Buffer.add_char buffer '\\';
246+
Buffer.add_char buffer escaped;
247+
loop (index + 2))
248+
| character ->
249+
Buffer.add_char buffer character;
250+
loop (index + 1)
251+
in
252+
loop 0;
253+
Buffer.contents buffer
254+
222255
type encode_js_mode = String | Template
223256

224257
let encode_js mode s =

compiler/ml/string_literal.mli

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@ val decode_js_template_escapes : string -> string option
5555
as required by JavaScript template-literal semantics. Returns [None] for
5656
malformed escapes, malformed UTF-8, or an unescaped interpolation opener. *)
5757

58+
val decode_raw_template_source : string -> string
59+
(** Remove the escapes that protect the surrounding ReScript template syntax
60+
from JavaScript source embedded in a [raw], [ffi], or [re] extension.
61+
Backslashes belonging to the embedded JavaScript, such as [\\n], are
62+
preserved. *)
63+
5864
val encode_js_string : string -> string
5965
(** Encode a semantic UTF-8 string as a canonical JavaScript string-literal
6066
body. *)

tests/ounit_tests/ounit_string_literal_tests.ml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,14 @@ let suites =
407407
( "template segments reject interpolation openers" >:: fun _ ->
408408
assert_invalid_template "${value}";
409409
assert_template_decoded ~encoded:"\\${value}" ~expected:"${value}" );
410+
( "raw templates decode only their surrounding syntax escapes"
411+
>:: fun _ ->
412+
OUnit.assert_equal ~printer:(Printf.sprintf "%S")
413+
{e|`${"hello"}` with \n and \t|e}
414+
(String_literal.decode_raw_template_source
415+
{e|\`\${"hello"}\` with \n and \t|e});
416+
OUnit.assert_equal ~printer:(Printf.sprintf "%S") "continued"
417+
(String_literal.decode_raw_template_source "con\\\r\ntinued") );
410418
( "ordinary literals become semantic strings" >:: fun _ ->
411419
assert_parsed_string ~source:{|\x61\n\uD83D\uDE00|}
412420
~expected_semantic:"a\n😀" );

tests/tests/src/gbk.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@ console.log("你好");
99

1010
console.log("你好你好");
1111

12-
console.log("\\u4f60\\u597d");
12+
console.log("\u4f60\u597d");
1313

1414
/* Not a pure module */

tests/tests/src/raw_output_test.mjs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ function mk(fn) {
99

1010
console.log(1);
1111

12+
var issue6236 = `${"hello"}`;
13+
;
14+
1215
export {
1316
mk,
1417
}

tests/tests/src/raw_output_test.res

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,9 @@ let mk = fn => fn()
1212
mk(%raw(`(_)=> console.log('should works')`))
1313

1414
Console.log((() => 1)())
15+
16+
// GitHub issue #6236: escapes needed for the surrounding ReScript template
17+
// must not survive in the emitted raw JavaScript.
18+
%%raw(`
19+
var issue6236 = \`\${"hello"}\`;
20+
`)

tests/tests/src/unsafe_ppx_test.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import * as Pervasives from "@rescript/runtime/lib/es6/Pervasives.mjs";
55
import * as Test_utils from "./test_utils.mjs";
66
import * as Ffi_js_test from "./ffi_js_test.mjs";
77

8-
let x = "\\x01\\x02\\x03";
8+
let x = "\x01\x02\x03";
99

1010
let max = Math.max;
1111

0 commit comments

Comments
 (0)