From 6b00a6268e975192099e790d13df80627b70a063 Mon Sep 17 00:00:00 2001 From: Zoltan Herczeg Date: Mon, 30 Aug 2021 06:08:25 +0000 Subject: [PATCH] Implement function.toString operation May increase the memory consumtpion heavily. JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com --- docs/02.API-REFERENCE.md | 5 +- jerry-core/CMakeLists.txt | 6 + jerry-core/api/jerry-snapshot.c | 15 +- jerry-core/api/jerry.c | 10 +- jerry-core/config.h | 17 ++ jerry-core/ecma/base/ecma-extended-info.c | 152 ++++++++++++++ jerry-core/ecma/base/ecma-extended-info.h | 58 ++++++ jerry-core/ecma/base/ecma-gc.c | 6 +- jerry-core/ecma/base/ecma-globals.h | 3 + jerry-core/ecma/base/ecma-helpers-string.c | 2 +- jerry-core/ecma/base/ecma-helpers.c | 42 ++-- jerry-core/ecma/base/ecma-helpers.h | 1 - .../ecma-builtin-function-prototype.c | 114 ++++++++++- .../builtin-objects/ecma-builtin-global.c | 2 +- .../ecma/operations/ecma-function-object.c | 17 +- jerry-core/include/jerryscript-snapshot.h | 2 +- jerry-core/include/jerryscript-types.h | 1 + jerry-core/lit/lit-magic-strings.inc.h | 57 +++++- jerry-core/lit/lit-magic-strings.ini | 7 +- jerry-core/lit/lit-strings.c | 31 +-- jerry-core/lit/lit-strings.h | 2 +- jerry-core/parser/js/byte-code.h | 70 +++++-- jerry-core/parser/js/common.c | 26 ++- jerry-core/parser/js/js-lexer.c | 30 ++- jerry-core/parser/js/js-lexer.h | 15 +- jerry-core/parser/js/js-parser-expr.c | 14 +- jerry-core/parser/js/js-parser-internal.h | 10 + jerry-core/parser/js/js-parser-statm.c | 21 ++ jerry-core/parser/js/js-parser.c | 173 +++++++++++++--- jerry-core/vm/vm.c | 2 +- .../es.next/function-prototype-tostring.js | 188 ++++++++++++++++++ tests/jerry/es.next/new-target.js | 4 +- .../es.next/regression-test-issue-4468.js | 4 +- tests/jerry/function-prototype-tostring.js | 21 +- tests/test262-esnext-excludelist.xml | 61 ------ tools/build.py | 3 + tools/run-tests.py | 2 +- 37 files changed, 1002 insertions(+), 192 deletions(-) create mode 100644 jerry-core/ecma/base/ecma-extended-info.c create mode 100644 jerry-core/ecma/base/ecma-extended-info.h create mode 100644 tests/jerry/es.next/function-prototype-tostring.js diff --git a/docs/02.API-REFERENCE.md b/docs/02.API-REFERENCE.md index a01f5da2ef..835e960b74 100644 --- a/docs/02.API-REFERENCE.md +++ b/docs/02.API-REFERENCE.md @@ -175,6 +175,8 @@ Possible compile time enabled feature types: - JERRY_FEATURE_GLOBAL_THIS - GlobalThisValue support - JERRY_FEATURE_PROMISE_CALLBACK - Promise callback support - JERRY_FEATURE_MODULE - Module support + - JERRY_FEATURE_WEAKREF - WeakRef support + - JERRY_FEATURE_FUNCTION_TO_STRING - function toString support *New in version 2.0*. @@ -183,7 +185,8 @@ Possible compile time enabled feature types: *Changed in version 2.4*: Added `JERRY_FEATURE_BIGINT`, `JERRY_FEATURE_REALM` values. *Changed in version [[NEXT_RELEASE]]*: Added `JERRY_FEATURE_VM_THROW`, `JERRY_FEATURE_GLOBAL_THIS`, - `JERRY_FEATURE_PROMISE_CALLBACK`, and `JERRY_FEATURE_MODULE` values. + `JERRY_FEATURE_PROMISE_CALLBACK`, and `JERRY_FEATURE_MODULE`, + `JERRY_FEATURE_WEAKREF`, and `JERRY_FEATURE_FUNCTION_TO_STRING` values. ## jerry_container_type_t diff --git a/jerry-core/CMakeLists.txt b/jerry-core/CMakeLists.txt index cb3c228536..f4e08de8bf 100644 --- a/jerry-core/CMakeLists.txt +++ b/jerry-core/CMakeLists.txt @@ -24,6 +24,7 @@ set(JERRY_DEBUGGER OFF CACHE BOOL "Enable JerryScrip set(JERRY_ERROR_MESSAGES OFF CACHE BOOL "Enable error messages?") set(JERRY_EXTERNAL_CONTEXT OFF CACHE BOOL "Enable external context?") set(JERRY_PARSER ON CACHE BOOL "Enable javascript-parser?") +set(JERRY_FUNCTION_TO_STRING OFF CACHE BOOL "Enable function toString operation?") set(JERRY_LINE_INFO OFF CACHE BOOL "Enable line info?") set(JERRY_LOGGING OFF CACHE BOOL "Enable logging?") set(JERRY_MEM_STATS OFF CACHE BOOL "Enable memory statistics?") @@ -83,6 +84,7 @@ message(STATUS "JERRY_DEBUGGER " ${JERRY_DEBUGGER}) message(STATUS "JERRY_ERROR_MESSAGES " ${JERRY_ERROR_MESSAGES}) message(STATUS "JERRY_EXTERNAL_CONTEXT " ${JERRY_EXTERNAL_CONTEXT}) message(STATUS "JERRY_PARSER " ${JERRY_PARSER}) +message(STATUS "JERRY_FUNCTION_TO_STRING " ${JERRY_FUNCTION_TO_STRING}) message(STATUS "JERRY_LINE_INFO " ${JERRY_LINE_INFO}) message(STATUS "JERRY_LOGGING " ${JERRY_LOGGING} ${JERRY_LOGGING_MESSAGE}) message(STATUS "JERRY_MEM_STATS " ${JERRY_MEM_STATS}) @@ -135,6 +137,7 @@ set(SOURCE_CORE_FILES ecma/base/ecma-alloc.c ecma/base/ecma-gc.c ecma/base/ecma-errors.c + ecma/base/ecma-extended-info.c ecma/base/ecma-helpers-collection.c ecma/base/ecma-helpers-conversion.c ecma/base/ecma-helpers-errol.c @@ -559,6 +562,9 @@ jerry_add_define01(JERRY_EXTERNAL_CONTEXT) # JS-Parser jerry_add_define01(JERRY_PARSER) +# JS function toString +jerry_add_define01(JERRY_FUNCTION_TO_STRING) + # JS line info jerry_add_define01(JERRY_LINE_INFO) diff --git a/jerry-core/api/jerry-snapshot.c b/jerry-core/api/jerry-snapshot.c index 4af6ca3032..5af9192f1d 100644 --- a/jerry-core/api/jerry-snapshot.c +++ b/jerry-core/api/jerry-snapshot.c @@ -979,8 +979,13 @@ jerry_exec_snapshot (const uint32_t *snapshot_p, /**< snapshot */ user_value = option_values_p->user_value; } - uint32_t script_size = (user_value != ECMA_VALUE_EMPTY ? sizeof (cbc_script_user_t) - : sizeof (cbc_script_t)); + size_t script_size = sizeof (cbc_script_t); + + if (user_value != ECMA_VALUE_EMPTY) + { + script_size += sizeof (ecma_value_t); + } + cbc_script_t *script_p = jmem_heap_alloc_block (script_size); CBC_SCRIPT_SET_TYPE (script_p, user_value, CBC_SCRIPT_REF_ONE); @@ -1003,6 +1008,10 @@ jerry_exec_snapshot (const uint32_t *snapshot_p, /**< snapshot */ script_p->resource_name = resource_name; #endif /* JERRY_RESOURCE_NAME */ +#if JERRY_FUNCTION_TO_STRING + script_p->source_code = ecma_make_magic_string_value (LIT_MAGIC_STRING__EMPTY); +#endif /* JERRY_FUNCTION_TO_STRING */ + const uint8_t *literal_base_p = snapshot_data_p + header_p->lit_table_offset; bytecode_p = snapshot_load_compiled_code ((const uint8_t *) bytecode_p, @@ -1021,7 +1030,7 @@ jerry_exec_snapshot (const uint32_t *snapshot_p, /**< snapshot */ if (user_value != ECMA_VALUE_EMPTY) { - ((cbc_script_user_t *) script_p)->user_value = ecma_copy_value_if_not_object (user_value); + CBC_SCRIPT_GET_USER_VALUE (script_p) = ecma_copy_value_if_not_object (user_value); } } diff --git a/jerry-core/api/jerry.c b/jerry-core/api/jerry.c index cb0079da76..b20b0b8f55 100644 --- a/jerry-core/api/jerry.c +++ b/jerry-core/api/jerry.c @@ -1841,6 +1841,9 @@ jerry_is_feature_enabled (const jerry_feature_t feature) /**< feature to check * #if JERRY_MODULE_SYSTEM || feature == JERRY_FEATURE_MODULE #endif /* JERRY_MODULE_SYSTEM */ +#if JERRY_FUNCTION_TO_STRING + || feature == JERRY_FEATURE_FUNCTION_TO_STRING +#endif /* JERRY_FUNCTION_TO_STRING */ ); } /* jerry_is_feature_enabled */ @@ -2801,7 +2804,7 @@ jerry_create_regexp_sz (const jerry_char_t *pattern_p, /**< zero-terminated UTF- jerry_assert_api_available (); #if JERRY_BUILTIN_REGEXP - if (!lit_is_valid_utf8_string (pattern_p, pattern_size)) + if (!lit_is_valid_utf8_string (pattern_p, pattern_size, true)) { return jerry_throw (ecma_raise_common_error (ECMA_ERR_MSG ("Input must be a valid utf8 string"))); } @@ -5130,7 +5133,8 @@ jerry_is_valid_utf8_string (const jerry_char_t *utf8_buf_p, /**< UTF-8 string */ jerry_size_t buf_size) /**< string size */ { return lit_is_valid_utf8_string ((lit_utf8_byte_t *) utf8_buf_p, - (lit_utf8_size_t) buf_size); + (lit_utf8_size_t) buf_size, + true); } /* jerry_is_valid_utf8_string */ /** @@ -5490,7 +5494,7 @@ jerry_get_user_value (const jerry_value_t value) /**< jerry api value */ return ECMA_VALUE_UNDEFINED; } - return ecma_copy_value (((cbc_script_user_t *) script_p)->user_value); + return ecma_copy_value (CBC_SCRIPT_GET_USER_VALUE (script_p)); } /* jerry_get_user_value */ /** diff --git a/jerry-core/config.h b/jerry-core/config.h index 67c3de9d43..7c5d0305f3 100644 --- a/jerry-core/config.h +++ b/jerry-core/config.h @@ -231,6 +231,19 @@ # define JERRY_LCACHE 1 #endif /* !defined (JERRY_LCACHE) */ +/** + * Enable/Disable function toString operation. + * + * Allowed values: + * 0: Disable function toString operation. + * 1: Enable function toString operation. + * + * Default value: 0 + */ +#ifndef JERRY_FUNCTION_TO_STRING +# define JERRY_FUNCTION_TO_STRING 0 +#endif /* !defined (JERRY_FUNCTION_TO_STRING) */ + /** * Enable/Disable line-info management inside the engine. * @@ -634,6 +647,10 @@ || ((JERRY_LCACHE != 0) && (JERRY_LCACHE != 1)) # error "Invalid value for 'JERRY_LCACHE' macro." #endif +#if !defined (JERRY_FUNCTION_TO_STRING) \ +|| ((JERRY_FUNCTION_TO_STRING != 0) && (JERRY_FUNCTION_TO_STRING != 1)) +# error "Invalid value for 'JERRY_FUNCTION_TO_STRING' macro." +#endif #if !defined (JERRY_LINE_INFO) \ || ((JERRY_LINE_INFO != 0) && (JERRY_LINE_INFO != 1)) # error "Invalid value for 'JERRY_LINE_INFO' macro." diff --git a/jerry-core/ecma/base/ecma-extended-info.c b/jerry-core/ecma/base/ecma-extended-info.c new file mode 100644 index 0000000000..b5fabf5801 --- /dev/null +++ b/jerry-core/ecma/base/ecma-extended-info.c @@ -0,0 +1,152 @@ +/* Copyright JS Foundation and other contributors, http://js.foundation + * + * 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. + */ + +#include "byte-code.h" +#include "ecma-helpers.h" +#include "ecma-extended-info.h" + +#if JERRY_ESNEXT || JERRY_FUNCTION_TO_STRING + +/** \addtogroup ecma ECMA + * @{ + * + * \addtogroup ecmaextendedinfo Extended info + * @{ + */ + +/** + * Decodes an uint32_t number, and updates the buffer position. + * + * @return the decoded value + */ +uint32_t +ecma_extended_info_decode_vlq (uint8_t **buffer_p) /**< [in/out] target buffer */ +{ + uint8_t *source_p = *buffer_p; + uint32_t value = 0; + + do + { + source_p--; + value = (value << ECMA_EXTENDED_INFO_VLQ_SHIFT) | (*source_p & ECMA_EXTENDED_INFO_VLQ_MASK); + } + while (*source_p & ECMA_EXTENDED_INFO_VLQ_CONTINUE); + + *buffer_p = source_p; + return value; +} /* ecma_extended_info_decode_vlq */ + +/** + * Encodes an uint32_t number into a buffer. + */ +void +ecma_extended_info_encode_vlq (uint8_t **buffer_p, /**< target buffer */ + uint32_t value) /**< encoded value */ +{ + uint8_t *destination_p = *buffer_p - 1; + + if (value <= ECMA_EXTENDED_INFO_VLQ_MASK) + { + *destination_p = (uint8_t) value; + *buffer_p = destination_p; + return; + } + + uint32_t length = 0; + uint32_t current_value = value >> ECMA_EXTENDED_INFO_VLQ_SHIFT; + + do + { + current_value >>= ECMA_EXTENDED_INFO_VLQ_SHIFT; + length++; + } + while (current_value > 0); + + destination_p -= length; + *buffer_p = destination_p; + + do + { + *destination_p++ = (uint8_t) (value | ECMA_EXTENDED_INFO_VLQ_CONTINUE); + value >>= ECMA_EXTENDED_INFO_VLQ_SHIFT; + } + while (value > 0); + + **buffer_p &= ECMA_EXTENDED_INFO_VLQ_MASK; +} /* ecma_extended_info_encode_vlq */ + +/** + * Gets the encoded length of a number. + * + * @return encoded length + */ +uint32_t +ecma_extended_info_get_encoded_length (uint32_t value) /**< encoded value */ +{ + uint32_t length = 0; + + do + { + value >>= ECMA_EXTENDED_INFO_VLQ_SHIFT; + length++; + } + while (value > 0); + + return length; +} /* ecma_extended_info_get_encoded_length */ + +/** + * Get the extended info from a byte code + * + * @return pointer to the extended info + */ +uint8_t * +ecma_compiled_code_resolve_extended_info (const ecma_compiled_code_t *bytecode_header_p) /**< compiled code */ +{ + JERRY_ASSERT (bytecode_header_p != NULL); + JERRY_ASSERT (bytecode_header_p->status_flags & CBC_CODE_FLAGS_HAS_EXTENDED_INFO); + + ecma_value_t *base_p = ecma_compiled_code_resolve_arguments_start (bytecode_header_p); + +#if JERRY_ESNEXT + if (CBC_FUNCTION_GET_TYPE (bytecode_header_p->status_flags) != CBC_FUNCTION_CONSTRUCTOR) + { + base_p--; + } + + if (bytecode_header_p->status_flags & CBC_CODE_FLAGS_HAS_TAGGED_LITERALS) + { + base_p--; + } +#endif /* JERRY_ESNEXT */ + +#if JERRY_LINE_INFO + if (bytecode_header_p->status_flags & CBC_CODE_FLAGS_HAS_LINE_INFO) + { + base_p--; + } +#endif /* JERRY_LINE_INFO */ + + JERRY_ASSERT (((uint8_t *) base_p)[-1] != 0); + + return ((uint8_t *) base_p) - 1; +} /* ecma_compiled_code_resolve_extended_info */ + +#endif /* JERRY_ESNEXT || JERRY_FUNCTION_TO_STRING */ + +/** + * @} + * @} + */ diff --git a/jerry-core/ecma/base/ecma-extended-info.h b/jerry-core/ecma/base/ecma-extended-info.h new file mode 100644 index 0000000000..c9fc1c90ed --- /dev/null +++ b/jerry-core/ecma/base/ecma-extended-info.h @@ -0,0 +1,58 @@ +/* Copyright JS Foundation and other contributors, http://js.foundation + * + * 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. + */ + +#ifndef ECMA_EXTENDED_INFO_H +#define ECMA_EXTENDED_INFO_H + +/** \addtogroup ecma ECMA + * @{ + * + * \addtogroup ecmaextendedinfo Extended info + * @{ + */ + +#if JERRY_ESNEXT || JERRY_FUNCTION_TO_STRING + +#include "ecma-globals.h" + +/** + * Vlq encoding: flag which is set for all bytes except the last one. + */ +#define ECMA_EXTENDED_INFO_VLQ_CONTINUE 0x80 + +/** + * Vlq encoding: mask to decode the number fragment. + */ +#define ECMA_EXTENDED_INFO_VLQ_MASK 0x7f + +/** + * Vlq encoding: number of bits stored in a byte. + */ +#define ECMA_EXTENDED_INFO_VLQ_SHIFT 7 + +uint32_t ecma_extended_info_decode_vlq (uint8_t **buffer_p); +void ecma_extended_info_encode_vlq (uint8_t **buffer_p, uint32_t value); +uint32_t ecma_extended_info_get_encoded_length (uint32_t value); + +uint8_t *ecma_compiled_code_resolve_extended_info (const ecma_compiled_code_t *bytecode_header_p); + +#endif /* JERRY_ESNEXT || JERRY_FUNCTION_TO_STRING */ + +/** + * @} + * @} + */ + +#endif /* !ECMA_EXTENDED_INFO_H */ diff --git a/jerry-core/ecma/base/ecma-gc.c b/jerry-core/ecma/base/ecma-gc.c index 4ca9692b3e..ab6a603a9e 100644 --- a/jerry-core/ecma/base/ecma-gc.c +++ b/jerry-core/ecma/base/ecma-gc.c @@ -430,10 +430,10 @@ ecma_gc_mark_compiled_code (const ecma_compiled_code_t *compiled_code_p) /**< co if (CBC_SCRIPT_GET_TYPE (script_p) == CBC_SCRIPT_USER_OBJECT) { - cbc_script_user_t *script_user_p = (cbc_script_user_t *) script_p; + ecma_value_t user_value = CBC_SCRIPT_GET_USER_VALUE (script_p); - JERRY_ASSERT (ecma_is_value_object (script_user_p->user_value)); - ecma_gc_set_object_visited (ecma_get_object_from_value (script_user_p->user_value)); + JERRY_ASSERT (ecma_is_value_object (user_value)); + ecma_gc_set_object_visited (ecma_get_object_from_value (user_value)); } #if JERRY_BUILTIN_REALMS diff --git a/jerry-core/ecma/base/ecma-globals.h b/jerry-core/ecma/base/ecma-globals.h index 1ecfba9f66..72247db105 100644 --- a/jerry-core/ecma/base/ecma-globals.h +++ b/jerry-core/ecma/base/ecma-globals.h @@ -130,6 +130,9 @@ typedef enum #if JERRY_ESNEXT ECMA_PARSE_INTERNAL_PRE_SCANNING = (1u << 16), /**< the parser is in pre-scanning mode */ #endif /* JERRY_ESNEXT */ +#if JERRY_FUNCTION_TO_STRING + ECMA_PARSE_INTERNAL_HAS_4_BYTE_MARKER = (1u << 17), /**< source has 4 byte marker */ +#endif /* JERRY_FUNCTION_TO_STRING */ #ifndef JERRY_NDEBUG /** * This flag represents an error in for in/of statements, which cannot be set diff --git a/jerry-core/ecma/base/ecma-helpers-string.c b/jerry-core/ecma/base/ecma-helpers-string.c index 0fefdc1e7d..aa9dfa4fa0 100644 --- a/jerry-core/ecma/base/ecma-helpers-string.c +++ b/jerry-core/ecma/base/ecma-helpers-string.c @@ -443,7 +443,7 @@ ecma_new_ecma_string_from_utf8_converted_to_cesu8 (const lit_utf8_byte_t *string converted_string_size += string_size; - JERRY_ASSERT (lit_is_valid_utf8_string (string_p, string_size)); + JERRY_ASSERT (lit_is_valid_utf8_string (string_p, string_size, false)); lit_utf8_byte_t *data_p; ecma_string_t *string_desc_p = ecma_new_ecma_string_from_utf8_buffer (converted_string_length, diff --git a/jerry-core/ecma/base/ecma-helpers.c b/jerry-core/ecma/base/ecma-helpers.c index 9d3a3db8ed..70c759f242 100644 --- a/jerry-core/ecma/base/ecma-helpers.c +++ b/jerry-core/ecma/base/ecma-helpers.c @@ -1527,14 +1527,14 @@ ecma_bytecode_deref (ecma_compiled_code_t *bytecode_p) /**< byte code pointer */ if (type != CBC_SCRIPT_GENERIC) { - script_size = sizeof (cbc_script_user_t); + script_size += sizeof (ecma_value_t); if (type == CBC_SCRIPT_USER_VALUE) { - cbc_script_user_t *script_user_p = (cbc_script_user_t *) script_p; + ecma_value_t user_value = CBC_SCRIPT_GET_USER_VALUE (script_p); - JERRY_ASSERT (!ecma_is_value_object (script_user_p->user_value)); - ecma_free_value (script_user_p->user_value); + JERRY_ASSERT (!ecma_is_value_object (user_value)); + ecma_free_value (user_value); } } @@ -1542,6 +1542,16 @@ ecma_bytecode_deref (ecma_compiled_code_t *bytecode_p) /**< byte code pointer */ ecma_deref_ecma_string (ecma_get_string_from_value (script_p->resource_name)); #endif /* JERRY_RESOURCE_NAME */ +#if JERRY_FUNCTION_TO_STRING + ecma_deref_ecma_string (ecma_get_string_from_value (script_p->source_code)); + + if (script_p->refs_and_type & CBC_SCRIPT_HAS_FUNCTION_ARGUMENTS) + { + ecma_deref_ecma_string (ecma_get_string_from_value (CBC_SCRIPT_GET_FUNCTION_ARGUMENTS (script_p, type))); + script_size += sizeof (ecma_value_t); + } +#endif /* JERRY_FUNCTION_TO_STRING */ + jmem_heap_free_block (script_p, script_size); } @@ -1728,21 +1738,6 @@ ecma_compiled_code_resolve_function_name (const ecma_compiled_code_t *bytecode_h return base_p; } /* ecma_compiled_code_resolve_function_name */ -/** - * Get the extended info from a byte code - * - * @return extended info value - */ -uint32_t -ecma_compiled_code_resolve_extended_info (const ecma_compiled_code_t *bytecode_header_p) /**< compiled code */ -{ - JERRY_ASSERT (bytecode_header_p != NULL); - JERRY_ASSERT (bytecode_header_p->status_flags & CBC_CODE_FLAGS_HAS_EXTENDED_INFO); - - ecma_value_t *base_p = ecma_compiled_code_resolve_function_name (bytecode_header_p); - return base_p[-1]; -} /* ecma_compiled_code_resolve_extended_info */ - /** * Get the tagged template collection of the compiled code * @@ -1755,9 +1750,7 @@ ecma_compiled_code_get_tagged_template_collection (const ecma_compiled_code_t *b JERRY_ASSERT (bytecode_header_p->status_flags & CBC_CODE_FLAGS_HAS_TAGGED_LITERALS); ecma_value_t *base_p = ecma_compiled_code_resolve_function_name (bytecode_header_p); - int offset = (bytecode_header_p->status_flags & CBC_CODE_FLAGS_HAS_EXTENDED_INFO) ? -2 : -1; - - return ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, base_p[offset]); + return ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, base_p[-1]); } /* ecma_compiled_code_get_tagged_template_collection */ #endif /* JERRY_ESNEXT */ @@ -1783,11 +1776,6 @@ ecma_compiled_code_get_line_info (const ecma_compiled_code_t *bytecode_header_p) base_p--; } - if (bytecode_header_p->status_flags & CBC_CODE_FLAGS_HAS_EXTENDED_INFO) - { - base_p--; - } - if (bytecode_header_p->status_flags & CBC_CODE_FLAGS_HAS_TAGGED_LITERALS) { base_p--; diff --git a/jerry-core/ecma/base/ecma-helpers.h b/jerry-core/ecma/base/ecma-helpers.h index a4349536be..e8f64ad6ba 100644 --- a/jerry-core/ecma/base/ecma-helpers.h +++ b/jerry-core/ecma/base/ecma-helpers.h @@ -539,7 +539,6 @@ const ecma_compiled_code_t *ecma_bytecode_get_from_value (ecma_value_t value); ecma_value_t *ecma_compiled_code_resolve_arguments_start (const ecma_compiled_code_t *bytecode_header_p); #if JERRY_ESNEXT ecma_value_t *ecma_compiled_code_resolve_function_name (const ecma_compiled_code_t *bytecode_header_p); -uint32_t ecma_compiled_code_resolve_extended_info (const ecma_compiled_code_t *bytecode_header_p); ecma_collection_t *ecma_compiled_code_get_tagged_template_collection (const ecma_compiled_code_t *bytecode_header_p); #endif /* JERRY_ESNEXT */ #if JERRY_LINE_INFO diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-function-prototype.c b/jerry-core/ecma/builtin-objects/ecma-builtin-function-prototype.c index 8837938594..c3ddd8cf47 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-function-prototype.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-function-prototype.c @@ -18,6 +18,7 @@ #include "ecma-builtins.h" #include "ecma-conversion.h" #include "ecma-exceptions.h" +#include "ecma-extended-info.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" @@ -79,9 +80,116 @@ enum * Returned value must be freed with ecma_free_value. */ static ecma_value_t -ecma_builtin_function_prototype_object_to_string (void) +ecma_builtin_function_prototype_object_to_string (ecma_object_t *func_obj_p) /**< this argument object */ { - return ecma_make_magic_string_value (LIT_MAGIC_STRING__FUNCTION_TO_STRING); + if (ecma_get_object_type (func_obj_p) != ECMA_OBJECT_TYPE_FUNCTION) + { + return ecma_make_magic_string_value (LIT_MAGIC_STRING_FUNCTION_TO_STRING_NATIVE); + } + +#if JERRY_FUNCTION_TO_STRING + const ecma_compiled_code_t *bytecode_p; + bytecode_p = ecma_op_function_get_compiled_code ((ecma_extended_object_t *) func_obj_p); + + ecma_value_t script_value = ((cbc_uint8_arguments_t *) bytecode_p)->script_value; + cbc_script_t *script_p = ECMA_GET_INTERNAL_VALUE_POINTER (cbc_script_t, script_value); + + if (bytecode_p->status_flags & CBC_CODE_FLAGS_HAS_EXTENDED_INFO) + { + uint8_t *extended_info_p = ecma_compiled_code_resolve_extended_info (bytecode_p); + uint8_t extended_info = *extended_info_p; + + if (extended_info & CBC_EXTENDED_CODE_FLAGS_HAS_SOURCE_CODE_RANGE) + { +#if JERRY_ESNEXT + if (extended_info & CBC_EXTENDED_CODE_FLAGS_HAS_ARGUMENT_LENGTH) + { + ecma_extended_info_decode_vlq (&extended_info_p); + } +#endif /* JERRY_ESNEXT */ + + uint32_t range_start = ecma_extended_info_decode_vlq (&extended_info_p); + uint32_t range_size = ecma_extended_info_decode_vlq (&extended_info_p); + ecma_value_t source_code; + + if (!(extended_info & CBC_EXTENDED_CODE_FLAGS_SOURCE_CODE_IN_ARGUMENTS)) + { + source_code = script_p->source_code; +#if JERRY_SNAPSHOT_EXEC + if (ecma_is_value_magic_string (source_code, LIT_MAGIC_STRING__EMPTY)) + { + return ecma_make_magic_string_value (LIT_MAGIC_STRING_FUNCTION_TO_STRING_ECMA); + } +#endif /* JERRY_SNAPSHOT_EXEC */ + } + else + { +#if JERRY_SNAPSHOT_EXEC + if (!(script_p->refs_and_type & CBC_SCRIPT_HAS_FUNCTION_ARGUMENTS)) + { + return ecma_make_magic_string_value (LIT_MAGIC_STRING_FUNCTION_TO_STRING_ECMA); + } +#else /* !JERRY_SNAPSHOT_EXEC */ + JERRY_ASSERT (script_p->refs_and_type & CBC_SCRIPT_HAS_FUNCTION_ARGUMENTS); +#endif /* JERRY_SNAPSHOT_EXEC */ + + source_code = CBC_SCRIPT_GET_FUNCTION_ARGUMENTS (script_p, CBC_SCRIPT_GET_TYPE (script_p)); + } + + ecma_string_t *result_string_p; + + ECMA_STRING_TO_UTF8_STRING (ecma_get_string_from_value (source_code), source_p, source_size); + result_string_p = ecma_new_ecma_string_from_utf8 (source_p + range_start, range_size); + ECMA_FINALIZE_UTF8_STRING (source_p, source_size); + + return ecma_make_string_value (result_string_p); + } + } + +#if JERRY_SNAPSHOT_EXEC + if (!(script_p->refs_and_type & CBC_SCRIPT_HAS_FUNCTION_ARGUMENTS)) + { + return ecma_make_magic_string_value (LIT_MAGIC_STRING_FUNCTION_TO_STRING_ECMA); + } +#else /* !JERRY_SNAPSHOT_EXEC */ + JERRY_ASSERT (script_p->refs_and_type & CBC_SCRIPT_HAS_FUNCTION_ARGUMENTS); +#endif /* JERRY_SNAPSHOT_EXEC */ + + lit_magic_string_id_t header_id = LIT_MAGIC_STRING_FUNCTION_TO_STRING_ANON; + +#if JERRY_ESNEXT + switch (CBC_FUNCTION_GET_TYPE (bytecode_p->status_flags)) + { + case CBC_FUNCTION_GENERATOR: + { + header_id = LIT_MAGIC_STRING_FUNCTION_TO_STRING_ANON_GENERATOR; + break; + } + case CBC_FUNCTION_ASYNC_GENERATOR: + { + header_id = LIT_MAGIC_STRING_FUNCTION_TO_STRING_ANON_ASYNC_GENERATOR; + break; + } + case CBC_FUNCTION_ASYNC: + { + header_id = LIT_MAGIC_STRING_FUNCTION_TO_STRING_ANON_ASYNC; + break; + } + } +#endif /* JERRY_ESNEXT */ + + ecma_stringbuilder_t builder = ecma_stringbuilder_create_from (ecma_get_magic_string (header_id)); + ecma_value_t function_arguments = CBC_SCRIPT_GET_FUNCTION_ARGUMENTS (script_p, CBC_SCRIPT_GET_TYPE (script_p)); + + ecma_stringbuilder_append (&builder, ecma_get_string_from_value (function_arguments)); + ecma_stringbuilder_append_raw (&builder, (const lit_utf8_byte_t *) "\n) {\n", 5); + ecma_stringbuilder_append (&builder, ecma_get_string_from_value (script_p->source_code)); + ecma_stringbuilder_append_raw (&builder, (const lit_utf8_byte_t *) "\n}", 2); + + return ecma_make_string_value (ecma_stringbuilder_finalize (&builder)); +#else /* !JERRY_FUNCTION_TO_STRING */ + return ecma_make_magic_string_value (LIT_MAGIC_STRING_FUNCTION_TO_STRING_ECMA); +#endif /* JERRY_FUNCTION_TO_STRING */ } /* ecma_builtin_function_prototype_object_to_string */ /** @@ -440,7 +548,7 @@ ecma_builtin_function_prototype_dispatch_routine (uint8_t builtin_routine_id, /* { case ECMA_FUNCTION_PROTOTYPE_TO_STRING: { - return ecma_builtin_function_prototype_object_to_string (); + return ecma_builtin_function_prototype_object_to_string (func_obj_p); } case ECMA_FUNCTION_PROTOTYPE_APPLY: { diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-global.c b/jerry-core/ecma/builtin-objects/ecma-builtin-global.c index 187dd2eea9..34570ecb0f 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-global.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-global.c @@ -284,7 +284,7 @@ ecma_builtin_global_object_decode_uri_helper (lit_utf8_byte_t *input_start_p, /* } if (!is_valid - || !lit_is_valid_utf8_string (octets, bytes_count)) + || !lit_is_valid_utf8_string (octets, bytes_count, true)) { ecma_stringbuilder_destroy (&builder); return ecma_raise_uri_error (ECMA_ERR_MSG ("Invalid UTF8 string")); diff --git a/jerry-core/ecma/operations/ecma-function-object.c b/jerry-core/ecma/operations/ecma-function-object.c index cfe96aba10..f15a225d3b 100644 --- a/jerry-core/ecma/operations/ecma-function-object.c +++ b/jerry-core/ecma/operations/ecma-function-object.c @@ -17,6 +17,7 @@ #include "ecma-builtin-helpers.h" #include "ecma-builtin-handlers.h" #include "ecma-exceptions.h" +#include "ecma-extended-info.h" #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-helpers.h" @@ -1739,11 +1740,7 @@ ecma_op_function_try_to_lazy_instantiate_property (ecma_object_t *object_p, /**< const ecma_compiled_code_t *bytecode_data_p = ecma_op_function_get_compiled_code (ext_func_p); uint32_t len; - if (bytecode_data_p->status_flags & CBC_CODE_FLAGS_HAS_EXTENDED_INFO) - { - len = CBC_EXTENDED_INFO_GET_LENGTH (ecma_compiled_code_resolve_extended_info (bytecode_data_p)); - } - else if (bytecode_data_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) + if (bytecode_data_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) { cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) bytecode_data_p; len = args_p->argument_end; @@ -1754,6 +1751,16 @@ ecma_op_function_try_to_lazy_instantiate_property (ecma_object_t *object_p, /**< len = args_p->argument_end; } + if (bytecode_data_p->status_flags & CBC_CODE_FLAGS_HAS_EXTENDED_INFO) + { + uint8_t *extended_info_p = ecma_compiled_code_resolve_extended_info (bytecode_data_p); + + if (*extended_info_p & CBC_EXTENDED_CODE_FLAGS_HAS_ARGUMENT_LENGTH) + { + len = ecma_extended_info_decode_vlq (&extended_info_p); + } + } + /* Set tag bit to represent initialized 'length' property */ ECMA_SET_FIRST_BIT_TO_POINTER_TAG (ext_func_p->u.function.scope_cp); ecma_property_t *value_prop_p; diff --git a/jerry-core/include/jerryscript-snapshot.h b/jerry-core/include/jerryscript-snapshot.h index 27db4c6613..0ac226d6d9 100644 --- a/jerry-core/include/jerryscript-snapshot.h +++ b/jerry-core/include/jerryscript-snapshot.h @@ -30,7 +30,7 @@ extern "C" /** * Jerry snapshot format version. */ -#define JERRY_SNAPSHOT_VERSION (67u) +#define JERRY_SNAPSHOT_VERSION (68u) /** * Flags for jerry_generate_snapshot and jerry_generate_function_snapshot. diff --git a/jerry-core/include/jerryscript-types.h b/jerry-core/include/jerryscript-types.h index 505a098e76..50966d13ce 100644 --- a/jerry-core/include/jerryscript-types.h +++ b/jerry-core/include/jerryscript-types.h @@ -112,6 +112,7 @@ typedef enum JERRY_FEATURE_PROMISE_CALLBACK, /**< Promise callback support */ JERRY_FEATURE_MODULE, /**< Module support */ JERRY_FEATURE_WEAKREF, /**< WeakRef support */ + JERRY_FEATURE_FUNCTION_TO_STRING, /**< function toString support */ JERRY_FEATURE__COUNT /**< number of features. NOTE: must be at the end of the list */ } jerry_feature_t; diff --git a/jerry-core/lit/lit-magic-strings.inc.h b/jerry-core/lit/lit-magic-strings.inc.h index 7744bf370f..b663a50863 100644 --- a/jerry-core/lit/lit-magic-strings.inc.h +++ b/jerry-core/lit/lit-magic-strings.inc.h @@ -1003,7 +1003,13 @@ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SET_UTC_MILLISECONDS_UL, "setUTCMilliseco LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_TO_LOCALE_DATE_STRING_UL, "toLocaleDateString") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_TO_LOCALE_TIME_STRING_UL, "toLocaleTimeString") #endif +#if JERRY_FUNCTION_TO_STRING +LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_FUNCTION_TO_STRING_ANON, "function anonymous(") +#endif LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_OWN_PROPERTY_NAMES_UL, "getOwnPropertyNames") +#if JERRY_ESNEXT && JERRY_FUNCTION_TO_STRING +LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_FUNCTION_TO_STRING_ANON_GENERATOR, "function* anonymous(") +#endif LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_PROPERTY_IS_ENUMERABLE_UL, "propertyIsEnumerable") #if JERRY_ESNEXT LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_OWN_PROPERTY_SYMBOLS_UL, "getOwnPropertySymbols") @@ -1011,10 +1017,20 @@ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ASYNC_GENERATOR_FUNCTION_UL, "AsyncGenera LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_REGEXP_STRING_ITERATOR_UL, "RegExp String Iterator") #endif LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_OWN_PROPERTY_DESCRIPTOR_UL, "getOwnPropertyDescriptor") +#if JERRY_ESNEXT && JERRY_FUNCTION_TO_STRING +LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_FUNCTION_TO_STRING_ANON_ASYNC, "async function anonymous(") +#endif #if JERRY_ESNEXT LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_OWN_PROPERTY_DESCRIPTORS_UL, "getOwnPropertyDescriptors") #endif -LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING__FUNCTION_TO_STRING, "function(){/* ecmascript */}") +#if JERRY_ESNEXT && JERRY_FUNCTION_TO_STRING +LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_FUNCTION_TO_STRING_ANON_ASYNC_GENERATOR, "async function* anonymous(") +#endif +LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_FUNCTION_TO_STRING_NATIVE, "function () { [native code] }") +#if JERRY_FUNCTION_TO_STRING && JERRY_SNAPSHOT_EXEC \ +|| !(JERRY_FUNCTION_TO_STRING) +LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_FUNCTION_TO_STRING_ECMA, "function () { /* ecmascript */ }") +#endif LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (0, LIT_MAGIC_STRING__EMPTY) #if JERRY_ESNEXT @@ -1213,8 +1229,16 @@ LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (17, LIT_MAGIC_STRING_GET_TIMEZONE_OFFSE LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (17, LIT_MAGIC_STRING_PREVENT_EXTENSIONS_UL) #endif LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (18, LIT_MAGIC_STRING_DECODE_URI_COMPONENT) +#if JERRY_FUNCTION_TO_STRING +LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (19, LIT_MAGIC_STRING_FUNCTION_TO_STRING_ANON) +#else LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (19, LIT_MAGIC_STRING_GET_OWN_PROPERTY_NAMES_UL) +#endif +#if JERRY_ESNEXT && JERRY_FUNCTION_TO_STRING +LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (20, LIT_MAGIC_STRING_FUNCTION_TO_STRING_ANON_GENERATOR) +#else LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (20, LIT_MAGIC_STRING_PROPERTY_IS_ENUMERABLE_UL) +#endif #if JERRY_ESNEXT LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (21, LIT_MAGIC_STRING_GET_OWN_PROPERTY_SYMBOLS_UL) #else @@ -1227,11 +1251,32 @@ LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (22, LIT_MAGIC_STRING_GET_OWN_PROPERTY_D #endif LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (23, LIT_MAGIC_STRING_GET_OWN_PROPERTY_DESCRIPTOR_UL) LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (24, LIT_MAGIC_STRING_GET_OWN_PROPERTY_DESCRIPTOR_UL) -#if JERRY_ESNEXT +#if JERRY_ESNEXT && JERRY_FUNCTION_TO_STRING +LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (25, LIT_MAGIC_STRING_FUNCTION_TO_STRING_ANON_ASYNC) +#elif JERRY_ESNEXT LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (25, LIT_MAGIC_STRING_GET_OWN_PROPERTY_DESCRIPTORS_UL) +#elif JERRY_ESNEXT && JERRY_FUNCTION_TO_STRING +LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (25, LIT_MAGIC_STRING_FUNCTION_TO_STRING_ANON_ASYNC_GENERATOR) +#else +LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (25, LIT_MAGIC_STRING_FUNCTION_TO_STRING_NATIVE) +#endif +#if JERRY_ESNEXT && JERRY_FUNCTION_TO_STRING +LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (26, LIT_MAGIC_STRING_FUNCTION_TO_STRING_ANON_ASYNC_GENERATOR) #else -LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (25, LIT_MAGIC_STRING__FUNCTION_TO_STRING) +LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (26, LIT_MAGIC_STRING_FUNCTION_TO_STRING_NATIVE) +#endif +LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (27, LIT_MAGIC_STRING_FUNCTION_TO_STRING_NATIVE) +LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (28, LIT_MAGIC_STRING_FUNCTION_TO_STRING_NATIVE) +LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (29, LIT_MAGIC_STRING_FUNCTION_TO_STRING_NATIVE) +#if JERRY_FUNCTION_TO_STRING && JERRY_SNAPSHOT_EXEC \ +|| !(JERRY_FUNCTION_TO_STRING) +LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (30, LIT_MAGIC_STRING_FUNCTION_TO_STRING_ECMA) +#endif +#if JERRY_FUNCTION_TO_STRING && JERRY_SNAPSHOT_EXEC \ +|| !(JERRY_FUNCTION_TO_STRING) +LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (31, LIT_MAGIC_STRING_FUNCTION_TO_STRING_ECMA) +#endif +#if JERRY_FUNCTION_TO_STRING && JERRY_SNAPSHOT_EXEC \ +|| !(JERRY_FUNCTION_TO_STRING) +LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (32, LIT_MAGIC_STRING_FUNCTION_TO_STRING_ECMA) #endif -LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (26, LIT_MAGIC_STRING__FUNCTION_TO_STRING) -LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (27, LIT_MAGIC_STRING__FUNCTION_TO_STRING) -LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (28, LIT_MAGIC_STRING__FUNCTION_TO_STRING) diff --git a/jerry-core/lit/lit-magic-strings.ini b/jerry-core/lit/lit-magic-strings.ini index a52e429ffc..8f311d1040 100644 --- a/jerry-core/lit/lit-magic-strings.ini +++ b/jerry-core/lit/lit-magic-strings.ini @@ -406,10 +406,15 @@ LIT_MAGIC_STRING_SET_UTC_MILLISECONDS_UL = "setUTCMilliseconds" LIT_MAGIC_STRING_TO_LOCALE_DATE_STRING_UL = "toLocaleDateString" LIT_MAGIC_STRING_TO_LOCALE_TIME_STRING_UL = "toLocaleTimeString" LIT_MAGIC_STRING_GET_OWN_PROPERTY_NAMES_UL = "getOwnPropertyNames" +LIT_MAGIC_STRING_FUNCTION_TO_STRING_ANON = "function anonymous(" +LIT_MAGIC_STRING_FUNCTION_TO_STRING_ANON_GENERATOR = "function* anonymous(" LIT_MAGIC_STRING_PROPERTY_IS_ENUMERABLE_UL = "propertyIsEnumerable" LIT_MAGIC_STRING_GET_OWN_PROPERTY_SYMBOLS_UL = "getOwnPropertySymbols" LIT_MAGIC_STRING_REGEXP_STRING_ITERATOR_UL = "RegExp String Iterator" LIT_MAGIC_STRING_ASYNC_GENERATOR_FUNCTION_UL = "AsyncGeneratorFunction" LIT_MAGIC_STRING_GET_OWN_PROPERTY_DESCRIPTOR_UL = "getOwnPropertyDescriptor" +LIT_MAGIC_STRING_FUNCTION_TO_STRING_ANON_ASYNC = "async function anonymous(" LIT_MAGIC_STRING_GET_OWN_PROPERTY_DESCRIPTORS_UL = "getOwnPropertyDescriptors" -LIT_MAGIC_STRING__FUNCTION_TO_STRING = "function(){/* ecmascript */}" +LIT_MAGIC_STRING_FUNCTION_TO_STRING_ANON_ASYNC_GENERATOR = "async function* anonymous(" +LIT_MAGIC_STRING_FUNCTION_TO_STRING_NATIVE = "function () { [native code] }" +LIT_MAGIC_STRING_FUNCTION_TO_STRING_ECMA = "function () { /* ecmascript */ }" diff --git a/jerry-core/lit/lit-strings.c b/jerry-core/lit/lit-strings.c index c2e6d929da..81d4ac0720 100644 --- a/jerry-core/lit/lit-strings.c +++ b/jerry-core/lit/lit-strings.c @@ -22,14 +22,14 @@ * * NOTE: * Isolated surrogates are allowed. - * Correct pair of surrogates is not allowed, it should be represented as 4-byte utf-8 character. * * @return true if utf-8 string is well-formed * false otherwise */ bool lit_is_valid_utf8_string (const lit_utf8_byte_t *utf8_buf_p, /**< utf-8 string */ - lit_utf8_size_t buf_size) /**< string size */ + lit_utf8_size_t buf_size, /**< string size */ + bool is_strict) /**< true if surrogate pairs are not allowed */ { lit_utf8_size_t idx = 0; @@ -95,21 +95,22 @@ lit_is_valid_utf8_string (const lit_utf8_byte_t *utf8_buf_p, /**< utf-8 string * return false; } - if (code_point >= LIT_UTF16_HIGH_SURROGATE_MIN - && code_point <= LIT_UTF16_HIGH_SURROGATE_MAX) - { - is_prev_code_point_high_surrogate = true; - } - else if (code_point >= LIT_UTF16_LOW_SURROGATE_MIN - && code_point <= LIT_UTF16_LOW_SURROGATE_MAX - && is_prev_code_point_high_surrogate) - { - /* sequence of high and low surrogate is not allowed */ - return false; - } - else + if (is_strict) { is_prev_code_point_high_surrogate = false; + + if (code_point >= LIT_UTF16_HIGH_SURROGATE_MIN + && code_point <= LIT_UTF16_HIGH_SURROGATE_MAX) + { + is_prev_code_point_high_surrogate = true; + } + else if (code_point >= LIT_UTF16_LOW_SURROGATE_MIN + && code_point <= LIT_UTF16_LOW_SURROGATE_MAX + && is_prev_code_point_high_surrogate) + { + /* sequence of high and low surrogate is not allowed */ + return false; + } } idx += extra_bytes_count; diff --git a/jerry-core/lit/lit-strings.h b/jerry-core/lit/lit-strings.h index 7ba61a2c06..0c18da4cb3 100644 --- a/jerry-core/lit/lit-strings.h +++ b/jerry-core/lit/lit-strings.h @@ -84,7 +84,7 @@ #define LIT_UTF8_FIRST_BYTE_MAX (0xF8) /* validation */ -bool lit_is_valid_utf8_string (const lit_utf8_byte_t *utf8_buf_p, lit_utf8_size_t buf_size); +bool lit_is_valid_utf8_string (const lit_utf8_byte_t *utf8_buf_p, lit_utf8_size_t buf_size, bool strict); bool lit_is_valid_cesu8_string (const lit_utf8_byte_t *cesu8_buf_p, lit_utf8_size_t buf_size); /* checks */ diff --git a/jerry-core/parser/js/byte-code.h b/jerry-core/parser/js/byte-code.h index 7f3e7ffdc5..1f9f0321a4 100644 --- a/jerry-core/parser/js/byte-code.h +++ b/jerry-core/parser/js/byte-code.h @@ -895,6 +895,27 @@ typedef enum check a range of types without decoding the actual type. */ } cbc_code_flags_t; +/** + * Optional byte code fields. These fields are stored in a reversed + * order from the end of the byte code data. + * + * Value fields: + * - when CBC_CODE_FLAGS_MAPPED_ARGUMENTS_NEEDED is set: + * argument_end number of argument names encoded as strings + * - when function type is not CBC_FUNCTION_CONSTRUCTOR: + * function name encoded as string + * - when CBC_CODE_FLAGS_HAS_TAGGED_LITERALS is set: + * pointer to the tagged template collection encoded as value + * + * Byte fields when CBC_CODE_FLAGS_HAS_EXTENDED_INFO is set: + * - always available: + * a byte which contains a combination of CBC_EXTENDED_CODE_FLAGS bits + * - when CBC_EXTENDED_CODE_FLAGS_HAS_ARGUMENT_LENGTH is set: + * a vlq encoded default value for function length + * - when CBC_EXTENDED_CODE_FLAGS_HAS_SOURCE_CODE_RANGE is set: + * a pair of vlq encoded values, representing the start and size of the range + */ + /** * Compact byte code function types. */ @@ -962,9 +983,15 @@ typedef enum ((flags) >= (CBC_FUNCTION_ARROW << CBC_FUNCTION_TYPE_SHIFT)) /** - * Get length property from extended info + * Compact byte code extended status flags. */ -#define CBC_EXTENDED_INFO_GET_LENGTH(extended_info) (extended_info) +typedef enum +{ + CBC_EXTENDED_CODE_FLAGS_HAS_ARGUMENT_LENGTH = (1u << 0), /**< has argument length */ + CBC_EXTENDED_CODE_FLAGS_HAS_SOURCE_CODE_RANGE = (1u << 1), /**< has source code range (start, end) */ + CBC_EXTENDED_CODE_FLAGS_SOURCE_CODE_IN_ARGUMENTS = (1u << 2), /**< source code range is inside + * the function arguments */ +} cbc_extended_code_flags_t; /** * Shared script data. @@ -977,20 +1004,25 @@ typedef enum } cbc_script_type; /** - * Value for increasing or decreasing the script reference counter. + * Script is a function with arguments source code. */ -#define CBC_SCRIPT_REF_ONE 0x4 +#define CBC_SCRIPT_HAS_FUNCTION_ARGUMENTS 0x4 /** - * Get the type of a script. + * Value for increasing or decreasing the script reference counter. */ -#define CBC_SCRIPT_GET_TYPE(script_p) ((script_p)->refs_and_type & (CBC_SCRIPT_REF_ONE - 1)) +#define CBC_SCRIPT_REF_ONE 0x8 /** * Maximum value of script reference counter. */ #define CBC_SCRIPT_REF_MAX (UINT32_MAX - CBC_SCRIPT_REF_ONE + 1) +/** + * Get the type of a script. + */ +#define CBC_SCRIPT_GET_TYPE(script_p) ((script_p)->refs_and_type & 0x3) + /** * Sets the type of a script using the user_value. */ @@ -1018,16 +1050,30 @@ typedef struct #if JERRY_RESOURCE_NAME ecma_value_t resource_name; /**< resource name */ #endif /* JERRY_RESOURCE_NAME */ +#if JERRY_FUNCTION_TO_STRING + ecma_value_t source_code; /**< source code */ +#endif /* JERRY_FUNCTION_TO_STRING */ } cbc_script_t; /** - * Script data with user value. + * Get the array of optional values assigned to a script. + * + * First value: user value + * Second value: function arguments value */ -typedef struct -{ - cbc_script_t header; /**< script header */ - ecma_value_t user_value; /**< user value */ -} cbc_script_user_t; +#define CBC_SCRIPT_GET_OPTIONAL_VALUES(script_p) ((ecma_value_t *) ((script_p) + 1)) + +/** + * Get user value. + */ +#define CBC_SCRIPT_GET_USER_VALUE(script_p) \ + (CBC_SCRIPT_GET_OPTIONAL_VALUES (script_p)[0]) + +/** + * Get function arguments. + */ +#define CBC_SCRIPT_GET_FUNCTION_ARGUMENTS(script_p, type) \ + (CBC_SCRIPT_GET_OPTIONAL_VALUES (script_p)[(type) != CBC_SCRIPT_GENERIC ? 1 : 0]) #define CBC_OPCODE(arg1, arg2, arg3, arg4) arg1, diff --git a/jerry-core/parser/js/common.c b/jerry-core/parser/js/common.c index 6985d77241..cf241a12f0 100644 --- a/jerry-core/parser/js/common.c +++ b/jerry-core/parser/js/common.c @@ -15,6 +15,7 @@ #include "common.h" #include "ecma-helpers.h" +#include "ecma-extended-info.h" #include "ecma-big-uint.h" #include "ecma-bigint.h" #include "js-parser-internal.h" @@ -396,16 +397,31 @@ util_print_cbc (ecma_compiled_code_t *compiled_code_p) /**< compiled code */ JERRY_DEBUG_MSG (" Const literal range end: %d\n", (int) const_literal_end); JERRY_DEBUG_MSG (" Literal range end: %d\n\n", (int) literal_end); -#if JERRY_ESNEXT +#if JERRY_ESNEXT || JERRY_FUNCTION_TO_STRING if (compiled_code_p->status_flags & CBC_CODE_FLAGS_HAS_EXTENDED_INFO) { - uint32_t extended_info = ecma_compiled_code_resolve_extended_info (compiled_code_p); + uint8_t *extended_info_p = ecma_compiled_code_resolve_extended_info (compiled_code_p); + uint8_t *extended_info_start_p = extended_info_p + sizeof (uint8_t); + uint8_t extended_info = *extended_info_p; - JERRY_DEBUG_MSG (" [Extended] Argument length: %d\n\n", (int) CBC_EXTENDED_INFO_GET_LENGTH (extended_info)); + if (extended_info & CBC_EXTENDED_CODE_FLAGS_HAS_ARGUMENT_LENGTH) + { + uint32_t argument_length = ecma_extended_info_decode_vlq (&extended_info_p); + JERRY_DEBUG_MSG (" [Extended] Argument length: %d\n", (int) argument_length); + } - size -= sizeof (ecma_value_t); + if (extended_info & CBC_EXTENDED_CODE_FLAGS_HAS_SOURCE_CODE_RANGE) + { + uint32_t range_start = ecma_extended_info_decode_vlq (&extended_info_p); + uint32_t range_end = ecma_extended_info_decode_vlq (&extended_info_p) + range_start; + JERRY_DEBUG_MSG (" [Extended] Source code range: %d - %d\n", (int) range_start, (int) range_end); + } + + JERRY_DEBUG_MSG ("\n"); + + size -= (size_t) (extended_info_start_p - extended_info_p); } -#endif /* JERRY_ESNEXT */ +#endif /* JERRY_ESNEXT || JERRY_FUNCTION_TO_STRING */ byte_code_start_p = (uint8_t *) compiled_code_p; diff --git a/jerry-core/parser/js/js-lexer.c b/jerry-core/parser/js/js-lexer.c index fe8d5a747d..98dec512e1 100644 --- a/jerry-core/parser/js/js-lexer.c +++ b/jerry-core/parser/js/js-lexer.c @@ -741,6 +741,9 @@ lexer_parse_identifier (parser_context_t *context_p, /**< context */ { decoded_length = 2 * 3; status_flags = LEXER_LIT_LOCATION_HAS_ESCAPE; +#if JERRY_FUNCTION_TO_STRING + context_p->global_status_flags |= ECMA_PARSE_INTERNAL_HAS_4_BYTE_MARKER; +#endif /* JERRY_FUNCTION_TO_STRING */ } #else /* !JERRY_ESNEXT */ if (code_point < LIT_UTF8_4_BYTE_MARKER) @@ -1171,6 +1174,9 @@ lexer_parse_string (parser_context_t *context_p, /**< context */ raw_length_adjust += 2; #endif /* JERRY_ESNEXT */ column++; +#if JERRY_FUNCTION_TO_STRING + context_p->global_status_flags |= ECMA_PARSE_INTERNAL_HAS_4_BYTE_MARKER; +#endif /* JERRY_FUNCTION_TO_STRING */ continue; } else if (*source_p == LIT_CHAR_TAB) @@ -1605,6 +1611,11 @@ lexer_next_token (parser_context_t *context_p) /**< context */ { size_t length; +#if JERRY_ESNEXT && JERRY_FUNCTION_TO_STRING + /* Needed by arrow functions with expression body */ + context_p->function_end_p = context_p->source_p; +#endif /* JERRY_ESNEXT && JERRY_FUNCTION_TO_STRING */ + lexer_skip_spaces (context_p); context_p->token.keyword_type = LEXER_EOS; @@ -3069,7 +3080,7 @@ lexer_construct_regexp_object (parser_context_t *context_p, /**< context */ } else { - JERRY_ASSERT (lit_is_valid_utf8_string (regex_start_p, length)); + JERRY_ASSERT (lit_is_valid_utf8_string (regex_start_p, length, false)); pattern_str_p = ecma_new_ecma_string_from_utf8_converted_to_cesu8 (regex_start_p, length); } @@ -3189,6 +3200,13 @@ lexer_expect_object_literal_id (parser_context_t *context_p, /**< context */ JERRY_ASSERT ((ident_opts & LEXER_OBJ_IDENT_CLASS_IDENTIFIER) || !(ident_opts & LEXER_OBJ_IDENT_CLASS_NO_STATIC)); +#if JERRY_FUNCTION_TO_STRING + if (ident_opts & LEXER_OBJ_IDENT_SET_FUNCTION_START) + { + context_p->function_start_p = context_p->source_p; + } +#endif /* JERRY_FUNCTION_TO_STRING */ + if (lexer_parse_identifier (context_p, LEXER_PARSE_NO_OPTS)) { if (!(ident_opts & (LEXER_OBJ_IDENT_ONLY_IDENTIFIERS | LEXER_OBJ_IDENT_OBJECT_PATTERN))) @@ -3253,6 +3271,10 @@ lexer_expect_object_literal_id (parser_context_t *context_p, /**< context */ #if JERRY_ESNEXT case LIT_CHAR_LEFT_SQUARE: { +#if JERRY_FUNCTION_TO_STRING + const uint8_t *function_start_p = context_p->function_start_p; +#endif /* JERRY_FUNCTION_TO_STRING */ + lexer_consume_next_character (context_p); lexer_next_token (context_p); @@ -3262,6 +3284,10 @@ lexer_expect_object_literal_id (parser_context_t *context_p, /**< context */ { parser_raise_error (context_p, PARSER_ERR_RIGHT_SQUARE_EXPECTED); } + +#if JERRY_FUNCTION_TO_STRING + context_p->function_start_p = function_start_p; +#endif /* JERRY_FUNCTION_TO_STRING */ return; } case LIT_CHAR_ASTERISK: @@ -3277,7 +3303,7 @@ lexer_expect_object_literal_id (parser_context_t *context_p, /**< context */ } case LIT_CHAR_DOT: { - if ((ident_opts & ((uint32_t) ~LEXER_OBJ_IDENT_OBJECT_PATTERN)) + if ((ident_opts & ((uint32_t) ~(LEXER_OBJ_IDENT_OBJECT_PATTERN | LEXER_OBJ_IDENT_SET_FUNCTION_START))) || context_p->source_p + 2 >= context_p->source_end_p || context_p->source_p[1] != LIT_CHAR_DOT || context_p->source_p[2] != LIT_CHAR_DOT) diff --git a/jerry-core/parser/js/js-lexer.h b/jerry-core/parser/js/js-lexer.h index e9f948cb5a..71b7c0394c 100644 --- a/jerry-core/parser/js/js-lexer.h +++ b/jerry-core/parser/js/js-lexer.h @@ -275,11 +275,16 @@ typedef enum */ typedef enum { - LEXER_OBJ_IDENT_NO_OPTS = 0, /**< no options */ - LEXER_OBJ_IDENT_ONLY_IDENTIFIERS = (1u << 0), /**< only identifiers are accepted */ - LEXER_OBJ_IDENT_CLASS_IDENTIFIER = (1u << 1), /**< expect identifier inside a class body */ - LEXER_OBJ_IDENT_CLASS_NO_STATIC = (1u << 2), /**< static keyword was not present before the identifier */ - LEXER_OBJ_IDENT_OBJECT_PATTERN = (1u << 3), /**< parse "get"/"set" as string literal in object pattern */ + LEXER_OBJ_IDENT_NO_OPTS = 0, /**< no options */ + LEXER_OBJ_IDENT_ONLY_IDENTIFIERS = (1u << 0), /**< only identifiers are accepted */ + LEXER_OBJ_IDENT_CLASS_IDENTIFIER = (1u << 1), /**< expect identifier inside a class body */ + LEXER_OBJ_IDENT_CLASS_NO_STATIC = (1u << 2), /**< static keyword was not present before the identifier */ + LEXER_OBJ_IDENT_OBJECT_PATTERN = (1u << 3), /**< parse "get"/"set" as string literal in object pattern */ +#if JERRY_FUNCTION_TO_STRING + LEXER_OBJ_IDENT_SET_FUNCTION_START = (1u << 4), /**< set function start */ +#else /* !JERRY_FUNCTION_TO_STRING */ + LEXER_OBJ_IDENT_SET_FUNCTION_START = 0, /**< set function start (disabled) */ +#endif /* JERRY_FUNCTION_TO_STRING */ } lexer_obj_ident_opts_t; /** diff --git a/jerry-core/parser/js/js-parser-expr.c b/jerry-core/parser/js/js-parser-expr.c index 0088b20fef..6e347a8e8e 100644 --- a/jerry-core/parser/js/js-parser-expr.c +++ b/jerry-core/parser/js/js-parser-expr.c @@ -562,6 +562,7 @@ parser_parse_class_body (parser_context_t *context_p, /**< context */ } lexer_expect_object_literal_id (context_p, (LEXER_OBJ_IDENT_CLASS_IDENTIFIER + | LEXER_OBJ_IDENT_SET_FUNCTION_START | (is_static ? 0 : LEXER_OBJ_IDENT_CLASS_NO_STATIC))); if (context_p->token.type == LEXER_RIGHT_BRACE) @@ -1167,7 +1168,7 @@ parser_parse_object_literal (parser_context_t *context_p) /**< context */ while (true) { - lexer_expect_object_literal_id (context_p, LEXER_OBJ_IDENT_NO_OPTS); + lexer_expect_object_literal_id (context_p, LEXER_OBJ_IDENT_SET_FUNCTION_START); switch (context_p->token.type) { @@ -2011,6 +2012,10 @@ parser_parse_unary_expression (parser_context_t *context_p, /**< context */ { JERRY_ASSERT (context_p->next_scanner_info_p->type == SCANNER_TYPE_FUNCTION); +#if JERRY_FUNCTION_TO_STRING + context_p->function_start_p = context_p->token.lit_location.char_p; +#endif /* JERRY_FUNCTION_TO_STRING */ + uint32_t arrow_status_flags = (PARSER_IS_FUNCTION | PARSER_IS_ARROW_FUNCTION | (context_p->status_flags & PARSER_INSIDE_CLASS_FIELD)); @@ -2124,6 +2129,9 @@ parser_parse_unary_expression (parser_context_t *context_p, /**< context */ } case LEXER_KEYW_FUNCTION: { +#if JERRY_FUNCTION_TO_STRING + context_p->function_start_p = context_p->token.lit_location.char_p; +#endif /* JERRY_FUNCTION_TO_STRING */ parser_parse_function_expression (context_p, PARSER_FUNCTION_CLOSURE | PARSER_IS_FUNC_EXPRESSION); break; } @@ -2270,6 +2278,10 @@ parser_parse_unary_expression (parser_context_t *context_p, /**< context */ parser_check_assignment_expr (context_p); +#if JERRY_FUNCTION_TO_STRING + context_p->function_start_p = context_p->source_p - 1; +#endif /* JERRY_FUNCTION_TO_STRING */ + uint32_t arrow_status_flags = (PARSER_IS_FUNCTION | PARSER_IS_ARROW_FUNCTION | (context_p->status_flags & PARSER_INSIDE_CLASS_FIELD)); diff --git a/jerry-core/parser/js/js-parser-internal.h b/jerry-core/parser/js/js-parser-internal.h index 237051d825..3d0e77b782 100644 --- a/jerry-core/parser/js/js-parser-internal.h +++ b/jerry-core/parser/js/js-parser-internal.h @@ -539,6 +539,10 @@ typedef struct parser_saved_context_t #if JERRY_LINE_INFO parser_line_info_data_t *line_info_p; /**< line info data */ #endif /* JERRY_LINE_INFO */ + +#if JERRY_FUNCTION_TO_STRING + const uint8_t *function_start_p; /**< start position of the current function */ +#endif /* JERRY_FUNCTION_TO_STRING */ } parser_saved_context_t; /** @@ -571,6 +575,7 @@ typedef struct const uint8_t *arguments_start_p; /**< function argument list start */ lit_utf8_size_t arguments_size; /**< function argument list size */ ecma_value_t script_value; /**< current script as value */ + ecma_value_t argument_list; /**< current argument list as value */ ecma_value_t user_value; /**< current user value */ #if JERRY_MODULE_SYSTEM @@ -639,6 +644,11 @@ typedef struct #if JERRY_LINE_INFO parser_line_info_data_t *line_info_p; /**< line info data */ #endif /* JERRY_LINE_INFO */ + +#if JERRY_FUNCTION_TO_STRING + const uint8_t *function_start_p; /**< start position of the function which will be parsed */ + const uint8_t *function_end_p; /**< end position of the current function */ +#endif /* JERRY_FUNCTION_TO_STRING */ } parser_context_t; /** diff --git a/jerry-core/parser/js/js-parser-statm.c b/jerry-core/parser/js/js-parser-statm.c index bd69b6966e..6ce13c5229 100644 --- a/jerry-core/parser/js/js-parser-statm.c +++ b/jerry-core/parser/js/js-parser-statm.c @@ -656,6 +656,17 @@ parser_parse_function_statement (parser_context_t *context_p) /**< context */ } #endif /* JERRY_ESNEXT */ +#if JERRY_FUNCTION_TO_STRING +#if JERRY_ESNEXT + if (!(context_p->next_scanner_info_p->u8_arg & SCANNER_FUNCTION_ASYNC)) + { + context_p->function_start_p = context_p->token.lit_location.char_p; + } +#else /* !JERRY_ESNEXT */ + context_p->function_start_p = context_p->token.lit_location.char_p; +#endif /* JERRY_ESNEXT */ +#endif /* JERRY_FUNCTION_TO_STRING */ + #if JERRY_DEBUGGER parser_line_counter_t debugger_line = context_p->token.line; parser_line_counter_t debugger_column = context_p->token.column; @@ -2560,6 +2571,9 @@ parser_parse_export_statement (parser_context_t *context_p) /**< context */ && context_p->next_scanner_info_p->source_p == context_p->source_p && context_p->next_scanner_info_p->type == SCANNER_TYPE_FUNCTION) { +#if JERRY_FUNCTION_TO_STRING + context_p->function_start_p = context_p->token.lit_location.char_p; +#endif /* JERRY_FUNCTION_TO_STRING */ lexer_next_token (context_p); } @@ -3246,6 +3260,9 @@ parser_parse_statements (parser_context_t *context_p) /**< context */ parser_raise_error (context_p, PARSER_ERR_LEXICAL_SINGLE_STATEMENT); } +#if JERRY_FUNCTION_TO_STRING + context_p->function_start_p = context_p->token.lit_location.char_p; +#endif /* JERRY_FUNCTION_TO_STRING */ lexer_next_token (context_p); JERRY_ASSERT (context_p->token.type == LEXER_KEYW_FUNCTION); continue; @@ -3343,6 +3360,10 @@ parser_parse_statements (parser_context_t *context_p) /**< context */ } #endif /* JERRY_LINE_INFO */ +#if JERRY_FUNCTION_TO_STRING + context_p->function_end_p = context_p->source_p; +#endif /* JERRY_FUNCTION_TO_STRING */ + parser_stack_pop_uint8 (context_p); context_p->last_statement.current_p = NULL; /* There is no lexer_next_token here, since the diff --git a/jerry-core/parser/js/js-parser.c b/jerry-core/parser/js/js-parser.c index 5fa0448000..25f43e7b2c 100644 --- a/jerry-core/parser/js/js-parser.c +++ b/jerry-core/parser/js/js-parser.c @@ -15,6 +15,7 @@ #include "debugger.h" #include "ecma-exceptions.h" +#include "ecma-extended-info.h" #include "ecma-helpers.h" #include "ecma-literal-storage.h" #include "ecma-module.h" @@ -910,11 +911,6 @@ parser_post_processing (parser_context_t *context_p) /**< context */ total_size += sizeof (ecma_value_t); } - if (context_p->argument_length != UINT16_MAX) - { - total_size += sizeof (ecma_value_t); - } - if (context_p->tagged_template_literal_cp != JMEM_CP_NULL) { total_size += sizeof (ecma_value_t); @@ -925,6 +921,48 @@ parser_post_processing (parser_context_t *context_p) /**< context */ total_size += sizeof (ecma_value_t); #endif /* JERRY_LINE_INFO */ +#if JERRY_ESNEXT || JERRY_FUNCTION_TO_STRING + uint8_t extended_info = 0; +#endif /* JERRY_ESNEXT || JERRY_FUNCTION_TO_STRING */ + +#if JERRY_ESNEXT + if (context_p->argument_length != UINT16_MAX) + { + extended_info |= CBC_EXTENDED_CODE_FLAGS_HAS_ARGUMENT_LENGTH; + total_size += ecma_extended_info_get_encoded_length (context_p->argument_length); + } +#endif /* JERRY_ESNEXT */ + +#if JERRY_FUNCTION_TO_STRING + if (context_p->last_context_p != NULL) + { + extended_info |= CBC_EXTENDED_CODE_FLAGS_HAS_SOURCE_CODE_RANGE; + + const uint8_t *start_p = context_p->source_start_p; + const uint8_t *function_start_p = context_p->last_context_p->function_start_p; + + if (function_start_p < start_p || function_start_p >= start_p + context_p->source_size) + { + JERRY_ASSERT (context_p->arguments_start_p != NULL + && function_start_p >= context_p->arguments_start_p + && function_start_p < context_p->arguments_start_p + context_p->arguments_size); + + start_p = context_p->arguments_start_p; + extended_info |= CBC_EXTENDED_CODE_FLAGS_SOURCE_CODE_IN_ARGUMENTS; + } + + total_size += ecma_extended_info_get_encoded_length ((uint32_t) (function_start_p - start_p)); + total_size += ecma_extended_info_get_encoded_length ((uint32_t) (context_p->function_end_p - function_start_p)); + } +#endif /* JERRY_FUNCTION_TO_STRING */ + +#if JERRY_ESNEXT || JERRY_FUNCTION_TO_STRING + if (extended_info != 0) + { + total_size += sizeof (uint8_t); + } +#endif /* JERRY_ESNEXT || JERRY_FUNCTION_TO_STRING */ + total_size = JERRY_ALIGNUP (total_size, JMEM_ALIGNMENT); compiled_code_p = (ecma_compiled_code_t *) parser_malloc (context_p, total_size); @@ -1327,19 +1365,10 @@ parser_post_processing (parser_context_t *context_p) /**< context */ *(--base_p) = ecma_make_magic_string_value (LIT_MAGIC_STRING__EMPTY); } - if (context_p->argument_length != UINT16_MAX) - { - compiled_code_p->status_flags |= CBC_CODE_FLAGS_HAS_EXTENDED_INFO; - *(--base_p) = context_p->argument_length; - } - if (context_p->tagged_template_literal_cp != JMEM_CP_NULL) { compiled_code_p->status_flags |= CBC_CODE_FLAGS_HAS_TAGGED_LITERALS; - base_p[-1] = (ecma_value_t) context_p->tagged_template_literal_cp; -#if JERRY_LINE_INFO - --base_p; -#endif /* JERRY_LINE_INFO */ + *(--base_p) = (ecma_value_t) context_p->tagged_template_literal_cp; } #endif /* JERRY_ESNEXT */ @@ -1347,6 +1376,44 @@ parser_post_processing (parser_context_t *context_p) /**< context */ ECMA_SET_INTERNAL_VALUE_POINTER (base_p[-1], line_info_p); #endif /* JERRY_LINE_INFO */ +#if JERRY_ESNEXT || JERRY_FUNCTION_TO_STRING + if (extended_info != 0) + { +#if JERRY_LINE_INFO + base_p--; +#endif /* JERRY_LINE_INFO */ + + uint8_t *extended_info_p = ((uint8_t *) base_p) - 1; + + compiled_code_p->status_flags |= CBC_CODE_FLAGS_HAS_EXTENDED_INFO; + *extended_info_p = extended_info; + +#if JERRY_ESNEXT + if (context_p->argument_length != UINT16_MAX) + { + ecma_extended_info_encode_vlq (&extended_info_p, context_p->argument_length); + } +#endif /* JERRY_ESNEXT */ + +#if JERRY_FUNCTION_TO_STRING + if (context_p->last_context_p != NULL) + { + const uint8_t *start_p = context_p->source_start_p; + + if (extended_info & CBC_EXTENDED_CODE_FLAGS_SOURCE_CODE_IN_ARGUMENTS) + { + start_p = context_p->arguments_start_p; + } + + const uint8_t *function_start_p = context_p->last_context_p->function_start_p; + + ecma_extended_info_encode_vlq (&extended_info_p, (uint32_t) (function_start_p - start_p)); + ecma_extended_info_encode_vlq (&extended_info_p, (uint32_t) (context_p->function_end_p - function_start_p)); + } +#endif /* JERRY_FUNCTION_TO_STRING */ + } +#endif /* JERRY_ESNEXT || JERRY_FUNCTION_TO_STRING */ + #if JERRY_PARSER_DUMP_BYTE_CODE if (context_p->is_show_opcodes) { @@ -1793,22 +1860,22 @@ parser_parse_source (void *source_p, /**< source code */ context.module_names_p = NULL; #endif /* JERRY_MODULE_SYSTEM */ - ecma_value_t argument_list = ECMA_VALUE_EMPTY; + context.argument_list = ECMA_VALUE_EMPTY; if (context.options_p != NULL && (context.options_p->options & JERRY_PARSE_HAS_ARGUMENT_LIST)) { - argument_list = context.options_p->argument_list; + context.argument_list = context.options_p->argument_list; } else if (context.global_status_flags & ECMA_PARSE_HAS_ARGUMENT_LIST_VALUE) { JERRY_ASSERT (context.global_status_flags & ECMA_PARSE_HAS_SOURCE_VALUE); - argument_list = ((ecma_value_t *) source_p)[1]; + context.argument_list = ((ecma_value_t *) source_p)[1]; } - if (argument_list != ECMA_VALUE_EMPTY) + if (context.argument_list != ECMA_VALUE_EMPTY) { - JERRY_ASSERT (ecma_is_value_string (argument_list)); + JERRY_ASSERT (ecma_is_value_string (context.argument_list)); context.status_flags |= PARSER_IS_FUNCTION; #if JERRY_ESNEXT @@ -1822,7 +1889,7 @@ parser_parse_source (void *source_p, /**< source code */ } #endif /* JERRY_ESNEXT */ - ecma_string_t *string_p = ecma_get_string_from_value (argument_list); + ecma_string_t *string_p = ecma_get_string_from_value (context.argument_list); uint8_t flags = ECMA_STRING_FLAG_EMPTY; context.arguments_start_p = ecma_string_get_chars (string_p, &context.arguments_size, NULL, NULL, &flags); @@ -1881,7 +1948,7 @@ parser_parse_source (void *source_p, /**< source code */ if (CBC_SCRIPT_GET_TYPE (parent_script_p) != CBC_SCRIPT_GENERIC) { - context.user_value = ((cbc_script_user_t *) parent_script_p)->user_value; + context.user_value = CBC_SCRIPT_GET_USER_VALUE (parent_script_p); } #if JERRY_SNAPSHOT_EXEC } @@ -1893,8 +1960,20 @@ parser_parse_source (void *source_p, /**< source code */ context.user_value = context.options_p->user_value; } - uint32_t script_size = (context.user_value != ECMA_VALUE_EMPTY ? sizeof (cbc_script_user_t) - : sizeof (cbc_script_t)); + size_t script_size = sizeof (cbc_script_t); + + if (context.user_value != ECMA_VALUE_EMPTY) + { + script_size += sizeof (ecma_value_t); + } + +#if JERRY_FUNCTION_TO_STRING + if (context.argument_list != ECMA_VALUE_EMPTY) + { + script_size += sizeof (ecma_value_t); + } +#endif /* JERRY_FUNCTION_TO_STRING */ + context.script_p = jmem_heap_alloc_block_null_on_error (script_size); if (JERRY_UNLIKELY (context.script_p == NULL)) @@ -1977,6 +2056,11 @@ parser_parse_source (void *source_p, /**< source code */ context.line_info_p = NULL; #endif /* JERRY_LINE_INFO */ +#if JERRY_FUNCTION_TO_STRING + context.function_start_p = NULL; + context.function_end_p = NULL; +#endif /* JERRY_FUNCTION_TO_STRING */ + #if JERRY_PARSER_DUMP_BYTE_CODE context.is_show_opcodes = (JERRY_CONTEXT (jerry_init_flags) & JERRY_INIT_SHOW_OPCODES); context.total_byte_code_size = 0; @@ -2116,9 +2200,44 @@ parser_parse_source (void *source_p, /**< source code */ if (context.user_value != ECMA_VALUE_EMPTY) { - ((cbc_script_user_t *) context.script_p)->user_value = ecma_copy_value_if_not_object (context.user_value); + CBC_SCRIPT_GET_USER_VALUE (context.script_p) = ecma_copy_value_if_not_object (context.user_value); } +#if JERRY_FUNCTION_TO_STRING + if (!(context.global_status_flags & ECMA_PARSE_HAS_SOURCE_VALUE)) + { + ecma_string_t *string_p; + + if (context.global_status_flags & ECMA_PARSE_INTERNAL_HAS_4_BYTE_MARKER) + { + string_p = ecma_new_ecma_string_from_utf8_converted_to_cesu8 (context.source_start_p, context.source_size); + } + else + { + string_p = ecma_new_ecma_string_from_utf8 (context.source_start_p, context.source_size); + } + + context.script_p->source_code = ecma_make_string_value (string_p); + } + else + { + ecma_value_t source = ((ecma_value_t *) source_p)[0]; + + ecma_ref_ecma_string (ecma_get_string_from_value (source)); + context.script_p->source_code = source; + } + + if (context.argument_list != ECMA_VALUE_EMPTY) + { + int idx = (context.user_value != ECMA_VALUE_EMPTY) ? 1 : 0; + + CBC_SCRIPT_GET_OPTIONAL_VALUES (context.script_p)[idx] = context.argument_list; + + ecma_ref_ecma_string (ecma_get_string_from_value (context.argument_list)); + context.script_p->refs_and_type |= CBC_SCRIPT_HAS_FUNCTION_ARGUMENTS; + } +#endif /* JERRY_FUNCTION_TO_STRING */ + #if JERRY_PARSER_DUMP_BYTE_CODE if (context.is_show_opcodes) { @@ -2315,6 +2434,10 @@ parser_save_context (parser_context_t *context_p, /**< context */ saved_context_p->line_info_p = context_p->line_info_p; #endif /* JERRY_LINE_INFO */ +#if JERRY_FUNCTION_TO_STRING + saved_context_p->function_start_p = context_p->function_start_p; +#endif /* JERRY_FUNCTION_TO_STRING */ + /* Reset private part of the context. */ context_p->status_flags &= PARSER_IS_STRICT; diff --git a/jerry-core/vm/vm.c b/jerry-core/vm/vm.c index bfa251a811..e428f60dce 100644 --- a/jerry-core/vm/vm.c +++ b/jerry-core/vm/vm.c @@ -4573,7 +4573,7 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ if (CBC_SCRIPT_GET_TYPE (script_p) != CBC_SCRIPT_GENERIC) { - user_value = ((cbc_script_user_t *) script_p)->user_value; + user_value = CBC_SCRIPT_GET_USER_VALUE (script_p); } #if JERRY_SNAPSHOT_EXEC } diff --git a/tests/jerry/es.next/function-prototype-tostring.js b/tests/jerry/es.next/function-prototype-tostring.js new file mode 100644 index 0000000000..72abaefe81 --- /dev/null +++ b/tests/jerry/es.next/function-prototype-tostring.js @@ -0,0 +1,188 @@ +// Copyright JS Foundation and other contributors, http://js.foundation +// +// 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. + +function check() {} + +if (check.toString() !== "function () { /* ecmascript */ }") +{ + var a, b, o, c, f; + + /* Function statements. */ + + a = 6; function f1(a,b=1,c) { return a /* comment */ + b + c } b = 7 + assert(f1.toString() === "function f1(a,b=1,c) { return a /* comment */ + b + c }") + + a = 6; function * f2(a,b,c=1) { + function x() {} + } b = 7 + assert(f2.toString() === "function * f2(a,b,c=1) {\n function x() {}\n }") + + a = 6;async function f3 ( a , b , c ) { } b = 7 + assert(f3.toString() === "async function f3 ( a , b , c ) { }") + + a = 6;async/**/function*f4(a,b,c){} b = 7 + assert(f4.toString() === "async/**/function*f4(a,b,c){}") + + /* Object initializers. */ + + o = {f(a) { return a }} + assert(o.f.toString() === "f(a) { return a }") + + o = {f:function(a){/**/}} + assert(o.f.toString() === "function(a){/**/}") + + o = { [function(){ return 'f' }()] (a = function() {}) {} } + assert(o.f.toString() === "[function(){ return 'f' }()] (a = function() {}) {}") + + o = {* f(a) {}} + assert(o.f.toString() === "* f(a) {}") + + o = {*[function(){ return 'f' }()](a) {}} + assert(o.f.toString() === "*[function(){ return 'f' }()](a) {}") + + o = {async/**/f(a) {}} + assert(o.f.toString() === "async/**/f(a) {}") + + o = {/**/async [function(){ return 'f' }()](a) {}/**/} + assert(o.f.toString() === "async [function(){ return 'f' }()](a) {}") + + o = {a:1,async/**/*/**/f(a) {},b:1} + assert(o.f.toString() === "async/**/*/**/f(a) {}") + + o = {async *// + [function(){ return 'f' }()](a) {}/**/} + assert(o.f.toString() === "async *//\n [function(){ return 'f' }()](a) {}") + + o = { get a() {return/**/6} } + assert(Object.getOwnPropertyDescriptor(o, "a").get.toString() === "get a() {return/**/6}") + + o = { get[function(){ return 'a' }()](){} } + assert(Object.getOwnPropertyDescriptor(o, "a").get.toString() === "get[function(){ return 'a' }()](){}") + + o = { set a(v) {/**/} } + assert(Object.getOwnPropertyDescriptor(o, "a").set.toString() === "set a(v) {/**/}") + + o = {/**/set/**/[function(){ return 'a' }()]/**/(v) {/**/}/**/} + assert(Object.getOwnPropertyDescriptor(o, "a").set.toString() === "set/**/[function(){ return 'a' }()]/**/(v) {/**/}") + + /* Class static functions. */ + + c = class { static/**/f() {}/**/ } + assert(c.f.toString() === "f() {}") + + c = class { static[function(){ return 'f' }()]() {} } + assert(c.f.toString() === "[function(){ return 'f' }()]() {}") + + c = class { static *f() {} } + assert(c.f.toString() === "*f() {}") + + c = class {/**/static * [function(){ return 'f' }()](a=6){}/**/} + assert(c.f.toString() === "* [function(){ return 'f' }()](a=6){}") + + c = class { static/**/async f() {} } + assert(c.f.toString() === "async f() {}") + + c = class {static async[function(){ return 'f' }()]() {/**/}} + assert(c.f.toString() === "async[function(){ return 'f' }()]() {/**/}") + + c = class { static async*f() {}} + assert(c.f.toString() === "async*f() {}") + + c = class { static async*/**/[function(){ return 'f' }()](){} } + assert(c.f.toString() === "async*/**/[function(){ return 'f' }()](){}") + + c = class { static/**/get/**/a() {}} + assert(Object.getOwnPropertyDescriptor(c, "a").get.toString() === "get/**/a() {}") + + c = class {static set a(v){}} + assert(Object.getOwnPropertyDescriptor(c, "a").set.toString() === "set a(v){}") + + c = class { static get[function(){ return 'a' }()](){} } + assert(Object.getOwnPropertyDescriptor(c, "a").get.toString() === "get[function(){ return 'a' }()](){}") + + c = class { static set[function(){ return 'a' }()](v){}// + } + assert(Object.getOwnPropertyDescriptor(c, "a").set.toString() === "set[function(){ return 'a' }()](v){}") + + /* Class functions. */ + + o = Object.getPrototypeOf(new class {/**/f() {}/**/}) + assert(o.f.toString() === "f() {}") + + o = Object.getPrototypeOf(new class {[function(){ return 'f' }()](){}}) + assert(o.f.toString() === "[function(){ return 'f' }()](){}") + + o = Object.getPrototypeOf(new class { * /**/ f() {} }) + assert(o.f.toString() === "* /**/ f() {}") + + o = Object.getPrototypeOf(new class { * [function(){ return 'f' }()]/**/(){} }) + assert(o.f.toString() === "* [function(){ return 'f' }()]/**/(){}") + + o = Object.getPrototypeOf(new class {async f() {}}) + assert(o.f.toString() === "async f() {}") + + o = Object.getPrototypeOf(new class {async[function(){ return 'f' }()](){}}) + assert(o.f.toString() === "async[function(){ return 'f' }()](){}") + + o = Object.getPrototypeOf(new class {/**/get/**/a() {}/**/}) + assert(Object.getOwnPropertyDescriptor(o, "a").get.toString() === "get/**/a() {}") + + o = Object.getPrototypeOf(new class { set a(v){} }) + assert(Object.getOwnPropertyDescriptor(o, "a").set.toString() === "set a(v){}") + + o = Object.getPrototypeOf(new class {/**/get/**/[function(){ return 'a' }()]() {}/**/}) + assert(Object.getOwnPropertyDescriptor(o, "a").get.toString() === "get/**/[function(){ return 'a' }()]() {}") + + o = Object.getPrototypeOf(new class { set/**/[function(){ return 'a' }()]( v ){} }) + assert(Object.getOwnPropertyDescriptor(o, "a").set.toString() === "set/**/[function(){ return 'a' }()]( v ){}") + + /* Function creators. */ + f = Function("a,b", "return a + b") + assert(f.toString() === "function anonymous(a,b\n) {\nreturn a + b\n}") + + f = Function("", "") + assert(f.toString() === "function anonymous(\n) {\n\n}") + + f = function*(){}.constructor("a,b", "c", "yield a; return b + c") + assert(f.toString() === "function* anonymous(a,b,c\n) {\nyield a; return b + c\n}") + + f = async function(){}.constructor("a", "return a + 'x'") + assert(f.toString() === "async function anonymous(a\n) {\nreturn a + 'x'\n}") + + f = async function*(){}.constructor("a=3", "return a") + assert(f.toString() === "async function* anonymous(a=3\n) {\nreturn a\n}") + + f = Function("a = function(x) { return x + 3 }", "return a") + assert(f().toString() === "function(x) { return x + 3 }") + + f = Function("return function(x) { return x + 3 }") + assert(f().toString() === "function(x) { return x + 3 }") + + /* Arrow functions. */ + f = x => x + 1/**/ + assert(f.toString() === "x => x + 1") + + f =x => { return x + 1 }/**/ + assert(f.toString() === "x => { return x + 1 }") + + f = (/**/) => 'x' // + + y + assert(f.toString() === "(/**/) => 'x' //\n + y") + + f = async x => x + 1 + assert(f.toString() === "async x => x + 1") + + f =/**/async (x,/**/y)=>/**/null + assert(f.toString() === "async (x,/**/y)=>/**/null") +} diff --git a/tests/jerry/es.next/new-target.js b/tests/jerry/es.next/new-target.js index cc6e87675e..a17ae95818 100644 --- a/tests/jerry/es.next/new-target.js +++ b/tests/jerry/es.next/new-target.js @@ -149,7 +149,9 @@ new delete_test (); function binary_test_1 () { /*/ new.target is converted to string */ - assert ((new.target + 1) === "function(){/* ecmascript */}1"); + var str = (new.target + 1); + assert (str.substring(0, 8) === "function" + && str.substring(str.length - 2, str.length) === "}1"); } function binary_test_2 () { assert (isNaN (new.target - 3)); } function binary_test_3 () { assert (isNaN (new.target * 2)); } diff --git a/tests/jerry/es.next/regression-test-issue-4468.js b/tests/jerry/es.next/regression-test-issue-4468.js index 2c8247e31a..0c5694af43 100644 --- a/tests/jerry/es.next/regression-test-issue-4468.js +++ b/tests/jerry/es.next/regression-test-issue-4468.js @@ -14,4 +14,6 @@ var str = 'for (let i=0; i<(eval("1; function x() { }; 2;")); x - i++) { x += delete x;}' -assert(eval(str) === 'function(){/* ecmascript */}true'); +var e = eval(str) +assert(e === 'function x() { }true' + || e === 'function () { /* ecmascript */ }true'); diff --git a/tests/jerry/function-prototype-tostring.js b/tests/jerry/function-prototype-tostring.js index 9b1438e629..fedd23f667 100644 --- a/tests/jerry/function-prototype-tostring.js +++ b/tests/jerry/function-prototype-tostring.js @@ -12,16 +12,27 @@ // See the License for the specific language governing permissions and // limitations under the License. -assert (Math.cos.toString() === "function(){/* ecmascript */}"); +assert (Math.cos.toString() === "function () { [native code] }"); + +var has_toString = none.toString() != "function () { /* ecmascript */ }" + +function check(f, expected) +{ + assert (f.toString() === (has_toString ? expected : "function () { /* ecmascript */ }")) +} function none() { return 1; } -assert (none.toString() === "function(){/* ecmascript */}"); +check (none, "function none() { return 1; }") +assert (none.bind({}).toString() === "function () { [native code] }") function single(b) { return 1; } -assert (single.toString() === "function(){/* ecmascript */}"); +check (single, "function single(b) { return 1; }") +assert (single.bind({}).toString() === "function () { [native code] }") function multiple(a,b) { return 1; } -assert (multiple.toString() === "function(){/* ecmascript */}"); +check (multiple, "function multiple(a,b) { return 1; }") +assert (multiple.bind({}).toString() === "function () { [native code] }") function lots(a,b,c,d,e,f,g,h,i,j,k) { return 1; } -assert (lots.toString() === "function(){/* ecmascript */}"); +check (lots, "function lots(a,b,c,d,e,f,g,h,i,j,k) { return 1; }") +assert (lots.bind({}).toString() === "function () { [native code] }") diff --git a/tests/test262-esnext-excludelist.xml b/tests/test262-esnext-excludelist.xml index 7b2f7cf4bf..7c426359a8 100644 --- a/tests/test262-esnext-excludelist.xml +++ b/tests/test262-esnext-excludelist.xml @@ -28,60 +28,9 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -6678,16 +6627,6 @@ - - - - - - - - - - diff --git a/tools/build.py b/tools/build.py index 5a38476c38..17f79247ba 100755 --- a/tools/build.py +++ b/tools/build.py @@ -120,6 +120,8 @@ def devhelp(helpstring): help='enable the jerry debugger (%(choices)s)') coregrp.add_argument('--js-parser', metavar='X', choices=['ON', 'OFF'], type=str.upper, help='enable js-parser (%(choices)s)') + coregrp.add_argument('--function-to-string', metavar='X', choices=['ON', 'OFF'], type=str.upper, + help='enable function toString (%(choices)s)') coregrp.add_argument('--line-info', metavar='X', choices=['ON', 'OFF'], type=str.upper, help='provide line info (%(choices)s)') coregrp.add_argument('--logging', metavar='X', choices=['ON', 'OFF'], type=str.upper, @@ -208,6 +210,7 @@ def build_options_append(cmakeopt, cliarg): build_options_append('JERRY_EXTERNAL_CONTEXT', arguments.external_context) build_options_append('JERRY_DEBUGGER', arguments.jerry_debugger) build_options_append('JERRY_PARSER', arguments.js_parser) + build_options_append('JERRY_FUNCTION_TO_STRING', arguments.function_to_string) build_options_append('JERRY_LINE_INFO', arguments.line_info) build_options_append('JERRY_LOGGING', arguments.logging) build_options_append('JERRY_GLOBAL_HEAP_SIZE', arguments.mem_heap) diff --git a/tools/run-tests.py b/tools/run-tests.py index b7d2e934bc..f449206e5d 100755 --- a/tools/run-tests.py +++ b/tools/run-tests.py @@ -42,7 +42,7 @@ def skip_if(condition, desc): OPTIONS_COMMON = ['--lto=off'] OPTIONS_PROFILE_MIN = ['--profile=minimal'] OPTIONS_PROFILE_ES51 = ['--profile=es5.1'] -OPTIONS_PROFILE_ESNEXT = ['--profile=es.next'] +OPTIONS_PROFILE_ESNEXT = ['--profile=es.next', '--function-to-string=on'] OPTIONS_STACK_LIMIT = ['--stack-limit=96'] OPTIONS_GC_MARK_LIMIT = ['--gc-mark-limit=16'] OPTIONS_MEM_STRESS = ['--mem-stress-test=on']