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