diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-global.cpp b/jerry-core/ecma/builtin-objects/ecma-builtin-global.cpp index f7d6db0ba1..353eb24a4c 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-global.cpp +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-global.cpp @@ -1,4 +1,5 @@ /* Copyright 2014-2015 Samsung Electronics Co., Ltd. + * Copyright 2015 University of Szeged * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +18,7 @@ #include "ecma-builtins.h" #include "ecma-conversion.h" #include "ecma-eval.h" +#include "ecma-exceptions.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" @@ -175,6 +177,336 @@ ecma_builtin_global_object_is_finite (ecma_value_t this_arg __attr_unused___, /* return ret_value; } /* ecma_builtin_global_object_is_finite */ +/** + * Helper function to check whether a character is in a character bitset. + * + * @return true if the character is in the character bitset. + */ +static bool +ecma_builtin_global_object_character_is_in (uint32_t character, /**< character */ + uint8_t *bitset) /**< character set */ +{ + JERRY_ASSERT (character < 128); + return (bitset[character >> 3] & (1 << (character & 0x7))) != 0; +} /* ecma_builtin_global_object_character_is_in */ + +/* + * Unescaped URI characters bitset: + * One bit for each character between 0 - 127. + * Bit is set if the character is in the unescaped URI set. + */ +static uint8_t unescaped_uri_set[16] = +{ + 0x0, 0x0, 0x0, 0x0, 0xda, 0xff, 0xff, 0xaf, + 0xff, 0xff, 0xff, 0x87, 0xfe, 0xff, 0xff, 0x47 +}; + +/* + * Unescaped URI component characters bitset: + * One bit for each character between 0 - 127. + * Bit is set if the character is in the unescaped component URI set. + */ +static uint8_t unescaped_uri_component_set[16] = +{ + 0x0, 0x0, 0x0, 0x0, 0x82, 0x67, 0xff, 0x3, + 0xfe, 0xff, 0xff, 0x87, 0xfe, 0xff, 0xff, 0x47 +}; + +#define ECMA_BUILTIN_HEX_TO_BYTE_ERROR 0x100 + +/** + * Helper function to decode a hexadecimal byte from a string. + * + * @return the decoded byte value + * It returns with ECMA_BUILTIN_HEX_TO_BYTE_ERROR if a parse error is occured. + */ +static uint32_t +ecma_builtin_global_object_hex_to_byte (ecma_char_t *source_p) /**< source string */ +{ + uint32_t decoded_byte = 0; + + /* + * Zero terminated string, so length check is not needed. + */ + if (*source_p != '%') + { + return ECMA_BUILTIN_HEX_TO_BYTE_ERROR; + } + + for (int i = 0; i < 2; i++) + { + source_p++; + decoded_byte <<= 4; + + if (*source_p >= '0' && *source_p <= '9') + { + decoded_byte |= (uint32_t) (*source_p - '0'); + } + else if (*source_p >= 'a' && *source_p <= 'f') + { + decoded_byte |= (uint32_t) (*source_p - ('a' - 10)); + } + else if (*source_p >= 'A' && *source_p <= 'F') + { + decoded_byte |= (uint32_t) (*source_p - ('A' - 10)); + } + else + { + return ECMA_BUILTIN_HEX_TO_BYTE_ERROR; + } + } + + return decoded_byte; +} /* ecma_builtin_global_object_hex_to_byte */ + +/** + * Helper function to decode URI. + * + * @return completion value + * Returned value must be freed with ecma_free_completion_value. + */ +static ecma_completion_value_t +ecma_builtin_global_object_decode_uri_helper (ecma_value_t uri __attr_unused___, /**< uri argument */ + uint8_t *reserved_uri_bitset) /**< reserved characters bitset */ +{ + ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); + + ECMA_TRY_CATCH (string, + ecma_op_to_string (uri), + ret_value); + + JERRY_ASSERT (ecma_is_value_string (string)); + + ecma_string_t *input_string_p = ecma_get_string_from_value (string); + uint32_t input_length = (uint32_t) ecma_string_get_length (input_string_p); + + MEM_DEFINE_LOCAL_ARRAY (input_start_p, + input_length + 1, + ecma_char_t); + + ecma_string_to_zt_string (input_string_p, + input_start_p, + (ssize_t) (input_length + 1) * (ssize_t) sizeof (ecma_char_t)); + + ecma_char_t *input_char_p = input_start_p; + ecma_char_t *input_end_p = input_start_p + input_length; + uint32_t output_length = 1; + + /* + * The URI decoding has two major phases: first we validate the input, + * and compute the length of the output, then we decode the input. + */ + + while (input_char_p < input_end_p) + { + /* Input validation. */ + if (*input_char_p != '%') + { + output_length++; + input_char_p++; + continue; + } + + uint32_t decoded_byte = ecma_builtin_global_object_hex_to_byte (input_char_p); + if (decoded_byte == ECMA_BUILTIN_HEX_TO_BYTE_ERROR) + { + ret_value = ecma_make_throw_obj_completion_value (ecma_new_standard_error (ECMA_ERROR_URI)); + break; + } + + input_char_p += 3; + + if (decoded_byte <= 0x7f) + { + /* + * We don't decode those bytes, which are part of reserved_uri_bitset + * but not part of unescaped_uri_component_set. + */ + if (ecma_builtin_global_object_character_is_in (decoded_byte, reserved_uri_bitset) + && !ecma_builtin_global_object_character_is_in (decoded_byte, unescaped_uri_component_set)) + { + output_length += 3; + } + else + { + output_length++; + } + } + else if (decoded_byte < 0xc0 || decoded_byte >= 0xf8) + { + /* + * Invalid UTF-8 starting bytes: + * 10xx xxxx - UTF continuation byte + * 1111 1xxx - maximum length is 4 bytes + */ + ret_value = ecma_make_throw_obj_completion_value (ecma_new_standard_error (ECMA_ERROR_URI)); + break; + } + else + { + uint32_t count; + uint32_t min; + uint32_t character; + + if (decoded_byte < 0xe0) + { + count = 1; + min = 0x80; + character = decoded_byte & 0x1f; + } + else if (decoded_byte < 0xf0) + { + count = 2; + min = 0x800; + character = decoded_byte & 0x0f; + } + else + { + count = 3; + min = 0x1000; + character = decoded_byte & 0x07; + } + + do + { + decoded_byte = ecma_builtin_global_object_hex_to_byte (input_char_p); + if (decoded_byte == ECMA_BUILTIN_HEX_TO_BYTE_ERROR + || (decoded_byte & 0xc0) != 0x80) + { + break; + } + + character = (character << 6) + (decoded_byte & 0x3f); + input_char_p += 3; + } + while (--count > 0); + + if (count != 0 + /* + * Explanation of the character < min check: according to + * the UTF standard, each character must be encoded + * with the minimum amount of bytes. We need to reject + * those characters, which does not satisfy this condition. + */ + || character < min + /* + * Not allowed character ranges. + */ + || character > 0x10ffff + || (character >= 0xd800 && character <= 0xdfff)) + { + ret_value = ecma_make_throw_obj_completion_value (ecma_new_standard_error (ECMA_ERROR_URI)); + break; + } + + output_length += (character <= 0xffff) ? 1 : 2; + } + } + + if (ecma_is_completion_value_empty (ret_value)) + { + MEM_DEFINE_LOCAL_ARRAY (output_start_p, + output_length, + ecma_char_t); + + input_char_p = input_start_p; + ecma_char_t *output_char_p = output_start_p; + + while (input_char_p < input_end_p) + { + /* Input decode. */ + if (*input_char_p != '%') + { + *output_char_p = *input_char_p; + output_char_p++; + input_char_p++; + continue; + } + + uint32_t decoded_byte = ecma_builtin_global_object_hex_to_byte (input_char_p); + input_char_p += 3; + + if (decoded_byte <= 0x7f) + { + if (ecma_builtin_global_object_character_is_in (decoded_byte, reserved_uri_bitset) + && !ecma_builtin_global_object_character_is_in (decoded_byte, unescaped_uri_component_set)) + { + *output_char_p = '%'; + output_char_p++; + input_char_p -= 2; + } + else + { + *output_char_p = (ecma_char_t) decoded_byte; + output_char_p++; + } + } + else + { + uint32_t count; + uint32_t character; + + /* The validator already checked this before. */ + JERRY_ASSERT (decoded_byte >= 0xc0 && decoded_byte < 0xf8); + + if (decoded_byte < 0xe0) + { + count = 1; + character = decoded_byte & 0x1f; + } + else if (decoded_byte < 0xf0) + { + count = 2; + character = decoded_byte & 0x0f; + } + else + { + count = 3; + character = decoded_byte & 0x07; + } + + do + { + decoded_byte = ecma_builtin_global_object_hex_to_byte (input_char_p); + JERRY_ASSERT (decoded_byte != ECMA_BUILTIN_HEX_TO_BYTE_ERROR + && (decoded_byte & 0xc0) == 0x80); + character = (character << 6) + (decoded_byte & 0x3f); + input_char_p += 3; + } + while (--count > 0); + + if (character < 0x10000) + { + *output_char_p = (ecma_char_t) character; + output_char_p++; + } + else + { + character -= 0x10000; + *output_char_p = (ecma_char_t) (0xd800 | (character & 0x3ff)); + output_char_p++; + *output_char_p = (ecma_char_t) (0xdc00 | (character >> 10)); + output_char_p++; + } + } + } + + *output_char_p = '\0'; + JERRY_ASSERT (output_start_p + output_length == output_char_p + 1); + + ecma_string_t *output_string_p = ecma_new_ecma_string (output_start_p); + + ret_value = ecma_make_normal_completion_value (ecma_make_string_value (output_string_p)); + + MEM_FINALIZE_LOCAL_ARRAY (output_start_p); + } + + MEM_FINALIZE_LOCAL_ARRAY (input_start_p); + + ECMA_FINALIZE (string); + return ret_value; +} /* ecma_builtin_global_object_decode_uri_helper */ + /** * The Global object's 'decodeURI' routine * @@ -185,10 +517,10 @@ ecma_builtin_global_object_is_finite (ecma_value_t this_arg __attr_unused___, /* * Returned value must be freed with ecma_free_completion_value. */ static ecma_completion_value_t -ecma_builtin_global_object_decode_uri (ecma_value_t this_arg, /**< this argument */ +ecma_builtin_global_object_decode_uri (ecma_value_t this_arg __attr_unused___, /**< this argument */ ecma_value_t encoded_uri) /**< routine's first argument */ { - ECMA_BUILTIN_CP_UNIMPLEMENTED (this_arg, encoded_uri); + return ecma_builtin_global_object_decode_uri_helper (encoded_uri, unescaped_uri_set); } /* ecma_builtin_global_object_decode_uri */ /** @@ -201,13 +533,178 @@ ecma_builtin_global_object_decode_uri (ecma_value_t this_arg, /**< this argument * Returned value must be freed with ecma_free_completion_value. */ static ecma_completion_value_t -ecma_builtin_global_object_decode_uri_component (ecma_value_t this_arg, /**< this argument */ +ecma_builtin_global_object_decode_uri_component (ecma_value_t this_arg __attr_unused___, /**< this argument */ ecma_value_t encoded_uri_component) /**< routine's * first argument */ { - ECMA_BUILTIN_CP_UNIMPLEMENTED (this_arg, encoded_uri_component); + return ecma_builtin_global_object_decode_uri_helper (encoded_uri_component, unescaped_uri_component_set); } /* ecma_builtin_global_object_decode_uri_component */ +/** + * Helper function to encode byte as hexadecimal values. + */ +static void +ecma_builtin_global_object_byte_to_hex (ecma_char_t *dest_p, /**< destination pointer */ + uint32_t byte) /**< value */ +{ + JERRY_ASSERT (byte < 256); + + dest_p[0] = '%'; + ecma_char_t hex_digit = (ecma_char_t) (byte >> 4); + dest_p[1] = (ecma_char_t) ((hex_digit > 9) ? (hex_digit + ('A' - 10)) : (hex_digit + '0')); + hex_digit = (ecma_char_t) (byte & 0xf); + dest_p[2] = (ecma_char_t) ((hex_digit > 9) ? (hex_digit + ('A' - 10)) : (hex_digit + '0')); +} /* ecma_builtin_global_object_byte_to_hex */ + +/** + * Helper function to encode URI. + * + * @return completion value + * Returned value must be freed with ecma_free_completion_value. + */ +static ecma_completion_value_t +ecma_builtin_global_object_encode_uri_helper (ecma_value_t uri, /**< uri argument */ + uint8_t* unescaped_uri_bitset) /**< unescaped bitset */ +{ + ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); + + ECMA_TRY_CATCH (string, + ecma_op_to_string (uri), + ret_value); + + JERRY_ASSERT (ecma_is_value_string (string)); + + ecma_string_t *input_string_p = ecma_get_string_from_value (string); + uint32_t input_length = (uint32_t) ecma_string_get_length (input_string_p); + + MEM_DEFINE_LOCAL_ARRAY (input_start_p, + input_length + 1, + ecma_char_t); + + ecma_string_to_zt_string (input_string_p, + input_start_p, + (ssize_t) (input_length + 1) * (ssize_t) sizeof (ecma_char_t)); + + /* + * The URI encoding has two major phases: first we validate the input, + * and compute the length of the output, then we encode the input. + */ + + ecma_char_t *input_char_p = input_start_p; + uint32_t output_length = 1; + for (uint32_t i = 0; i < input_length; i++) + { + /* Input validation. */ + uint32_t character = *input_char_p++; + + if (character <= 0x7f) + { + if (ecma_builtin_global_object_character_is_in (character, unescaped_uri_bitset)) + { + output_length++; + } + else + { + output_length += 3; + } + } + else if (character <= 0x7ff) + { + output_length += 6; + } + else if (character <= 0xffff) + { + if (character >= 0xd800 && character <= 0xdfff) + { + ret_value = ecma_make_throw_obj_completion_value (ecma_new_standard_error (ECMA_ERROR_URI)); + break; + } + else + { + output_length += 9; + } + } + else if (character <= 0x10ffff) + { + output_length += 12; + } + else + { + ret_value = ecma_make_throw_obj_completion_value (ecma_new_standard_error (ECMA_ERROR_URI)); + break; + } + } + + if (ecma_is_completion_value_empty (ret_value)) + { + MEM_DEFINE_LOCAL_ARRAY (output_start_p, + output_length, + ecma_char_t); + + input_char_p = input_start_p; + ecma_char_t *output_char_p = output_start_p; + for (uint32_t i = 0; i < input_length; i++) + { + /* Input decode. */ + uint32_t character = *input_char_p++; + + if (character <= 0x7f) + { + if (ecma_builtin_global_object_character_is_in (character, unescaped_uri_bitset)) + { + *output_char_p++ = (ecma_char_t) character; + } + else + { + ecma_builtin_global_object_byte_to_hex (output_char_p, character); + output_char_p += 3; + } + } + else if (character <= 0x7ff) + { + ecma_builtin_global_object_byte_to_hex (output_char_p, 0xc0 | (character >> 6)); + output_char_p += 3; + ecma_builtin_global_object_byte_to_hex (output_char_p, 0x80 | (character & 0x3f)); + output_char_p += 3; + } + else if (character <= 0xffff) + { + ecma_builtin_global_object_byte_to_hex (output_char_p, 0xe0 | (character >> 12)); + output_char_p += 3; + ecma_builtin_global_object_byte_to_hex (output_char_p, 0x80 | ((character >> 6) & 0x3f)); + output_char_p += 3; + ecma_builtin_global_object_byte_to_hex (output_char_p, 0x80 | (character & 0x3f)); + output_char_p += 3; + } + else + { + ecma_builtin_global_object_byte_to_hex (output_char_p, 0xf0 | (character >> 18)); + output_char_p += 3; + ecma_builtin_global_object_byte_to_hex (output_char_p, 0x80 | ((character >> 12) & 0x3f)); + output_char_p += 3; + ecma_builtin_global_object_byte_to_hex (output_char_p, 0x80 | ((character >> 6) & 0x3f)); + output_char_p += 3; + ecma_builtin_global_object_byte_to_hex (output_char_p, 0x80 | (character & 0x3f)); + output_char_p += 3; + } + } + + *output_char_p = '\0'; + JERRY_ASSERT (output_start_p + output_length == output_char_p + 1); + + ecma_string_t *output_string_p = ecma_new_ecma_string (output_start_p); + + ret_value = ecma_make_normal_completion_value (ecma_make_string_value (output_string_p)); + + MEM_FINALIZE_LOCAL_ARRAY (output_start_p); + } + + MEM_FINALIZE_LOCAL_ARRAY (input_start_p); + + ECMA_FINALIZE (string); + return ret_value; +} /* ecma_builtin_global_object_encode_uri_helper */ + /** * The Global object's 'encodeURI' routine * @@ -218,10 +715,10 @@ ecma_builtin_global_object_decode_uri_component (ecma_value_t this_arg, /**< thi * Returned value must be freed with ecma_free_completion_value. */ static ecma_completion_value_t -ecma_builtin_global_object_encode_uri (ecma_value_t this_arg, /**< this argument */ +ecma_builtin_global_object_encode_uri (ecma_value_t this_arg __attr_unused___, /**< this argument */ ecma_value_t uri) /**< routine's first argument */ { - ECMA_BUILTIN_CP_UNIMPLEMENTED (this_arg, uri); + return ecma_builtin_global_object_encode_uri_helper (uri, unescaped_uri_set); } /* ecma_builtin_global_object_encode_uri */ /** @@ -234,10 +731,10 @@ ecma_builtin_global_object_encode_uri (ecma_value_t this_arg, /**< this argument * Returned value must be freed with ecma_free_completion_value. */ static ecma_completion_value_t -ecma_builtin_global_object_encode_uri_component (ecma_value_t this_arg, /**< this argument */ +ecma_builtin_global_object_encode_uri_component (ecma_value_t this_arg __attr_unused___, /**< this argument */ ecma_value_t uri_component) /**< routine's first argument */ { - ECMA_BUILTIN_CP_UNIMPLEMENTED (this_arg, uri_component); + return ecma_builtin_global_object_encode_uri_helper (uri_component, unescaped_uri_component_set); } /* ecma_builtin_global_object_encode_uri_component */ /** diff --git a/tests/jerry/global-uri-coding.js b/tests/jerry/global-uri-coding.js new file mode 100644 index 0000000000..9dcf116b1c --- /dev/null +++ b/tests/jerry/global-uri-coding.js @@ -0,0 +1,111 @@ +// Copyright 2015 University of Szeged +// Copyright 2015 Samsung Electronics Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// URI encoding + +assert (encodeURI ("\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f") === + "%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F"); +assert (encodeURI ("\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f") === + "%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F"); +assert (encodeURI (" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMN") === + "%20!%22#$%25&'()*+,-./0123456789:;%3C=%3E?@ABCDEFGHIJKLMN"); +assert (encodeURI ("OPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}\x7F") === + "OPQRSTUVWXYZ%5B%5C%5D%5E_%60abcdefghijklmnopqrstuvwxyz%7B%7C%7D%7F"); + +assert (encodeURIComponent ("\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f") === + "%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F"); +assert (encodeURIComponent ("\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f") === + "%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F"); +assert (encodeURIComponent (" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMN") === + "%20!%22%23%24%25%26'()*%2B%2C-.%2F0123456789%3A%3B%3C%3D%3E%3F%40ABCDEFGHIJKLMN"); +assert (encodeURIComponent ("OPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}\x7F") === + "OPQRSTUVWXYZ%5B%5C%5D%5E_%60abcdefghijklmnopqrstuvwxyz%7B%7C%7D%7F"); + +// TODO: we need tests for characters greater than 0xff and equal to 0x0 + +assert (encodeURI ("\xe9") == "%C3%A9"); + +// URI decoding + +function checkDecodeURIParseError (str) +{ + try { + decodeURI (str); + assert (false); + } catch(e) { + assert(e instanceof URIError); + } +} + +assert (decodeURI ("%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F") === + "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"); +assert (decodeURI ("%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F") === + "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"); +assert (decodeURI ("%20%21%22%23%24%25%26%27%28%29%2a%2b%2c%2d%2e%2f") === + " !\"%23%24%%26'()*%2b%2c-.%2f"); +assert (decodeURI ("%30%31%32%33%34%35%36%37%38%39%3a%3b%3c%3d%3e%3f") === + "0123456789%3a%3b<%3d>%3f"); +assert (decodeURI ("%40%41%42%43%44%45%46%47%48%49%4a%4b%4c%4d%4e%4f") === + "%40ABCDEFGHIJKLMNO"); +assert (decodeURI ("%50%51%52%53%54%55%56%57%58%59%5a%5b%5c%5d%5e%5f") === + "PQRSTUVWXYZ[\\]^_"); +assert (decodeURI ("%60%61%62%63%64%65%66%67%68%69%6a%6b%6c%6d%6e%6f") === + "`abcdefghijklmno"); +assert (decodeURI ("%70%71%72%73%74%75%76%77%78%79%7a%7b%7c%7d%7e") === + "pqrstuvwxyz{|}~"); + +assert (decodeURIComponent ("%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F") === + "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"); +assert (decodeURIComponent ("%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F") === + "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"); +assert (decodeURIComponent ("%20%21%22%23%24%25%26%27%28%29%2a%2b%2c%2d%2e%2f") === + " !\"#$%&'()*+,-./"); +assert (decodeURIComponent ("%30%31%32%33%34%35%36%37%38%39%3a%3b%3c%3d%3e%3f") === + "0123456789:;<=>?"); +assert (decodeURIComponent ("%40%41%42%43%44%45%46%47%48%49%4a%4b%4c%4d%4e%4f") === + "@ABCDEFGHIJKLMNO"); +assert (decodeURIComponent ("%50%51%52%53%54%55%56%57%58%59%5a%5b%5c%5d%5e%5f") === + "PQRSTUVWXYZ[\\]^_"); +assert (decodeURIComponent ("%60%61%62%63%64%65%66%67%68%69%6a%6b%6c%6d%6e%6f") === + "`abcdefghijklmno"); +assert (decodeURIComponent ("%70%71%72%73%74%75%76%77%78%79%7a%7b%7c%7d%7e") === + "pqrstuvwxyz{|}~"); + + +assert (decodeURI ("%6A%6B%6C%6D%6E%6F") === "jklmno"); +assert (decodeURI ("%C3%A9") === "\xe9"); + +checkDecodeURIParseError ("13%"); +checkDecodeURIParseError ("%0g"); +checkDecodeURIParseError ("%1G"); +checkDecodeURIParseError ("%a"); +checkDecodeURIParseError ("%c1%81"); +checkDecodeURIParseError ("a%80b"); +checkDecodeURIParseError ("%f4%90%80%80"); +checkDecodeURIParseError ("%ed%a0%80"); +checkDecodeURIParseError ("%ed%b0%80"); +checkDecodeURIParseError ("%fa%81%82%83%84"); +checkDecodeURIParseError ("%fd%81%82%83%84%85"); + +// Coerce (automatic toString()) tests + +assert (decodeURI ([1, 2, 3]) === "1,2,3"); +assert (decodeURI ({ x:1 }) === "[object Object]"); +assert (encodeURI (void 0) === "undefined"); +assert (encodeURI (216.000e1) === "2160"); + +// TODO: we need tests for characters greater than 0xff and equal to 0x0 + +assert (decodeURI ("%f0%9f%9f%8f").length === 2);