]>
Commit | Line | Data |
---|---|---|
9dae56ea A |
1 | /* |
2 | * Copyright (C) 1999-2002 Harri Porten (porten@kde.org) | |
3 | * Copyright (C) 2001 Peter Kelly (pmk@post.com) | |
4 | * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. | |
5 | * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) | |
6 | * Copyright (C) 2007 Maks Orlovich | |
7 | * | |
8 | * This library is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU Library General Public | |
10 | * License as published by the Free Software Foundation; either | |
11 | * version 2 of the License, or (at your option) any later version. | |
12 | * | |
13 | * This library is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 | * Library General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU Library General Public License | |
19 | * along with this library; see the file COPYING.LIB. If not, write to | |
20 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | |
21 | * Boston, MA 02110-1301, USA. | |
22 | * | |
23 | */ | |
24 | ||
25 | #include "config.h" | |
26 | #include "JSGlobalObjectFunctions.h" | |
27 | ||
28 | #include "CallFrame.h" | |
f9bf01c6 | 29 | #include "Interpreter.h" |
9dae56ea A |
30 | #include "JSGlobalObject.h" |
31 | #include "JSString.h" | |
4e4e5a6f | 32 | #include "JSStringBuilder.h" |
9dae56ea | 33 | #include "Lexer.h" |
f9bf01c6 | 34 | #include "LiteralParser.h" |
9dae56ea | 35 | #include "Nodes.h" |
f9bf01c6 | 36 | #include "Parser.h" |
14957cd0 | 37 | #include "UStringBuilder.h" |
f9bf01c6 | 38 | #include "dtoa.h" |
9dae56ea A |
39 | #include <stdio.h> |
40 | #include <stdlib.h> | |
9dae56ea A |
41 | #include <wtf/ASCIICType.h> |
42 | #include <wtf/Assertions.h> | |
43 | #include <wtf/MathExtras.h> | |
14957cd0 | 44 | #include <wtf/StringExtras.h> |
9dae56ea A |
45 | #include <wtf/unicode/UTF8.h> |
46 | ||
47 | using namespace WTF; | |
48 | using namespace Unicode; | |
49 | ||
50 | namespace JSC { | |
51 | ||
14957cd0 | 52 | static JSValue encode(ExecState* exec, const char* doNotEscape) |
9dae56ea | 53 | { |
14957cd0 A |
54 | UString str = exec->argument(0).toString(exec); |
55 | CString cstr = str.utf8(true); | |
4e4e5a6f | 56 | if (!cstr.data()) |
14957cd0 | 57 | return throwError(exec, createURIError(exec, "String contained an illegal UTF-16 sequence.")); |
9dae56ea | 58 | |
4e4e5a6f A |
59 | JSStringBuilder builder; |
60 | const char* p = cstr.data(); | |
61 | for (size_t k = 0; k < cstr.length(); k++, p++) { | |
9dae56ea A |
62 | char c = *p; |
63 | if (c && strchr(doNotEscape, c)) | |
f9bf01c6 | 64 | builder.append(c); |
9dae56ea A |
65 | else { |
66 | char tmp[4]; | |
67 | snprintf(tmp, sizeof(tmp), "%%%02X", static_cast<unsigned char>(c)); | |
4e4e5a6f | 68 | builder.append(tmp); |
9dae56ea A |
69 | } |
70 | } | |
4e4e5a6f | 71 | return builder.build(exec); |
9dae56ea A |
72 | } |
73 | ||
14957cd0 | 74 | static JSValue decode(ExecState* exec, const char* doNotUnescape, bool strict) |
9dae56ea | 75 | { |
4e4e5a6f | 76 | JSStringBuilder builder; |
14957cd0 | 77 | UString str = exec->argument(0).toString(exec); |
9dae56ea | 78 | int k = 0; |
14957cd0 A |
79 | int len = str.length(); |
80 | const UChar* d = str.characters(); | |
9dae56ea A |
81 | UChar u = 0; |
82 | while (k < len) { | |
83 | const UChar* p = d + k; | |
84 | UChar c = *p; | |
85 | if (c == '%') { | |
86 | int charLen = 0; | |
87 | if (k <= len - 3 && isASCIIHexDigit(p[1]) && isASCIIHexDigit(p[2])) { | |
88 | const char b0 = Lexer::convertHex(p[1], p[2]); | |
89 | const int sequenceLen = UTF8SequenceLength(b0); | |
90 | if (sequenceLen != 0 && k <= len - sequenceLen * 3) { | |
91 | charLen = sequenceLen * 3; | |
92 | char sequence[5]; | |
93 | sequence[0] = b0; | |
94 | for (int i = 1; i < sequenceLen; ++i) { | |
95 | const UChar* q = p + i * 3; | |
96 | if (q[0] == '%' && isASCIIHexDigit(q[1]) && isASCIIHexDigit(q[2])) | |
97 | sequence[i] = Lexer::convertHex(q[1], q[2]); | |
98 | else { | |
99 | charLen = 0; | |
100 | break; | |
101 | } | |
102 | } | |
103 | if (charLen != 0) { | |
104 | sequence[sequenceLen] = 0; | |
105 | const int character = decodeUTF8Sequence(sequence); | |
106 | if (character < 0 || character >= 0x110000) | |
107 | charLen = 0; | |
108 | else if (character >= 0x10000) { | |
109 | // Convert to surrogate pair. | |
f9bf01c6 | 110 | builder.append(static_cast<UChar>(0xD800 | ((character - 0x10000) >> 10))); |
9dae56ea A |
111 | u = static_cast<UChar>(0xDC00 | ((character - 0x10000) & 0x3FF)); |
112 | } else | |
113 | u = static_cast<UChar>(character); | |
114 | } | |
115 | } | |
116 | } | |
117 | if (charLen == 0) { | |
118 | if (strict) | |
14957cd0 | 119 | return throwError(exec, createURIError(exec, "URI error")); |
9dae56ea A |
120 | // The only case where we don't use "strict" mode is the "unescape" function. |
121 | // For that, it's good to support the wonky "%u" syntax for compatibility with WinIE. | |
122 | if (k <= len - 6 && p[1] == 'u' | |
123 | && isASCIIHexDigit(p[2]) && isASCIIHexDigit(p[3]) | |
124 | && isASCIIHexDigit(p[4]) && isASCIIHexDigit(p[5])) { | |
125 | charLen = 6; | |
126 | u = Lexer::convertUnicode(p[2], p[3], p[4], p[5]); | |
127 | } | |
128 | } | |
129 | if (charLen && (u == 0 || u >= 128 || !strchr(doNotUnescape, u))) { | |
130 | c = u; | |
131 | k += charLen - 1; | |
132 | } | |
133 | } | |
134 | k++; | |
f9bf01c6 | 135 | builder.append(c); |
9dae56ea | 136 | } |
4e4e5a6f | 137 | return builder.build(exec); |
9dae56ea A |
138 | } |
139 | ||
140 | bool isStrWhiteSpace(UChar c) | |
141 | { | |
142 | switch (c) { | |
14957cd0 | 143 | // ECMA-262-5th 7.2 & 7.3 |
9dae56ea A |
144 | case 0x0009: |
145 | case 0x000A: | |
146 | case 0x000B: | |
147 | case 0x000C: | |
148 | case 0x000D: | |
149 | case 0x0020: | |
150 | case 0x00A0: | |
151 | case 0x2028: | |
152 | case 0x2029: | |
14957cd0 | 153 | case 0xFEFF: |
9dae56ea A |
154 | return true; |
155 | default: | |
156 | return c > 0xff && isSeparatorSpace(c); | |
157 | } | |
158 | } | |
159 | ||
160 | static int parseDigit(unsigned short c, int radix) | |
161 | { | |
162 | int digit = -1; | |
163 | ||
164 | if (c >= '0' && c <= '9') | |
165 | digit = c - '0'; | |
166 | else if (c >= 'A' && c <= 'Z') | |
167 | digit = c - 'A' + 10; | |
168 | else if (c >= 'a' && c <= 'z') | |
169 | digit = c - 'a' + 10; | |
170 | ||
171 | if (digit >= radix) | |
172 | return -1; | |
173 | return digit; | |
174 | } | |
175 | ||
176 | double parseIntOverflow(const char* s, int length, int radix) | |
177 | { | |
178 | double number = 0.0; | |
179 | double radixMultiplier = 1.0; | |
180 | ||
181 | for (const char* p = s + length - 1; p >= s; p--) { | |
182 | if (radixMultiplier == Inf) { | |
183 | if (*p != '0') { | |
184 | number = Inf; | |
185 | break; | |
186 | } | |
187 | } else { | |
188 | int digit = parseDigit(*p, radix); | |
189 | number += digit * radixMultiplier; | |
190 | } | |
191 | ||
192 | radixMultiplier *= radix; | |
193 | } | |
194 | ||
195 | return number; | |
196 | } | |
197 | ||
14957cd0 A |
198 | double parseIntOverflow(const UChar* s, int length, int radix) |
199 | { | |
200 | double number = 0.0; | |
201 | double radixMultiplier = 1.0; | |
202 | ||
203 | for (const UChar* p = s + length - 1; p >= s; p--) { | |
204 | if (radixMultiplier == Inf) { | |
205 | if (*p != '0') { | |
206 | number = Inf; | |
207 | break; | |
208 | } | |
209 | } else { | |
210 | int digit = parseDigit(*p, radix); | |
211 | number += digit * radixMultiplier; | |
212 | } | |
213 | ||
214 | radixMultiplier *= radix; | |
215 | } | |
216 | ||
217 | return number; | |
218 | } | |
219 | ||
9dae56ea A |
220 | static double parseInt(const UString& s, int radix) |
221 | { | |
14957cd0 A |
222 | int length = s.length(); |
223 | const UChar* data = s.characters(); | |
9dae56ea A |
224 | int p = 0; |
225 | ||
226 | while (p < length && isStrWhiteSpace(data[p])) | |
227 | ++p; | |
228 | ||
229 | double sign = 1; | |
230 | if (p < length) { | |
231 | if (data[p] == '+') | |
232 | ++p; | |
233 | else if (data[p] == '-') { | |
234 | sign = -1; | |
235 | ++p; | |
236 | } | |
237 | } | |
238 | ||
239 | if ((radix == 0 || radix == 16) && length - p >= 2 && data[p] == '0' && (data[p + 1] == 'x' || data[p + 1] == 'X')) { | |
240 | radix = 16; | |
241 | p += 2; | |
242 | } else if (radix == 0) { | |
243 | if (p < length && data[p] == '0') | |
244 | radix = 8; | |
245 | else | |
246 | radix = 10; | |
247 | } | |
248 | ||
249 | if (radix < 2 || radix > 36) | |
250 | return NaN; | |
251 | ||
252 | int firstDigitPosition = p; | |
253 | bool sawDigit = false; | |
254 | double number = 0; | |
255 | while (p < length) { | |
256 | int digit = parseDigit(data[p], radix); | |
257 | if (digit == -1) | |
258 | break; | |
259 | sawDigit = true; | |
260 | number *= radix; | |
261 | number += digit; | |
262 | ++p; | |
263 | } | |
264 | ||
265 | if (number >= mantissaOverflowLowerBound) { | |
266 | if (radix == 10) | |
14957cd0 | 267 | number = WTF::strtod(s.substringSharingImpl(firstDigitPosition, p - firstDigitPosition).utf8().data(), 0); |
9dae56ea | 268 | else if (radix == 2 || radix == 4 || radix == 8 || radix == 16 || radix == 32) |
14957cd0 | 269 | number = parseIntOverflow(s.substringSharingImpl(firstDigitPosition, p - firstDigitPosition).utf8().data(), p - firstDigitPosition, radix); |
9dae56ea A |
270 | } |
271 | ||
272 | if (!sawDigit) | |
273 | return NaN; | |
274 | ||
275 | return sign * number; | |
276 | } | |
277 | ||
14957cd0 A |
278 | static const int SizeOfInfinity = 8; |
279 | ||
280 | static bool isInfinity(const UChar* data, const UChar* end) | |
281 | { | |
282 | return (end - data) >= SizeOfInfinity | |
283 | && data[0] == 'I' | |
284 | && data[1] == 'n' | |
285 | && data[2] == 'f' | |
286 | && data[3] == 'i' | |
287 | && data[4] == 'n' | |
288 | && data[5] == 'i' | |
289 | && data[6] == 't' | |
290 | && data[7] == 'y'; | |
291 | } | |
292 | ||
293 | // See ecma-262 9.3.1 | |
294 | static double jsHexIntegerLiteral(const UChar*& data, const UChar* end) | |
295 | { | |
296 | // Hex number. | |
297 | data += 2; | |
298 | const UChar* firstDigitPosition = data; | |
299 | double number = 0; | |
300 | while (true) { | |
301 | number = number * 16 + toASCIIHexValue(*data); | |
302 | ++data; | |
303 | if (data == end) | |
304 | break; | |
305 | if (!isASCIIHexDigit(*data)) | |
306 | break; | |
307 | } | |
308 | if (number >= mantissaOverflowLowerBound) | |
309 | number = parseIntOverflow(firstDigitPosition, data - firstDigitPosition, 16); | |
310 | ||
311 | return number; | |
312 | } | |
313 | ||
314 | // See ecma-262 9.3.1 | |
315 | static double jsStrDecimalLiteral(const UChar*& data, const UChar* end) | |
316 | { | |
317 | ASSERT(data < end); | |
318 | ||
319 | // Copy the sting into a null-terminated byte buffer, and call strtod. | |
320 | Vector<char, 32> byteBuffer; | |
321 | for (const UChar* characters = data; characters < end; ++characters) { | |
322 | UChar character = *characters; | |
323 | byteBuffer.append(isASCII(character) ? character : 0); | |
324 | } | |
325 | byteBuffer.append(0); | |
326 | char* endOfNumber; | |
327 | double number = WTF::strtod(byteBuffer.data(), &endOfNumber); | |
328 | ||
329 | // Check if strtod found a number; if so return it. | |
330 | ptrdiff_t consumed = endOfNumber - byteBuffer.data(); | |
331 | if (consumed) { | |
332 | data += consumed; | |
333 | return number; | |
334 | } | |
335 | ||
336 | // Check for [+-]?Infinity | |
337 | switch (*data) { | |
338 | case 'I': | |
339 | if (isInfinity(data, end)) { | |
340 | data += SizeOfInfinity; | |
341 | return Inf; | |
342 | } | |
343 | break; | |
344 | ||
345 | case '+': | |
346 | if (isInfinity(data + 1, end)) { | |
347 | data += SizeOfInfinity + 1; | |
348 | return Inf; | |
349 | } | |
350 | break; | |
351 | ||
352 | case '-': | |
353 | if (isInfinity(data + 1, end)) { | |
354 | data += SizeOfInfinity + 1; | |
355 | return -Inf; | |
356 | } | |
357 | break; | |
358 | } | |
359 | ||
360 | // Not a number. | |
361 | return NaN; | |
362 | } | |
363 | ||
364 | // See ecma-262 9.3.1 | |
365 | double jsToNumber(const UString& s) | |
366 | { | |
367 | unsigned size = s.length(); | |
368 | ||
369 | if (size == 1) { | |
370 | UChar c = s.characters()[0]; | |
371 | if (isASCIIDigit(c)) | |
372 | return c - '0'; | |
373 | if (isStrWhiteSpace(c)) | |
374 | return 0; | |
375 | return NaN; | |
376 | } | |
377 | ||
378 | const UChar* data = s.characters(); | |
379 | const UChar* end = data + size; | |
380 | ||
381 | // Skip leading white space. | |
382 | for (; data < end; ++data) { | |
383 | if (!isStrWhiteSpace(*data)) | |
384 | break; | |
385 | } | |
386 | ||
387 | // Empty string. | |
388 | if (data == end) | |
389 | return 0.0; | |
390 | ||
391 | double number; | |
392 | if (data[0] == '0' && data + 2 < end && (data[1] | 0x20) == 'x' && isASCIIHexDigit(data[2])) | |
393 | number = jsHexIntegerLiteral(data, end); | |
394 | else | |
395 | number = jsStrDecimalLiteral(data, end); | |
396 | ||
397 | // Allow trailing white space. | |
398 | for (; data < end; ++data) { | |
399 | if (!isStrWhiteSpace(*data)) | |
400 | break; | |
401 | } | |
402 | if (data != end) | |
403 | return NaN; | |
404 | ||
405 | return number; | |
406 | } | |
407 | ||
9dae56ea A |
408 | static double parseFloat(const UString& s) |
409 | { | |
14957cd0 | 410 | unsigned size = s.length(); |
9dae56ea | 411 | |
14957cd0 A |
412 | if (size == 1) { |
413 | UChar c = s.characters()[0]; | |
414 | if (isASCIIDigit(c)) | |
415 | return c - '0'; | |
416 | return NaN; | |
417 | } | |
9dae56ea | 418 | |
14957cd0 A |
419 | const UChar* data = s.characters(); |
420 | const UChar* end = data + size; | |
9dae56ea | 421 | |
14957cd0 A |
422 | // Skip leading white space. |
423 | for (; data < end; ++data) { | |
424 | if (!isStrWhiteSpace(*data)) | |
425 | break; | |
426 | } | |
427 | ||
428 | // Empty string. | |
429 | if (data == end) | |
430 | return NaN; | |
431 | ||
432 | return jsStrDecimalLiteral(data, end); | |
9dae56ea A |
433 | } |
434 | ||
14957cd0 | 435 | EncodedJSValue JSC_HOST_CALL globalFuncEval(ExecState* exec) |
9dae56ea | 436 | { |
14957cd0 | 437 | JSObject* thisObject = exec->hostThisValue().toThisObject(exec); |
9dae56ea | 438 | JSObject* unwrappedObject = thisObject->unwrappedObject(); |
14957cd0 A |
439 | if (!unwrappedObject->isGlobalObject() || static_cast<JSGlobalObject*>(unwrappedObject)->evalFunction() != exec->callee()) |
440 | return throwVMError(exec, createEvalError(exec, "The \"this\" value passed to eval must be the global object from which eval originated")); | |
9dae56ea | 441 | |
14957cd0 | 442 | JSValue x = exec->argument(0); |
9dae56ea | 443 | if (!x.isString()) |
14957cd0 | 444 | return JSValue::encode(x); |
9dae56ea A |
445 | |
446 | UString s = x.toString(exec); | |
447 | ||
14957cd0 | 448 | LiteralParser preparser(exec, s.characters(), s.length(), LiteralParser::NonStrictJSON); |
ba379fdc | 449 | if (JSValue parsedObject = preparser.tryLiteralParse()) |
14957cd0 | 450 | return JSValue::encode(parsedObject); |
ba379fdc | 451 | |
14957cd0 A |
452 | EvalExecutable* eval = EvalExecutable::create(exec, makeSource(s), false); |
453 | JSObject* error = eval->compile(exec, static_cast<JSGlobalObject*>(unwrappedObject)->globalScopeChain()); | |
f9bf01c6 | 454 | if (error) |
14957cd0 | 455 | return throwVMError(exec, error); |
9dae56ea | 456 | |
14957cd0 | 457 | return JSValue::encode(exec->interpreter()->execute(eval, exec, thisObject, static_cast<JSGlobalObject*>(unwrappedObject)->globalScopeChain())); |
9dae56ea A |
458 | } |
459 | ||
14957cd0 | 460 | EncodedJSValue JSC_HOST_CALL globalFuncParseInt(ExecState* exec) |
9dae56ea | 461 | { |
14957cd0 A |
462 | JSValue value = exec->argument(0); |
463 | int32_t radix = exec->argument(1).toInt32(exec); | |
ba379fdc A |
464 | |
465 | if (radix != 0 && radix != 10) | |
14957cd0 | 466 | return JSValue::encode(jsNumber(parseInt(value.toString(exec), radix))); |
ba379fdc A |
467 | |
468 | if (value.isInt32()) | |
14957cd0 | 469 | return JSValue::encode(value); |
9dae56ea | 470 | |
ba379fdc A |
471 | if (value.isDouble()) { |
472 | double d = value.asDouble(); | |
9dae56ea | 473 | if (isfinite(d)) |
14957cd0 | 474 | return JSValue::encode(jsNumber((d > 0) ? floor(d) : ceil(d))); |
9dae56ea | 475 | if (isnan(d) || isinf(d)) |
14957cd0 A |
476 | return JSValue::encode(jsNaN()); |
477 | return JSValue::encode(jsNumber(0)); | |
9dae56ea A |
478 | } |
479 | ||
14957cd0 | 480 | return JSValue::encode(jsNumber(parseInt(value.toString(exec), radix))); |
9dae56ea A |
481 | } |
482 | ||
14957cd0 | 483 | EncodedJSValue JSC_HOST_CALL globalFuncParseFloat(ExecState* exec) |
9dae56ea | 484 | { |
14957cd0 | 485 | return JSValue::encode(jsNumber(parseFloat(exec->argument(0).toString(exec)))); |
9dae56ea A |
486 | } |
487 | ||
14957cd0 | 488 | EncodedJSValue JSC_HOST_CALL globalFuncIsNaN(ExecState* exec) |
9dae56ea | 489 | { |
14957cd0 | 490 | return JSValue::encode(jsBoolean(isnan(exec->argument(0).toNumber(exec)))); |
9dae56ea A |
491 | } |
492 | ||
14957cd0 | 493 | EncodedJSValue JSC_HOST_CALL globalFuncIsFinite(ExecState* exec) |
9dae56ea | 494 | { |
14957cd0 A |
495 | double n = exec->argument(0).toNumber(exec); |
496 | return JSValue::encode(jsBoolean(!isnan(n) && !isinf(n))); | |
9dae56ea A |
497 | } |
498 | ||
14957cd0 | 499 | EncodedJSValue JSC_HOST_CALL globalFuncDecodeURI(ExecState* exec) |
9dae56ea A |
500 | { |
501 | static const char do_not_unescape_when_decoding_URI[] = | |
502 | "#$&+,/:;=?@"; | |
503 | ||
14957cd0 | 504 | return JSValue::encode(decode(exec, do_not_unescape_when_decoding_URI, true)); |
9dae56ea A |
505 | } |
506 | ||
14957cd0 | 507 | EncodedJSValue JSC_HOST_CALL globalFuncDecodeURIComponent(ExecState* exec) |
9dae56ea | 508 | { |
14957cd0 | 509 | return JSValue::encode(decode(exec, "", true)); |
9dae56ea A |
510 | } |
511 | ||
14957cd0 | 512 | EncodedJSValue JSC_HOST_CALL globalFuncEncodeURI(ExecState* exec) |
9dae56ea A |
513 | { |
514 | static const char do_not_escape_when_encoding_URI[] = | |
515 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | |
516 | "abcdefghijklmnopqrstuvwxyz" | |
517 | "0123456789" | |
518 | "!#$&'()*+,-./:;=?@_~"; | |
519 | ||
14957cd0 | 520 | return JSValue::encode(encode(exec, do_not_escape_when_encoding_URI)); |
9dae56ea A |
521 | } |
522 | ||
14957cd0 | 523 | EncodedJSValue JSC_HOST_CALL globalFuncEncodeURIComponent(ExecState* exec) |
9dae56ea A |
524 | { |
525 | static const char do_not_escape_when_encoding_URI_component[] = | |
526 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | |
527 | "abcdefghijklmnopqrstuvwxyz" | |
528 | "0123456789" | |
529 | "!'()*-._~"; | |
530 | ||
14957cd0 | 531 | return JSValue::encode(encode(exec, do_not_escape_when_encoding_URI_component)); |
9dae56ea A |
532 | } |
533 | ||
14957cd0 | 534 | EncodedJSValue JSC_HOST_CALL globalFuncEscape(ExecState* exec) |
9dae56ea A |
535 | { |
536 | static const char do_not_escape[] = | |
537 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | |
538 | "abcdefghijklmnopqrstuvwxyz" | |
539 | "0123456789" | |
540 | "*+-./@_"; | |
541 | ||
4e4e5a6f | 542 | JSStringBuilder builder; |
14957cd0 A |
543 | UString str = exec->argument(0).toString(exec); |
544 | const UChar* c = str.characters(); | |
545 | for (unsigned k = 0; k < str.length(); k++, c++) { | |
9dae56ea A |
546 | int u = c[0]; |
547 | if (u > 255) { | |
548 | char tmp[7]; | |
549 | snprintf(tmp, sizeof(tmp), "%%u%04X", u); | |
4e4e5a6f | 550 | builder.append(tmp); |
9dae56ea | 551 | } else if (u != 0 && strchr(do_not_escape, static_cast<char>(u))) |
4e4e5a6f | 552 | builder.append(c, 1); |
9dae56ea A |
553 | else { |
554 | char tmp[4]; | |
555 | snprintf(tmp, sizeof(tmp), "%%%02X", u); | |
4e4e5a6f | 556 | builder.append(tmp); |
9dae56ea | 557 | } |
9dae56ea A |
558 | } |
559 | ||
14957cd0 | 560 | return JSValue::encode(builder.build(exec)); |
9dae56ea A |
561 | } |
562 | ||
14957cd0 | 563 | EncodedJSValue JSC_HOST_CALL globalFuncUnescape(ExecState* exec) |
9dae56ea | 564 | { |
14957cd0 A |
565 | UStringBuilder builder; |
566 | UString str = exec->argument(0).toString(exec); | |
9dae56ea | 567 | int k = 0; |
14957cd0 | 568 | int len = str.length(); |
9dae56ea | 569 | while (k < len) { |
14957cd0 | 570 | const UChar* c = str.characters() + k; |
9dae56ea A |
571 | UChar u; |
572 | if (c[0] == '%' && k <= len - 6 && c[1] == 'u') { | |
ba379fdc | 573 | if (isASCIIHexDigit(c[2]) && isASCIIHexDigit(c[3]) && isASCIIHexDigit(c[4]) && isASCIIHexDigit(c[5])) { |
9dae56ea A |
574 | u = Lexer::convertUnicode(c[2], c[3], c[4], c[5]); |
575 | c = &u; | |
576 | k += 5; | |
577 | } | |
ba379fdc | 578 | } else if (c[0] == '%' && k <= len - 3 && isASCIIHexDigit(c[1]) && isASCIIHexDigit(c[2])) { |
9dae56ea A |
579 | u = UChar(Lexer::convertHex(c[1], c[2])); |
580 | c = &u; | |
581 | k += 2; | |
582 | } | |
583 | k++; | |
f9bf01c6 | 584 | builder.append(*c); |
9dae56ea A |
585 | } |
586 | ||
14957cd0 | 587 | return JSValue::encode(jsString(exec, builder.toUString())); |
9dae56ea | 588 | } |
9dae56ea A |
589 | |
590 | } // namespace JSC |