1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "util/Text.h"
8
9 #include "mozilla/PodOperations.h"
10
11 #include "gc/GC.h"
12 #include "js/GCAPI.h"
13 #include "vm/JSContext.h"
14 #include "vm/StringType.h"
15
16 using namespace JS;
17 using namespace js;
18 using js::gc::AutoSuppressGC;
19 using mozilla::PodCopy;
20
21 template <typename CharT>
js_strchr_limit(const CharT * s,char16_t c,const CharT * limit)22 const CharT* js_strchr_limit(const CharT* s, char16_t c, const CharT* limit) {
23 while (s < limit) {
24 if (*s == c) return s;
25 s++;
26 }
27 return nullptr;
28 }
29
30 template const Latin1Char* js_strchr_limit(const Latin1Char* s, char16_t c,
31 const Latin1Char* limit);
32
33 template const char16_t* js_strchr_limit(const char16_t* s, char16_t c,
34 const char16_t* limit);
35
js_strdup(const char * s)36 JS_PUBLIC_API char* js_strdup(const char* s) {
37 return DuplicateString(s).release();
38 }
39
js_fputs(const char16_t * s,FILE * f)40 int32_t js_fputs(const char16_t* s, FILE* f) {
41 while (*s != 0) {
42 if (fputwc(wchar_t(*s), f) == WEOF) return WEOF;
43 s++;
44 }
45 return 1;
46 }
47
DuplicateString(JSContext * cx,const char * s)48 UniqueChars js::DuplicateString(JSContext* cx, const char* s) {
49 size_t n = strlen(s) + 1;
50 auto ret = cx->make_pod_array<char>(n);
51 if (!ret) return ret;
52 PodCopy(ret.get(), s, n);
53 return ret;
54 }
55
DuplicateString(JSContext * cx,const char16_t * s)56 UniqueTwoByteChars js::DuplicateString(JSContext* cx, const char16_t* s) {
57 size_t n = js_strlen(s) + 1;
58 auto ret = cx->make_pod_array<char16_t>(n);
59 if (!ret) return ret;
60 PodCopy(ret.get(), s, n);
61 return ret;
62 }
63
DuplicateString(const char * s)64 UniqueChars js::DuplicateString(const char* s) {
65 size_t n = strlen(s) + 1;
66 UniqueChars ret(js_pod_malloc<char>(n));
67 if (!ret) return ret;
68 PodCopy(ret.get(), s, n);
69 return ret;
70 }
71
DuplicateString(const char * s,size_t n)72 UniqueChars js::DuplicateString(const char* s, size_t n) {
73 UniqueChars ret(js_pod_malloc<char>(n + 1));
74 if (!ret) return nullptr;
75 PodCopy(ret.get(), s, n);
76 ret[n] = 0;
77 return ret;
78 }
79
DuplicateString(const char16_t * s)80 UniqueTwoByteChars js::DuplicateString(const char16_t* s) {
81 return DuplicateString(s, js_strlen(s));
82 }
83
DuplicateString(const char16_t * s,size_t n)84 UniqueTwoByteChars js::DuplicateString(const char16_t* s, size_t n) {
85 UniqueTwoByteChars ret(js_pod_malloc<char16_t>(n + 1));
86 if (!ret) return nullptr;
87 PodCopy(ret.get(), s, n);
88 ret[n] = 0;
89 return ret;
90 }
91
InflateString(JSContext * cx,const char * bytes,size_t length)92 char16_t* js::InflateString(JSContext* cx, const char* bytes, size_t length) {
93 char16_t* chars = cx->pod_malloc<char16_t>(length + 1);
94 if (!chars) return nullptr;
95 CopyAndInflateChars(chars, bytes, length);
96 chars[length] = 0;
97 return chars;
98 }
99
100 template <typename CharT>
DeflateStringToBuffer(JSContext * maybecx,const CharT * src,size_t srclen,char * dst,size_t * dstlenp)101 bool js::DeflateStringToBuffer(JSContext* maybecx, const CharT* src,
102 size_t srclen, char* dst, size_t* dstlenp) {
103 size_t dstlen = *dstlenp;
104 if (srclen > dstlen) {
105 for (size_t i = 0; i < dstlen; i++) dst[i] = char(src[i]);
106 if (maybecx) {
107 AutoSuppressGC suppress(maybecx);
108 JS_ReportErrorNumberASCII(maybecx, GetErrorMessage, nullptr,
109 JSMSG_BUFFER_TOO_SMALL);
110 }
111 return false;
112 }
113 for (size_t i = 0; i < srclen; i++) dst[i] = char(src[i]);
114 *dstlenp = srclen;
115 return true;
116 }
117
118 template bool js::DeflateStringToBuffer(JSContext* maybecx,
119 const Latin1Char* src, size_t srclen,
120 char* dst, size_t* dstlenp);
121
122 template bool js::DeflateStringToBuffer(JSContext* maybecx, const char16_t* src,
123 size_t srclen, char* dst,
124 size_t* dstlenp);
125
126 /*
127 * Convert one UCS-4 char and write it into a UTF-8 buffer, which must be at
128 * least 4 bytes long. Return the number of UTF-8 bytes of data written.
129 */
OneUcs4ToUtf8Char(uint8_t * utf8Buffer,uint32_t ucs4Char)130 uint32_t js::OneUcs4ToUtf8Char(uint8_t* utf8Buffer, uint32_t ucs4Char) {
131 MOZ_ASSERT(ucs4Char <= unicode::NonBMPMax);
132
133 if (ucs4Char < 0x80) {
134 utf8Buffer[0] = uint8_t(ucs4Char);
135 return 1;
136 }
137
138 uint32_t a = ucs4Char >> 11;
139 uint32_t utf8Length = 2;
140 while (a) {
141 a >>= 5;
142 utf8Length++;
143 }
144
145 MOZ_ASSERT(utf8Length <= 4);
146
147 uint32_t i = utf8Length;
148 while (--i) {
149 utf8Buffer[i] = uint8_t((ucs4Char & 0x3F) | 0x80);
150 ucs4Char >>= 6;
151 }
152
153 utf8Buffer[0] = uint8_t(0x100 - (1 << (8 - utf8Length)) + ucs4Char);
154 return utf8Length;
155 }
156
PutEscapedStringImpl(char * buffer,size_t bufferSize,GenericPrinter * out,JSLinearString * str,uint32_t quote)157 size_t js::PutEscapedStringImpl(char* buffer, size_t bufferSize,
158 GenericPrinter* out, JSLinearString* str,
159 uint32_t quote) {
160 size_t len = str->length();
161 AutoCheckCannotGC nogc;
162 return str->hasLatin1Chars()
163 ? PutEscapedStringImpl(buffer, bufferSize, out,
164 str->latin1Chars(nogc), len, quote)
165 : PutEscapedStringImpl(buffer, bufferSize, out,
166 str->twoByteChars(nogc), len, quote);
167 }
168
169 template <typename CharT>
PutEscapedStringImpl(char * buffer,size_t bufferSize,GenericPrinter * out,const CharT * chars,size_t length,uint32_t quote)170 size_t js::PutEscapedStringImpl(char* buffer, size_t bufferSize,
171 GenericPrinter* out, const CharT* chars,
172 size_t length, uint32_t quote) {
173 enum {
174 STOP,
175 FIRST_QUOTE,
176 LAST_QUOTE,
177 CHARS,
178 ESCAPE_START,
179 ESCAPE_MORE
180 } state;
181
182 MOZ_ASSERT(quote == 0 || quote == '\'' || quote == '"');
183 MOZ_ASSERT_IF(!buffer, bufferSize == 0);
184 MOZ_ASSERT_IF(out, !buffer);
185
186 if (bufferSize == 0)
187 buffer = nullptr;
188 else
189 bufferSize--;
190
191 const CharT* charsEnd = chars + length;
192 size_t n = 0;
193 state = FIRST_QUOTE;
194 unsigned shift = 0;
195 unsigned hex = 0;
196 unsigned u = 0;
197 char c = 0; /* to quell GCC warnings */
198
199 for (;;) {
200 switch (state) {
201 case STOP:
202 goto stop;
203 case FIRST_QUOTE:
204 state = CHARS;
205 goto do_quote;
206 case LAST_QUOTE:
207 state = STOP;
208 do_quote:
209 if (quote == 0) continue;
210 c = (char)quote;
211 break;
212 case CHARS:
213 if (chars == charsEnd) {
214 state = LAST_QUOTE;
215 continue;
216 }
217 u = *chars++;
218 if (u < ' ') {
219 if (u != 0) {
220 const char* escape = strchr(js_EscapeMap, (int)u);
221 if (escape) {
222 u = escape[1];
223 goto do_escape;
224 }
225 }
226 goto do_hex_escape;
227 }
228 if (u < 127) {
229 if (u == quote || u == '\\') goto do_escape;
230 c = (char)u;
231 } else if (u < 0x100) {
232 goto do_hex_escape;
233 } else {
234 shift = 16;
235 hex = u;
236 u = 'u';
237 goto do_escape;
238 }
239 break;
240 do_hex_escape:
241 shift = 8;
242 hex = u;
243 u = 'x';
244 do_escape:
245 c = '\\';
246 state = ESCAPE_START;
247 break;
248 case ESCAPE_START:
249 MOZ_ASSERT(' ' <= u && u < 127);
250 c = (char)u;
251 state = ESCAPE_MORE;
252 break;
253 case ESCAPE_MORE:
254 if (shift == 0) {
255 state = CHARS;
256 continue;
257 }
258 shift -= 4;
259 u = 0xF & (hex >> shift);
260 c = (char)(u + (u < 10 ? '0' : 'A' - 10));
261 break;
262 }
263 if (buffer) {
264 MOZ_ASSERT(n <= bufferSize);
265 if (n != bufferSize) {
266 buffer[n] = c;
267 } else {
268 buffer[n] = '\0';
269 buffer = nullptr;
270 }
271 } else if (out) {
272 if (!out->put(&c, 1)) return size_t(-1);
273 }
274 n++;
275 }
276 stop:
277 if (buffer) buffer[n] = '\0';
278 return n;
279 }
280
281 template size_t js::PutEscapedStringImpl(char* buffer, size_t bufferSize,
282 GenericPrinter* out,
283 const Latin1Char* chars, size_t length,
284 uint32_t quote);
285
286 template size_t js::PutEscapedStringImpl(char* buffer, size_t bufferSize,
287 GenericPrinter* out, const char* chars,
288 size_t length, uint32_t quote);
289
290 template size_t js::PutEscapedStringImpl(char* buffer, size_t bufferSize,
291 GenericPrinter* out,
292 const char16_t* chars, size_t length,
293 uint32_t quote);
294
295 template size_t js::PutEscapedString(char* buffer, size_t bufferSize,
296 const Latin1Char* chars, size_t length,
297 uint32_t quote);
298
299 template size_t js::PutEscapedString(char* buffer, size_t bufferSize,
300 const char16_t* chars, size_t length,
301 uint32_t quote);
302