Skip to content

Commit b1cd1f8

Browse files
committed
Enforce string literal payload invariants
Signed-off-by: Christoph Knittel <ck@cca.io>
1 parent c84542d commit b1cd1f8

27 files changed

Lines changed: 278 additions & 182 deletions

analysis/src/dump_ast.ml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,12 @@ let print_constant const =
4545
| Pconst_char {source; semantic} ->
4646
"Pconst_char(source=" ^ source ^ ", semantic=" ^ string_of_int semantic
4747
^ ")"
48-
| Pconst_string {source; semantic} ->
49-
"Pconst_string(source=" ^ source ^ ", semantic=" ^ semantic ^ ")"
48+
| Pconst_string payload ->
49+
"Pconst_string(source="
50+
^ String_literal.source payload
51+
^ ", semantic="
52+
^ String_literal.semantic payload
53+
^ ")"
5054
| Pconst_json source -> "Pconst_json(" ^ source ^ ")"
5155
| Pconst_raw_source source -> "Pconst_raw_source(" ^ source ^ ")"
5256
| Pconst_float (s, _) -> "Pconst_float(" ^ s ^ ")"

compiler/core/j.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ and expression_desc =
149149
longer needs to be preserved. For example, ["a\\n"] is stored with
150150
an actual newline and is emitted using the compiler's chosen quoting
151151
and escaping. *)
152-
| Template_literal of {source: string; semantic: string}
152+
| Template_literal of Asttypes.template_segment
153153
(** A non-interpolated ordinary backquoted literal. For [`a\n`], [source]
154154
is ["a\\n"] and [semantic] contains an actual newline. [source] is
155155
used for JavaScript emission; [semantic] is used for comparisons and

compiler/core/js_analyzer.ml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -229,10 +229,9 @@ let rec eq_expression ({expression_desc = x0} : J.expression)
229229
match y0 with
230230
| Str a1 -> a0 = a1
231231
| _ -> false)
232-
| Template_literal {source = source0; semantic = semantic0} -> (
232+
| Template_literal segment0 -> (
233233
match y0 with
234-
| Template_literal {source = source1; semantic = semantic1} ->
235-
source0 = source1 && semantic0 = semantic1
234+
| Template_literal segment1 -> segment0 = segment1
236235
| _ -> false)
237236
| Static_index (e0, p0, off0) -> (
238237
match y0 with

compiler/core/js_dump.ml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -710,11 +710,11 @@ and expression_desc cxt ~(level : int) f x : cxt =
710710
P.string f "`";
711711
let rec print_segments cxt segments values =
712712
match (segments, values) with
713-
| [({source} : Asttypes.template_segment)], [] ->
714-
P.string f source;
713+
| [segment], [] ->
714+
P.string f (String_literal.source segment);
715715
cxt
716-
| ({source} : Asttypes.template_segment) :: segments, value :: values ->
717-
P.string f source;
716+
| segment :: segments, value :: values ->
717+
P.string f (String_literal.source segment);
718718
P.string f "${";
719719
let cxt = expression cxt ~level:0 f value in
720720
P.string f "}";
@@ -734,8 +734,8 @@ and expression_desc cxt ~(level : int) f x : cxt =
734734
| Str txt ->
735735
Js_dump_string.pp_string f txt;
736736
cxt
737-
| Template_literal {source} ->
738-
P.string f ("`" ^ source ^ "`");
737+
| Template_literal segment ->
738+
P.string f ("`" ^ String_literal.source segment ^ "`");
739739
cxt
740740
| Json_literal source ->
741741
P.string f source;

compiler/core/js_exp_make.ml

Lines changed: 23 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -81,43 +81,35 @@ let interpolated_template ?comment segments values : t =
8181
if value.comment <> None then None
8282
else
8383
match value.expression_desc with
84-
| Str semantic ->
85-
Some
86-
({source = String_literal.encode_js_template semantic; semantic}
87-
: Asttypes.template_segment)
88-
| Template_literal {source; semantic} ->
89-
Some ({source; semantic} : Asttypes.template_segment)
84+
| Str semantic -> Some (String_literal.template_from_semantic semantic)
85+
| Template_literal segment -> Some segment
9086
| _ -> None
9187
in
92-
let rec merge rev_segments rev_values
93-
(segments : Asttypes.template_segment list) values =
88+
let rec merge rev_segments rev_values rev_parts segments values =
9489
match (segments, values) with
95-
| [segment], [] -> (List.rev (segment :: rev_segments), List.rev rev_values)
96-
| segment :: next_segment :: rest, value :: values -> (
90+
| [], [] ->
91+
let segment = String_literal.concat_template (List.rev rev_parts) in
92+
(List.rev (segment :: rev_segments), List.rev rev_values)
93+
| next_segment :: rest, value :: values -> (
9794
match literal_segment value with
9895
| Some literal ->
99-
let semantic =
100-
segment.semantic ^ literal.semantic ^ next_segment.semantic
101-
in
102-
let preserved_source =
103-
segment.source ^ literal.source ^ next_segment.source
104-
in
105-
let source =
106-
match String_literal.decode_js_template_escapes preserved_source with
107-
| Some decoded when decoded = semantic -> preserved_source
108-
| _ -> String_literal.encode_js_template semantic
109-
in
110-
let merged : Asttypes.template_segment = {source; semantic} in
111-
merge rev_segments rev_values (merged :: rest) values
96+
merge rev_segments rev_values
97+
(next_segment :: literal :: rev_parts)
98+
rest values
11299
| None ->
113-
merge (segment :: rev_segments) (value :: rev_values)
114-
(next_segment :: rest) values)
100+
let segment = String_literal.concat_template (List.rev rev_parts) in
101+
merge (segment :: rev_segments) (value :: rev_values) [next_segment]
102+
rest values)
115103
| _ -> assert false
116104
in
117-
let segments, values = merge [] [] segments values in
105+
let segments, values =
106+
match segments with
107+
| segment :: segments -> merge [] [] [segment] segments values
108+
| [] -> assert false
109+
in
118110
let expression_desc =
119111
match (segments, values) with
120-
| [{source; semantic}], [] -> J.Template_literal {source; semantic}
112+
| [segment], [] -> J.Template_literal segment
121113
| _ -> J.Interpolated_template {segments; values}
122114
in
123115
{expression_desc; comment; source_loc = None}
@@ -206,12 +198,8 @@ let pure_runtime_call module_name fn_name args =
206198
let str ?comment txt : t =
207199
{expression_desc = Str txt; comment; source_loc = None}
208200

209-
let template_literal ?comment ~semantic source : t =
210-
{
211-
expression_desc = Template_literal {source; semantic};
212-
comment;
213-
source_loc = None;
214-
}
201+
let template_literal ?comment segment : t =
202+
{expression_desc = Template_literal segment; comment; source_loc = None}
215203

216204
let json_literal ?comment source : t =
217205
{expression_desc = Json_literal source; comment; source_loc = None}
@@ -692,7 +680,8 @@ let array_length ?comment (e : t) : t =
692680

693681
let string_literal_semantic (e : t) =
694682
match e.expression_desc with
695-
| Str semantic | Template_literal {semantic} -> Some semantic
683+
| Str semantic -> Some semantic
684+
| Template_literal segment -> Some (String_literal.semantic segment)
696685
| _ -> None
697686

698687
let string_length ?comment (e : t) : t =

compiler/core/js_exp_make.mli

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ val runtime_call :
8181

8282
val str : ?comment:string -> string -> t
8383

84-
val template_literal : ?comment:string -> semantic:string -> string -> t
84+
val template_literal : ?comment:string -> Asttypes.template_segment -> t
8585

8686
val json_literal : ?comment:string -> string -> t
8787

compiler/core/lam_compile_primitive.ml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,7 @@ let translate output_prefix loc (cxt : Lam_compile_context.t)
8585
match args with
8686
| fn :: values -> E.tagged_template fn strings values
8787
| [] -> assert false)
88-
| Ptemplate [{source; semantic}] when args = [] ->
89-
E.template_literal ~semantic source
88+
| Ptemplate [segment] when args = [] -> E.template_literal segment
9089
| Ptemplate segments -> E.interpolated_template segments args
9190
| Pnull_to_opt -> (
9291
match args with

compiler/ml/ast_helper.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ module Const = struct
4747
let semantic = Char.code c in
4848
Pconst_char {source = String_literal.encode_char_source semantic; semantic}
4949
let string semantic =
50-
Pconst_string {source = String_literal.encode_js_string semantic; semantic}
50+
Pconst_string (String_literal.string_from_semantic semantic)
5151
end
5252

5353
module Typ = struct

compiler/ml/ast_mapper.ml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -655,7 +655,8 @@ module Ppx_context = struct
655655
let restore fields =
656656
let field name payload =
657657
let rec get_string = function
658-
| {pexp_desc = Pexp_constant (Pconst_string {semantic})} -> semantic
658+
| {pexp_desc = Pexp_constant (Pconst_string payload)} ->
659+
String_literal.semantic payload
659660
| _ ->
660661
raise_errorf
661662
"Internal error: invalid [@@@ocaml.ppx.context { %s }] string \

compiler/ml/ast_mapper_from0.ml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,10 +117,12 @@ let normalize_ppx_semantic_string semantic =
117117

118118
let semantic_string semantic =
119119
let semantic = normalize_ppx_semantic_string semantic in
120-
Pt.Pconst_string {source = String_literal.encode_js_string semantic; semantic}
120+
Pt.Pconst_string (String_literal.string_from_semantic semantic)
121121

122122
let source_string ~loc source =
123-
Pt.Pconst_string {source; semantic = decode_js_string ~loc source}
123+
match String_literal.string_from_source source with
124+
| Some payload -> Pt.Pconst_string payload
125+
| None -> Location.raise_errorf ~loc "Invalid string escape sequence"
124126

125127
let template_source_from0 = function
126128
| source, Some ("js" | "*j") -> source

0 commit comments

Comments
 (0)