Skip to content

Commit 4a75cd6

Browse files
author
Hongbo Zhang
committed
fix string escape problem across different OSes, in ocaml 4.02.3, the escape is locale sensitive
1 parent 1453285 commit 4a75cd6

10 files changed

+138
-7
lines changed

jscomp/compiler.mllib

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ ext_pp
1616
ext_option
1717
ext_list
1818
ext_string
19+
ext_char
1920
ext_format
2021
ext_filename
2122
ext_marshal

jscomp/ext_char.ml

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
(* OCamlScript compiler
2+
* Copyright (C) 2015-2016 Bloomberg Finance L.P.
3+
*
4+
* This program is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU Lesser General Public License as published by
6+
* the Free Software Foundation, with linking exception;
7+
* either version 2.1 of the License, or (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU Lesser General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU Lesser General Public License
15+
* along with this program; if not, write to the Free Software
16+
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17+
*)
18+
19+
(* Author: Hongbo Zhang *)
20+
21+
22+
external string_unsafe_set : string -> int -> char -> unit
23+
= "%string_unsafe_set"
24+
25+
external string_create: int -> string = "caml_create_string"
26+
27+
external unsafe_chr: int -> char = "%identity"
28+
29+
(** {!Char.escaped} is locale sensitive in 4.02.3, fixed in the trunk,
30+
backport it here
31+
*)
32+
let escaped = function
33+
| '\'' -> "\\'"
34+
| '\\' -> "\\\\"
35+
| '\n' -> "\\n"
36+
| '\t' -> "\\t"
37+
| '\r' -> "\\r"
38+
| '\b' -> "\\b"
39+
| ' ' .. '~' as c ->
40+
let s = string_create 1 in
41+
string_unsafe_set s 0 c;
42+
s
43+
| c ->
44+
let n = Char.code c in
45+
let s = string_create 4 in
46+
string_unsafe_set s 0 '\\';
47+
string_unsafe_set s 1 (unsafe_chr (48 + n / 100));
48+
string_unsafe_set s 2 (unsafe_chr (48 + (n / 10) mod 10));
49+
string_unsafe_set s 3 (unsafe_chr (48 + n mod 10));
50+
s

jscomp/ext_char.mli

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
(* OCamlScript compiler
2+
* Copyright (C) 2015-2016 Bloomberg Finance L.P.
3+
*
4+
* This program is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU Lesser General Public License as published by
6+
* the Free Software Foundation, with linking exception;
7+
* either version 2.1 of the License, or (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU Lesser General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU Lesser General Public License
15+
* along with this program; if not, write to the Free Software
16+
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17+
*)
18+
19+
(* Author: Hongbo Zhang *)
20+
21+
val escaped : char -> string

jscomp/ext_string.ml

+18
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,21 @@ let ends_with s beg =
6363
aux (j - 1) (k - 1)
6464
else false in
6565
aux s_finish s_beg
66+
67+
68+
(** In OCaml 4.02.3, {!String.escaped} is locale senstive,
69+
this version try to make it not locale senstive, this bug is fixed
70+
in the compiler trunk
71+
*)
72+
let escaped s =
73+
let rec needs_escape i =
74+
if i >= String.length s then false else
75+
match String.unsafe_get s i with
76+
| '"' | '\\' | '\n' | '\t' | '\r' | '\b' -> true
77+
| ' ' .. '~' -> needs_escape (i+1)
78+
| _ -> true
79+
in
80+
if needs_escape 0 then
81+
Bytes.unsafe_to_string (Bytes.escaped (Bytes.unsafe_of_string s))
82+
else
83+
s

jscomp/ext_string.mli

+2
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,5 @@ val split : ?keep_empty:bool -> string -> char -> string list
3131
val starts_with : string -> string -> bool
3232

3333
val ends_with : string -> string -> bool
34+
35+
val escaped : string -> string

jscomp/j_helper.ml

+1-1
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ module Exp = struct
300300
match v.expression_desc with
301301
| Str (_, x) ->
302302
assert (String.length x = 1) ;
303-
int ~comment:(Printf.sprintf "%S" x)
303+
int ~comment:("\"" ^ Ext_string.escaped x ^ "\"")
304304
(Char.code x.[0])
305305
| Char_of_int v -> v
306306
| _ -> {comment; expression_desc = Char_to_int v }

jscomp/js_of_lam_string.ml

+1-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ module E = J_helper.Exp
2626
module A = struct
2727

2828
let const_char (i : char) =
29-
(* E.int ~comment:(Char.escaped i) (Char.code i) *)
3029
E.str (String.make 1 i)
3130

3231
let caml_char_of_int ?comment (v : J.expression) =
@@ -82,7 +81,7 @@ end
8281
module B = struct
8382

8483
let const_char (i : char) =
85-
E.int ~comment:(Printf.sprintf "%S" (String.make 1 i))
84+
E.int ~comment:("\"" ^ Ext_string.escaped (String.make 1 i) ^ "\"")
8685
~c:i (Char.code i)
8786

8887
let caml_char_of_int ?comment (v : J.expression) = v

jscomp/test/.depend

+4-4
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ ext_filename.cmx : ../stdlib/sys.cmx ../stdlib/string.cmx ../stdlib/list.cmx \
6464
../stdlib/filename.cmx ext_string.cmx
6565
ext_list.cmo : ../stdlib/list.cmi ../stdlib/array.cmi
6666
ext_list.cmx : ../stdlib/list.cmx ../stdlib/array.cmx
67-
ext_string.cmo : ../stdlib/string.cmi
68-
ext_string.cmx : ../stdlib/string.cmx
67+
ext_string.cmo : ../stdlib/string.cmi ../stdlib/bytes.cmi
68+
ext_string.cmx : ../stdlib/string.cmx ../stdlib/bytes.cmx
6969
fib.cmo :
7070
fib.cmx :
7171
for_loop_test.cmo : mt.cmo ../stdlib/list.cmi ../stdlib/array.cmi
@@ -390,8 +390,8 @@ ext_filename.cmo : ../stdlib/sys.cmo ../stdlib/string.cmo ../stdlib/list.cmo \
390390
../stdlib/filename.cmo ext_string.cmo
391391
ext_list.cmo : ../stdlib/list.cmi ../stdlib/array.cmi
392392
ext_list.cmo : ../stdlib/list.cmo ../stdlib/array.cmo
393-
ext_string.cmo : ../stdlib/string.cmi
394-
ext_string.cmo : ../stdlib/string.cmo
393+
ext_string.cmo : ../stdlib/string.cmi ../stdlib/bytes.cmi
394+
ext_string.cmo : ../stdlib/string.cmo ../stdlib/bytes.cmo
395395
fib.cmo :
396396
fib.cmo :
397397
for_loop_test.cmo : mt.cmo ../stdlib/list.cmi ../stdlib/array.cmi

jscomp/test/ext_string.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ export var split_by: ($staropt$star : any, is_delim : any, str : any) => any
33
export var split: (keep_empty : any, str : any, on : any) => any ;
44
export var starts_with: (s : any, beg : any) => any ;
55
export var ends_with: (s : any, beg : any) => any ;
6+
export var escaped: (s : any) => any ;
67

jscomp/test/ext_string.js

+39
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Generated CODE, PLEASE EDIT WITH CARE
22
"use strict";
3+
var Bytes = require("../stdlib/bytes");
34
var $$String = require("../stdlib/string");
45

56
function split_by($staropt$star, is_delim, str) {
@@ -89,8 +90,46 @@ function ends_with(s, beg) {
8990
}
9091
}
9192

93+
function escaped(s) {
94+
var needs_escape = function (_i) {
95+
while(/* true */1) {
96+
var i = _i;
97+
if (i >= s.length) {
98+
return /* false */0;
99+
}
100+
else {
101+
var match = s.charCodeAt(i);
102+
if (match >= 32) {
103+
var switcher = -34 + match;
104+
if (!(58 < (switcher >>> 0))) {
105+
if (56 < (-1 + switcher >>> 0)) {
106+
return /* true */1;
107+
}
108+
else {
109+
_i = i + 1;
110+
}
111+
}
112+
else {
113+
if (switcher >= 93) {
114+
return /* true */1;
115+
}
116+
else {
117+
_i = i + 1;
118+
}
119+
}
120+
}
121+
else {
122+
return match >= 11 ? (match !== 13, /* true */1) : (match >= 8, /* true */1);
123+
}
124+
}
125+
};
126+
};
127+
return needs_escape(0) ? Bytes.unsafe_to_string(Bytes.escaped(Bytes.unsafe_of_string(s))) : s;
128+
}
129+
92130
exports.split_by = split_by;
93131
exports.split = split;
94132
exports.starts_with = starts_with;
95133
exports.ends_with = ends_with;
134+
exports.escaped = escaped;
96135
/* No side effect */

0 commit comments

Comments
 (0)