/* * QuickJS Javascript Engine * * Copyright (c) 2017-2021 Fabrice Bellard * Copyright (c) 2017-2021 Charlie Gordon * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include "third_party/quickjs/internal.h" asm(".ident\t\"\\n\\n\ QuickJS (MIT License)\\n\ Copyright (c) 2017-2021 Fabrice Bellard\\n\ Copyright (c) 2017-2021 Charlie Gordon\""); asm(".include \"libc/disclaimer.inc\""); /* clang-format off */ static int string_get_hex(JSString *p, int k, int n) { int c = 0, h; while (n-- > 0) { if ((h = from_hex(string_get(p, k++))) < 0) return -1; c = (c << 4) | h; } return c; } static int isURIReserved(int c) { return c < 0x100 && memchr(";/?:@&=+$,#", c, sizeof(";/?:@&=+$,#") - 1) != NULL; } static int hex_decode(JSContext *ctx, JSString *p, int k) { int c; if (k >= p->len || string_get(p, k) != '%') return js_throw_URIError(ctx, "expecting %%"); if (k + 2 >= p->len || (c = string_get_hex(p, k + 1, 2)) < 0) return js_throw_URIError(ctx, "expecting hex digit"); return c; } JSValue js_global_decodeURI(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int isComponent) { JSValue str; StringBuffer b_s, *b = &b_s; JSString *p; int k, c, c1, n, c_min; str = JS_ToString(ctx, argv[0]); if (JS_IsException(str)) return str; string_buffer_init(ctx, b, 0); p = JS_VALUE_GET_STRING(str); for (k = 0; k < p->len;) { c = string_get(p, k); if (c == '%') { c = hex_decode(ctx, p, k); if (c < 0) goto fail; k += 3; if (c < 0x80) { if (!isComponent && isURIReserved(c)) { c = '%'; k -= 2; } } else { /* Decode URI-encoded UTF-8 sequence */ if (c >= 0xc0 && c <= 0xdf) { n = 1; c_min = 0x80; c &= 0x1f; } else if (c >= 0xe0 && c <= 0xef) { n = 2; c_min = 0x800; c &= 0xf; } else if (c >= 0xf0 && c <= 0xf7) { n = 3; c_min = 0x10000; c &= 0x7; } else { n = 0; c_min = 1; c = 0; } while (n-- > 0) { c1 = hex_decode(ctx, p, k); if (c1 < 0) goto fail; k += 3; if ((c1 & 0xc0) != 0x80) { c = 0; break; } c = (c << 6) | (c1 & 0x3f); } if (c < c_min || c > 0x10FFFF || (c >= 0xd800 && c < 0xe000)) { js_throw_URIError(ctx, "malformed UTF-8"); goto fail; } } } else { k++; } string_buffer_putc(b, c); } JS_FreeValue(ctx, str); return string_buffer_end(b); fail: JS_FreeValue(ctx, str); string_buffer_free(b); return JS_EXCEPTION; } static int isUnescaped(int c) { static char const unescaped_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789" "@*_+-./"; return c < 0x100 && memchr(unescaped_chars, c, sizeof(unescaped_chars) - 1); } static int isURIUnescaped(int c, int isComponent) { return c < 0x100 && ((c >= 0x61 && c <= 0x7a) || (c >= 0x41 && c <= 0x5a) || (c >= 0x30 && c <= 0x39) || memchr("-_.!~*'()", c, sizeof("-_.!~*'()") - 1) != NULL || (!isComponent && isURIReserved(c))); } static int encodeURI_hex(StringBuffer *b, int c) { uint8_t buf[6]; int n = 0; const char *hex = "0123456789ABCDEF"; buf[n++] = '%'; if (c >= 256) { buf[n++] = 'u'; buf[n++] = hex[(c >> 12) & 15]; buf[n++] = hex[(c >> 8) & 15]; } buf[n++] = hex[(c >> 4) & 15]; buf[n++] = hex[(c >> 0) & 15]; return string_buffer_write8(b, buf, n); } JSValue js_global_encodeURI(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int isComponent) { JSValue str; StringBuffer b_s, *b = &b_s; JSString *p; int k, c, c1; str = JS_ToString(ctx, argv[0]); if (JS_IsException(str)) return str; p = JS_VALUE_GET_STRING(str); string_buffer_init(ctx, b, p->len); for (k = 0; k < p->len;) { c = string_get(p, k); k++; if (isURIUnescaped(c, isComponent)) { string_buffer_putc16(b, c); } else { if (c >= 0xdc00 && c <= 0xdfff) { js_throw_URIError(ctx, "invalid character"); goto fail; } else if (c >= 0xd800 && c <= 0xdbff) { if (k >= p->len) { js_throw_URIError(ctx, "expecting surrogate pair"); goto fail; } c1 = string_get(p, k); k++; if (c1 < 0xdc00 || c1 > 0xdfff) { js_throw_URIError(ctx, "expecting surrogate pair"); goto fail; } c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000; } if (c < 0x80) { encodeURI_hex(b, c); } else { /* XXX: use C UTF-8 conversion ? */ if (c < 0x800) { encodeURI_hex(b, (c >> 6) | 0xc0); } else { if (c < 0x10000) { encodeURI_hex(b, (c >> 12) | 0xe0); } else { encodeURI_hex(b, (c >> 18) | 0xf0); encodeURI_hex(b, ((c >> 12) & 0x3f) | 0x80); } encodeURI_hex(b, ((c >> 6) & 0x3f) | 0x80); } encodeURI_hex(b, (c & 0x3f) | 0x80); } } } JS_FreeValue(ctx, str); return string_buffer_end(b); fail: JS_FreeValue(ctx, str); string_buffer_free(b); return JS_EXCEPTION; } JSValue js_global_escape(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { JSValue str; StringBuffer b_s, *b = &b_s; JSString *p; int i, len, c; str = JS_ToString(ctx, argv[0]); if (JS_IsException(str)) return str; p = JS_VALUE_GET_STRING(str); string_buffer_init(ctx, b, p->len); for (i = 0, len = p->len; i < len; i++) { c = string_get(p, i); if (isUnescaped(c)) { string_buffer_putc16(b, c); } else { encodeURI_hex(b, c); } } JS_FreeValue(ctx, str); return string_buffer_end(b); } JSValue js_global_unescape(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { JSValue str; StringBuffer b_s, *b = &b_s; JSString *p; int i, len, c, n; str = JS_ToString(ctx, argv[0]); if (JS_IsException(str)) return str; string_buffer_init(ctx, b, 0); p = JS_VALUE_GET_STRING(str); for (i = 0, len = p->len; i < len; i++) { c = string_get(p, i); if (c == '%') { if (i + 6 <= len && string_get(p, i + 1) == 'u' && (n = string_get_hex(p, i + 2, 4)) >= 0) { c = n; i += 6 - 1; } else if (i + 3 <= len && (n = string_get_hex(p, i + 1, 2)) >= 0) { c = n; i += 3 - 1; } } string_buffer_putc16(b, c); } JS_FreeValue(ctx, str); return string_buffer_end(b); }