Implement tagged template literals (#3456)

Missing features: snapshot support

JerryScript-DCO-1.0-Signed-off-by: Robert Fancsik frobert@inf.u-szeged.hu
This commit is contained in:
Robert Fancsik
2019-12-19 19:10:45 +01:00
committed by GitHub
parent 7bfbc701d8
commit 9596a7e1d6
28 changed files with 1229 additions and 314 deletions
+7
View File
@@ -161,6 +161,13 @@ snapshot_add_compiled_code (ecma_compiled_code_t *compiled_code_p, /**< compiled
ecma_compiled_code_t *copied_code_p = (ecma_compiled_code_t *) copied_code_start_p;
#if ENABLED (JERRY_ES2015)
if (compiled_code_p->status_flags & CBC_CODE_FLAG_HAS_TAGGED_LITERALS)
{
const char * const error_message_p = "Unsupported feature: tagged template literals.";
globals_p->snapshot_error = jerry_create_error (JERRY_ERROR_RANGE, (const jerry_char_t *) error_message_p);
return 0;
}
if (compiled_code_p->status_flags & CBC_CODE_FLAGS_CONSTRUCTOR)
{
globals_p->class_found = true;
+38 -10
View File
@@ -20,6 +20,7 @@
#include "ecma-alloc.h"
#include "ecma-array-object.h"
#include "ecma-container-object.h"
#include "ecma-function-object.h"
#include "ecma-globals.h"
#include "ecma-gc.h"
#include "ecma-helpers.h"
@@ -362,6 +363,22 @@ ecma_gc_mark_container_object (ecma_object_t *object_p) /**< object */
#if ENABLED (JERRY_ES2015)
/**
* Mark tagged template literals of the compiled code.
*/
static void
ecma_gc_mark_tagged_template_literals (const ecma_compiled_code_t *byte_code_p)
{
JERRY_ASSERT (byte_code_p->status_flags & CBC_CODE_FLAG_HAS_TAGGED_LITERALS);
ecma_collection_t *collection_p = ecma_compiled_code_get_tagged_template_collection (byte_code_p);
for (uint32_t i = 0; i < collection_p->item_count; i++)
{
ecma_gc_set_object_visited (ecma_get_object_from_value (collection_p->buffer_p[i]));
}
} /* ecma_gc_mark_tagged_template_literals */
/**
* Mark objects referenced by inactive generator functions, async functions, etc.
*/
@@ -602,9 +619,17 @@ ecma_gc_mark (ecma_object_t *object_p) /**< object to mark from */
if (!ecma_get_object_is_builtin (object_p))
{
ecma_extended_object_t *ext_func_p = (ecma_extended_object_t *) object_p;
ecma_gc_set_object_visited (ECMA_GET_INTERNAL_VALUE_POINTER (ecma_object_t,
ext_func_p->u.function.scope_cp));
#if ENABLED (JERRY_ES2015)
const ecma_compiled_code_t *byte_code_p = ecma_op_function_get_compiled_code (ext_func_p);
if (byte_code_p->status_flags & CBC_CODE_FLAG_HAS_TAGGED_LITERALS)
{
ecma_gc_mark_tagged_template_literals (byte_code_p);
}
#endif /* ENABLED (JERRY_ES2015) */
}
break;
}
@@ -620,6 +645,13 @@ ecma_gc_mark (ecma_object_t *object_p) /**< object to mark from */
{
ecma_gc_set_object_visited (ecma_get_object_from_value (arrow_func_p->this_binding));
}
const ecma_compiled_code_t *byte_code_p = ecma_op_arrow_function_get_compiled_code (arrow_func_p);
if (byte_code_p->status_flags & CBC_CODE_FLAG_HAS_TAGGED_LITERALS)
{
ecma_gc_mark_tagged_template_literals (byte_code_p);
}
break;
}
#endif /* ENABLED (JERRY_ES2015) */
@@ -1133,16 +1165,15 @@ ecma_gc_free_object (ecma_object_t *object_p) /**< object to free */
#if ENABLED (JERRY_SNAPSHOT_EXEC)
if (ext_func_p->u.function.bytecode_cp != ECMA_NULL_POINTER)
{
#endif /* ENABLED (JERRY_SNAPSHOT_EXEC) */
ecma_bytecode_deref (ECMA_GET_INTERNAL_VALUE_POINTER (ecma_compiled_code_t,
ext_func_p->u.function.bytecode_cp));
#if ENABLED (JERRY_SNAPSHOT_EXEC)
}
else
{
ext_object_size = sizeof (ecma_static_function_t);
}
#else /* !ENABLED (JERRY_SNAPSHOT_EXEC) */
ecma_bytecode_deref (ECMA_GET_INTERNAL_VALUE_POINTER (ecma_compiled_code_t,
ext_func_p->u.function.bytecode_cp));
#endif /* ENABLED (JERRY_SNAPSHOT_EXEC) */
break;
}
@@ -1156,18 +1187,15 @@ ecma_gc_free_object (ecma_object_t *object_p) /**< object to free */
#if ENABLED (JERRY_SNAPSHOT_EXEC)
if (arrow_func_p->bytecode_cp != ECMA_NULL_POINTER)
{
ecma_bytecode_deref (ECMA_GET_NON_NULL_POINTER (ecma_compiled_code_t,
arrow_func_p->bytecode_cp));
#endif /* ENABLED (JERRY_SNAPSHOT_EXEC) */
ecma_bytecode_deref (ECMA_GET_NON_NULL_POINTER (ecma_compiled_code_t, arrow_func_p->bytecode_cp));
ext_object_size = sizeof (ecma_arrow_function_t);
#if ENABLED (JERRY_SNAPSHOT_EXEC)
}
else
{
ext_object_size = sizeof (ecma_static_arrow_function_t);
}
#else /* !ENABLED (JERRY_SNAPSHOT_EXEC) */
ecma_bytecode_deref (ECMA_GET_NON_NULL_POINTER (ecma_compiled_code_t,
arrow_func_p->bytecode_cp));
ext_object_size = sizeof (ecma_arrow_function_t);
#endif /* ENABLED (JERRY_SNAPSHOT_EXEC) */
break;
}
+62
View File
@@ -1461,6 +1461,23 @@ ecma_bytecode_deref (ecma_compiled_code_t *bytecode_p) /**< byte code pointer */
}
#endif /* ENABLED (JERRY_DEBUGGER) */
#if ENABLED (JERRY_ES2015)
if (bytecode_p->status_flags & CBC_CODE_FLAG_HAS_TAGGED_LITERALS)
{
ecma_length_t formal_params_number = ecma_compiled_code_get_formal_params (bytecode_p);
uint8_t *byte_p = (uint8_t *) bytecode_p;
byte_p += ((size_t) bytecode_p->size) << JMEM_ALIGNMENT_LOG;
ecma_value_t *tagged_base_p = (ecma_value_t *) byte_p;
tagged_base_p -= formal_params_number;
ecma_collection_t *coll_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, tagged_base_p[-1]);
ecma_collection_destroy (coll_p);
}
#endif /* ENABLED (JERRY_ES2015) */
#if ENABLED (JERRY_MEM_STATS)
jmem_stats_free_byte_code_bytes (((size_t) bytecode_p->size) << JMEM_ALIGNMENT_LOG);
#endif /* ENABLED (JERRY_MEM_STATS) */
@@ -1478,6 +1495,51 @@ ecma_bytecode_deref (ecma_compiled_code_t *bytecode_p) /**< byte code pointer */
((size_t) bytecode_p->size) << JMEM_ALIGNMENT_LOG);
} /* ecma_bytecode_deref */
#if ENABLED (JERRY_ES2015)
/**
* Get the tagged template collection of the compiled code
*
* @return pointer to the tagged template collection
*/
ecma_collection_t *
ecma_compiled_code_get_tagged_template_collection (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_FLAG_HAS_TAGGED_LITERALS);
uint8_t *byte_p = (uint8_t *) bytecode_header_p;
byte_p += ((size_t) bytecode_header_p->size) << JMEM_ALIGNMENT_LOG;
ecma_value_t *tagged_base_p = (ecma_value_t *) byte_p;
tagged_base_p -= ecma_compiled_code_get_formal_params (bytecode_header_p);
return ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, tagged_base_p[-1]);
} /* ecma_compiled_code_get_tagged_template_collection */
#endif /* ENABLED (JERRY_ES2015) */
#if ENABLED (JERRY_LINE_INFO) || ENABLED (JERRY_ES2015_MODULE_SYSTEM) || ENABLED (JERRY_ES2015)
/**
* Get the number of formal parameters of the compiled code
*
* @return number of formal parameters
*/
ecma_length_t
ecma_compiled_code_get_formal_params (const ecma_compiled_code_t *bytecode_header_p) /**< compiled code */
{
if (!(bytecode_header_p->status_flags & CBC_CODE_FLAGS_MAPPED_ARGUMENTS_NEEDED))
{
return 0;
}
if (bytecode_header_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS)
{
return ((cbc_uint16_arguments_t *) bytecode_header_p)->argument_end;
}
return ((cbc_uint8_arguments_t *) bytecode_header_p)->argument_end;
} /* ecma_compiled_code_get_formal_params */
#endif /* ENABLED (JERRY_LINE_INFO) || ENABLED (JERRY_ES2015_MODULE_SYSTEM) || ENABLED (JERRY_ES2015) */
#if (JERRY_STACK_LIMIT != 0)
/**
* Check the current stack usage by calculating the difference from the initial stack base.
+6
View File
@@ -425,6 +425,12 @@ void ecma_raise_error_from_error_reference (ecma_value_t value);
void ecma_bytecode_ref (ecma_compiled_code_t *bytecode_p);
void ecma_bytecode_deref (ecma_compiled_code_t *bytecode_p);
#if ENABLED (JERRY_ES2015)
ecma_collection_t *ecma_compiled_code_get_tagged_template_collection (const ecma_compiled_code_t *bytecode_header_p);
#endif /* ENABLED (JERRY_ES2015) */
#if ENABLED (JERRY_LINE_INFO) || ENABLED (JERRY_ES2015_MODULE_SYSTEM) || ENABLED (JERRY_ES2015)
ecma_length_t ecma_compiled_code_get_formal_params (const ecma_compiled_code_t *bytecode_p);
#endif /* ENABLED (JERRY_LINE_INFO) || ENABLED (JERRY_ES2015_MODULE_SYSTEM) || ENABLED (JERRY_ES2015) */
#if (JERRY_STACK_LIMIT != 0)
uintptr_t ecma_get_current_stack_usage (void);
#endif /* (JERRY_STACK_LIMIT != 0) */
@@ -112,6 +112,159 @@ ecma_builtin_string_object_from_char_code (ecma_value_t this_arg, /**< 'this' ar
#if ENABLED (JERRY_ES2015)
/**
* The String object's 'raw' routine
*
* See also:
* ECMA-262 v6, 21.1.2.4
*
* @return ecma value
* Returned value must be freed with ecma_free_value.
*/
static ecma_value_t
ecma_builtin_string_object_raw (ecma_value_t this_arg, /**< 'this' argument */
const ecma_value_t args[], /**< arguments list */
ecma_length_t args_number) /**< number of arguments */
{
JERRY_UNUSED (this_arg);
/* 1 - 2. */
const ecma_value_t *substitutions;
ecma_length_t number_of_substitutions;
if (args_number > 1)
{
substitutions = args + 1;
number_of_substitutions = args_number - 1;
}
else
{
substitutions = NULL;
number_of_substitutions = 0;
}
/* 3. */
ecma_value_t template = args_number > 0 ? args[0] : ECMA_VALUE_UNDEFINED;
ecma_value_t cooked = ecma_op_to_object (template);
/* 4. */
if (ECMA_IS_VALUE_ERROR (cooked))
{
return cooked;
}
ecma_object_t *cooked_obj_p = ecma_get_object_from_value (cooked);
/* 5. */
ecma_value_t raw = ecma_op_object_get_by_magic_id (cooked_obj_p, LIT_MAGIC_STRING_RAW);
ecma_deref_object (cooked_obj_p);
if (ECMA_IS_VALUE_ERROR (raw))
{
return raw;
}
ecma_value_t raw_obj = ecma_op_to_object (raw);
/* 6. */
if (ECMA_IS_VALUE_ERROR (raw_obj))
{
ecma_free_value (raw);
return raw_obj;
}
ecma_object_t *raw_obj_p = ecma_get_object_from_value (raw_obj);
ecma_value_t ret_value = ECMA_VALUE_ERROR;
/* 7 - 8. */
uint32_t literal_segments;
if (ECMA_IS_VALUE_ERROR (ecma_op_object_get_length (raw_obj_p, &literal_segments)))
{
goto cleanup;
}
/* 9. */
if (literal_segments == 0)
{
ret_value = ecma_make_magic_string_value (LIT_MAGIC_STRING__EMPTY);
goto cleanup;
}
/* 10. */
ecma_stringbuilder_t builder = ecma_stringbuilder_create ();
/* 11. */
uint32_t next_index = 0;
/* 12. */
while (true)
{
/* 12.a,b */
ecma_value_t next_seg = ecma_op_object_get_by_uint32_index (raw_obj_p, next_index);
if (ECMA_IS_VALUE_ERROR (next_seg))
{
goto builder_cleanup;
}
ecma_string_t *next_seg_srt_p = ecma_op_to_string (next_seg);
/* 12.c */
if (JERRY_UNLIKELY (next_seg_srt_p == NULL))
{
ecma_free_value (next_seg);
goto builder_cleanup;
}
/* 12.d */
ecma_stringbuilder_append (&builder, next_seg_srt_p);
ecma_deref_ecma_string (next_seg_srt_p);
ecma_free_value (next_seg);
/* 12.e */
if (next_index + 1 == literal_segments)
{
ret_value = ecma_make_string_value (ecma_stringbuilder_finalize (&builder));
goto cleanup;
}
/* 12.f-g */
if (next_index >= number_of_substitutions)
{
continue;
}
/* 12.h */
ecma_string_t *next_sub_p = ecma_op_to_string (substitutions[next_index]);
/* 12.i */
if (JERRY_UNLIKELY (next_sub_p == NULL))
{
goto builder_cleanup;
}
/* 12.j */
ecma_stringbuilder_append (&builder, next_sub_p);
ecma_deref_ecma_string (next_sub_p);
/* 12.k */
next_index++;
}
builder_cleanup:
ecma_stringbuilder_destroy (&builder);
cleanup:
ecma_deref_object (raw_obj_p);
ecma_free_value (raw);
return ret_value;
} /* ecma_builtin_string_object_raw */
/**
* The String object's 'fromCodePoint' routine
*
@@ -43,6 +43,7 @@ ROUTINE (LIT_MAGIC_STRING_FROM_CHAR_CODE_UL, ecma_builtin_string_object_from_cha
#if ENABLED (JERRY_ES2015)
ROUTINE (LIT_MAGIC_STRING_FROM_CODE_POINT_UL, ecma_builtin_string_object_from_code_point, NON_FIXED, 1)
ROUTINE (LIT_MAGIC_STRING_RAW, ecma_builtin_string_object_raw, NON_FIXED, 1)
#endif /* ENABLED (JERRY_ES2015) */
#endif /* ENABLED (JERRY_BUILTIN_STRING) */
@@ -45,29 +45,18 @@ ecma_op_resource_name (const ecma_compiled_code_t *bytecode_header_p)
{
JERRY_ASSERT (bytecode_header_p != NULL);
ecma_length_t formal_params_number = 0;
if (bytecode_header_p->status_flags & CBC_CODE_FLAGS_MAPPED_ARGUMENTS_NEEDED)
{
if (bytecode_header_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS)
{
cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) bytecode_header_p;
formal_params_number = args_p->argument_end;
}
else
{
cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) bytecode_header_p;
formal_params_number = args_p->argument_end;
}
}
uint8_t *byte_p = (uint8_t *) bytecode_header_p;
byte_p += ((size_t) bytecode_header_p->size) << JMEM_ALIGNMENT_LOG;
ecma_value_t *resource_name_p = (ecma_value_t *) byte_p;
resource_name_p -= formal_params_number;
resource_name_p -= ecma_compiled_code_get_formal_params (bytecode_header_p);
#if ENABLED (JERRY_ES2015)
if (bytecode_header_p->status_flags & CBC_CODE_FLAG_HAS_TAGGED_LITERALS)
{
resource_name_p--;
}
#endif /* ENABLED (JERRY_ES2015) */
return resource_name_p[-1];
} /* ecma_op_resource_name */
+1 -1
View File
@@ -30,7 +30,7 @@ extern "C"
/**
* Jerry snapshot format version.
*/
#define JERRY_SNAPSHOT_VERSION (34u)
#define JERRY_SNAPSHOT_VERSION (35u)
/**
* Flags for jerry_generate_snapshot and jerry_generate_function_snapshot.
+3
View File
@@ -102,6 +102,9 @@ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_POP, "pop")
#if ENABLED (JERRY_BUILTIN_MATH)
LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_POW, "pow")
#endif
#if ENABLED (JERRY_ES2015)
LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_RAW, "raw")
#endif
LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SET, "set")
#if ENABLED (JERRY_BUILTIN_MATH)
LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SIN, "sin")
+1
View File
@@ -54,6 +54,7 @@ LIT_MAGIC_STRING_MIN = "min"
LIT_MAGIC_STRING_NOW = "now"
LIT_MAGIC_STRING_POP = "pop"
LIT_MAGIC_STRING_POW = "pow"
LIT_MAGIC_STRING_RAW = "raw"
LIT_MAGIC_STRING_SET = "set"
LIT_MAGIC_STRING_SIN = "sin"
LIT_MAGIC_STRING_TAN = "tan"
+9
View File
@@ -662,6 +662,14 @@
VM_OC_YIELD | VM_OC_GET_STACK) \
CBC_OPCODE (CBC_EXT_RETURN, CBC_NO_FLAG, -1, \
VM_OC_EXT_RETURN | VM_OC_GET_STACK) \
CBC_OPCODE (CBC_EXT_STRING_CONCAT, CBC_NO_FLAG, -1, \
VM_OC_STRING_CONCAT | VM_OC_GET_STACK_STACK | VM_OC_PUT_STACK) \
CBC_OPCODE (CBC_EXT_STRING_CONCAT_RIGHT_LITERAL, CBC_HAS_LITERAL_ARG, 0, \
VM_OC_STRING_CONCAT | VM_OC_GET_STACK_LITERAL | VM_OC_PUT_STACK) \
CBC_OPCODE (CBC_EXT_STRING_CONCAT_TWO_LITERALS, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, 1, \
VM_OC_STRING_CONCAT | VM_OC_GET_LITERAL_LITERAL | VM_OC_PUT_STACK) \
CBC_OPCODE (CBC_EXT_GET_TAGGED_TEMPLATE_LITERAL, CBC_HAS_BYTE_ARG, 1, \
VM_OC_GET_TEMPLATE_OBJECT | VM_OC_PUT_STACK) \
\
/* Last opcode (not a real opcode). */ \
CBC_OPCODE (CBC_EXT_END, CBC_NO_FLAG, 0, \
@@ -753,6 +761,7 @@ typedef enum
CBC_CODE_FLAGS_CONSTRUCTOR = (1u << 10), /**< this function is a constructor */
CBC_CODE_FLAGS_GENERATOR = (1u << 11), /**< this function is a generator */
CBC_CODE_FLAGS_REST_PARAMETER = (1u << 12), /**< this function has rest parameter */
CBC_CODE_FLAG_HAS_TAGGED_LITERALS = (1u << 13), /**< this function has tagged template literal list */
} cbc_code_flags;
/**
+267 -227
View File
@@ -874,8 +874,15 @@ lexer_parse_identifier (parser_context_t *context_p, /**< context */
* Parse string.
*/
void
lexer_parse_string (parser_context_t *context_p) /**< context */
lexer_parse_string (parser_context_t *context_p, /**< context */
lexer_string_options_t opts) /**< options */
{
#if ENABLED (JERRY_ES2015)
const size_t raw_length_inc = (opts & LEXER_STRING_RAW) ? 1 : 0;
#else /* ENABLED (JERRY_ES2015) */
JERRY_UNUSED (opts);
#endif /* ENABLED (JERRY_ES2015) */
uint8_t str_end_character = context_p->source_p[0];
const uint8_t *source_p = context_p->source_p + 1;
const uint8_t *string_start_p = source_p;
@@ -918,6 +925,10 @@ lexer_parse_string (parser_context_t *context_p) /**< context */
continue;
}
#if ENABLED (JERRY_ES2015)
length += raw_length_inc;
#endif /* ENABLED (JERRY_ES2015) */
has_escape = true;
/* Newline is ignored. */
@@ -931,6 +942,9 @@ lexer_parse_string (parser_context_t *context_p) /**< context */
}
line++;
#if ENABLED (JERRY_ES2015)
length += raw_length_inc;
#endif /* ENABLED (JERRY_ES2015) */
column = 1;
continue;
}
@@ -938,6 +952,9 @@ lexer_parse_string (parser_context_t *context_p) /**< context */
{
source_p++;
line++;
#if ENABLED (JERRY_ES2015)
length += raw_length_inc;
#endif /* ENABLED (JERRY_ES2015) */
column = 1;
continue;
}
@@ -949,6 +966,13 @@ lexer_parse_string (parser_context_t *context_p) /**< context */
continue;
}
#if ENABLED (JERRY_ES2015)
if (opts & LEXER_STRING_RAW)
{
continue;
}
#endif /* ENABLED (JERRY_ES2015) */
if (*source_p == LIT_CHAR_0
&& source_p + 1 < source_end_p
&& (*(source_p + 1) < LIT_CHAR_0 || *(source_p + 1) > LIT_CHAR_9))
@@ -1086,16 +1110,17 @@ lexer_parse_string (parser_context_t *context_p) /**< context */
#if ENABLED (JERRY_ES2015)
else if (str_end_character == LIT_CHAR_GRAVE_ACCENT)
{
/* Newline (without backslash) is part of the string. */
/* Newline (without backslash) is part of the string.
Note: ECMAScript v6, 11.8.6.1 <CR> or <CR><LF> are both normalized to <LF> */
if (*source_p == LIT_CHAR_CR)
{
has_escape = true;
source_p++;
length++;
if (source_p < source_end_p
&& *source_p == LIT_CHAR_LF)
{
source_p++;
length++;
}
line++;
column = 1;
@@ -1658,7 +1683,7 @@ lexer_next_token (parser_context_t *context_p) /**< context */
case LIT_CHAR_GRAVE_ACCENT:
#endif /* ENABLED (JERRY_ES2015) */
{
lexer_parse_string (context_p);
lexer_parse_string (context_p, LEXER_STRING_NO_OPTS);
return;
}
@@ -1931,11 +1956,6 @@ lexer_process_char_literal (parser_context_t *context_p, /**< context */
context_p->literal_count++;
} /* lexer_process_char_literal */
/**
* Maximum local buffer size for identifiers which contains escape sequences.
*/
#define LEXER_MAX_LITERAL_LOCAL_BUFFER_SIZE 48
/**
* Convert an ident with escapes to a utf8 string.
*/
@@ -1974,6 +1994,238 @@ lexer_convert_ident_to_cesu8 (uint8_t *destination_p, /**< destination string */
while (destination_p < destination_end_p);
} /* lexer_convert_ident_to_cesu8 */
/**
* Convert literal to character sequence
*/
const uint8_t *
lexer_convert_literal_to_chars (parser_context_t *context_p, /**< context */
const lexer_lit_location_t *literal_p, /**< literal location */
uint8_t *local_byte_array_p, /**< local byte array to store chars */
lexer_string_options_t opts) /**< options */
{
JERRY_ASSERT (context_p->u.allocated_buffer_p == NULL);
if (!literal_p->has_escape)
{
return literal_p->char_p;
}
uint8_t *destination_start_p;
if (literal_p->length > LEXER_MAX_LITERAL_LOCAL_BUFFER_SIZE)
{
context_p->u.allocated_buffer_p = (uint8_t *) parser_malloc_local (context_p, literal_p->length);
context_p->allocated_buffer_size = literal_p->length;
destination_start_p = context_p->u.allocated_buffer_p;
}
else
{
destination_start_p = local_byte_array_p;
}
if (literal_p->type == LEXER_IDENT_LITERAL)
{
lexer_convert_ident_to_cesu8 (destination_start_p, literal_p->char_p, literal_p->length);
return destination_start_p;
}
const uint8_t *source_p = literal_p->char_p;
uint8_t *destination_p = destination_start_p;
uint8_t str_end_character = source_p[-1];
#if ENABLED (JERRY_ES2015)
if (str_end_character == LIT_CHAR_RIGHT_BRACE)
{
str_end_character = LIT_CHAR_GRAVE_ACCENT;
}
bool is_raw = (opts & LEXER_STRING_RAW) != 0;
#else /* !ENABLED (JERRY_ES2015) */
JERRY_UNUSED (opts);
bool is_raw = false;
#endif /* ENABLED (JERRY_ES2015) */
while (true)
{
if (*source_p == str_end_character)
{
break;
}
if (*source_p == LIT_CHAR_BACKSLASH && !is_raw)
{
uint8_t conv_character;
source_p++;
JERRY_ASSERT (source_p < context_p->source_end_p);
/* Newline is ignored. */
if (*source_p == LIT_CHAR_CR)
{
source_p++;
JERRY_ASSERT (source_p < context_p->source_end_p);
if (*source_p == LIT_CHAR_LF)
{
source_p++;
}
continue;
}
else if (*source_p == LIT_CHAR_LF)
{
source_p++;
continue;
}
else if (*source_p == LEXER_NEWLINE_LS_PS_BYTE_1 && LEXER_NEWLINE_LS_PS_BYTE_23 (source_p))
{
source_p += 3;
continue;
}
if (*source_p >= LIT_CHAR_0 && *source_p <= LIT_CHAR_3)
{
lit_code_point_t octal_number = (uint32_t) (*source_p - LIT_CHAR_0);
source_p++;
JERRY_ASSERT (source_p < context_p->source_end_p);
if (*source_p >= LIT_CHAR_0 && *source_p <= LIT_CHAR_7)
{
octal_number = octal_number * 8 + (uint32_t) (*source_p - LIT_CHAR_0);
source_p++;
JERRY_ASSERT (source_p < context_p->source_end_p);
if (*source_p >= LIT_CHAR_0 && *source_p <= LIT_CHAR_7)
{
octal_number = octal_number * 8 + (uint32_t) (*source_p - LIT_CHAR_0);
source_p++;
JERRY_ASSERT (source_p < context_p->source_end_p);
}
}
destination_p += lit_code_point_to_cesu8_bytes (destination_p, octal_number);
continue;
}
if (*source_p >= LIT_CHAR_4 && *source_p <= LIT_CHAR_7)
{
uint32_t octal_number = (uint32_t) (*source_p - LIT_CHAR_0);
source_p++;
JERRY_ASSERT (source_p < context_p->source_end_p);
if (*source_p >= LIT_CHAR_0 && *source_p <= LIT_CHAR_7)
{
octal_number = octal_number * 8 + (uint32_t) (*source_p - LIT_CHAR_0);
source_p++;
JERRY_ASSERT (source_p < context_p->source_end_p);
}
*destination_p++ = (uint8_t) octal_number;
continue;
}
if (*source_p == LIT_CHAR_LOWERCASE_X || *source_p == LIT_CHAR_LOWERCASE_U)
{
source_p++;
destination_p += lit_code_point_to_cesu8_bytes (destination_p,
lexer_unchecked_hex_to_character (&source_p));
continue;
}
conv_character = *source_p;
switch (*source_p)
{
case LIT_CHAR_LOWERCASE_B:
{
conv_character = 0x08;
break;
}
case LIT_CHAR_LOWERCASE_T:
{
conv_character = 0x09;
break;
}
case LIT_CHAR_LOWERCASE_N:
{
conv_character = 0x0a;
break;
}
case LIT_CHAR_LOWERCASE_V:
{
conv_character = 0x0b;
break;
}
case LIT_CHAR_LOWERCASE_F:
{
conv_character = 0x0c;
break;
}
case LIT_CHAR_LOWERCASE_R:
{
conv_character = 0x0d;
break;
}
}
if (conv_character != *source_p)
{
*destination_p++ = conv_character;
source_p++;
continue;
}
}
#if ENABLED (JERRY_ES2015)
else if (str_end_character == LIT_CHAR_GRAVE_ACCENT)
{
if (source_p[0] == LIT_CHAR_DOLLAR_SIGN
&& source_p[1] == LIT_CHAR_LEFT_BRACE)
{
source_p++;
JERRY_ASSERT (source_p < context_p->source_end_p);
break;
}
if (*source_p == LIT_CHAR_CR)
{
*destination_p++ = LIT_CHAR_LF;
source_p++;
if (*source_p != str_end_character
&& *source_p == LIT_CHAR_LF)
{
source_p++;
}
continue;
}
}
#endif /* ENABLED (JERRY_ES2015) */
if (*source_p >= LEXER_UTF8_4BYTE_START)
{
/* Processing 4 byte unicode sequence (even if it is
* after a backslash). Always converted to two 3 byte
* long sequence. */
lit_four_byte_utf8_char_to_cesu8 (destination_p, source_p);
destination_p += 6;
source_p += 4;
continue;
}
*destination_p++ = *source_p++;
/* There is no need to check the source_end_p
* since the string is terminated by a quotation mark. */
while (IS_UTF8_INTERMEDIATE_OCTET (*source_p))
{
*destination_p++ = *source_p++;
}
}
JERRY_ASSERT (destination_p == destination_start_p + literal_p->length);
return destination_start_p;
} /* lexer_convert_literal_to_chars */
/**
* Construct a literal object from an identifier.
*/
@@ -1982,213 +2234,11 @@ lexer_construct_literal_object (parser_context_t *context_p, /**< context */
const lexer_lit_location_t *literal_p, /**< literal location */
uint8_t literal_type) /**< final literal type */
{
uint8_t *destination_start_p;
const uint8_t *source_p;
uint8_t local_byte_array[LEXER_MAX_LITERAL_LOCAL_BUFFER_SIZE];
JERRY_ASSERT (literal_p->type == LEXER_IDENT_LITERAL
|| literal_p->type == LEXER_STRING_LITERAL);
JERRY_ASSERT (context_p->u.allocated_buffer_p == NULL);
destination_start_p = local_byte_array;
source_p = literal_p->char_p;
if (literal_p->has_escape)
{
uint8_t *destination_p;
if (literal_p->length > LEXER_MAX_LITERAL_LOCAL_BUFFER_SIZE)
{
destination_start_p = (uint8_t *) parser_malloc_local (context_p, literal_p->length);
context_p->u.allocated_buffer_p = destination_start_p;
context_p->allocated_buffer_size = literal_p->length;
}
destination_p = destination_start_p;
if (literal_p->type == LEXER_IDENT_LITERAL)
{
lexer_convert_ident_to_cesu8 (destination_start_p, source_p, literal_p->length);
}
else
{
uint8_t str_end_character = source_p[-1];
#if ENABLED (JERRY_ES2015)
if (str_end_character == LIT_CHAR_RIGHT_BRACE)
{
str_end_character = LIT_CHAR_GRAVE_ACCENT;
}
#endif /* ENABLED (JERRY_ES2015) */
while (true)
{
if (*source_p == str_end_character)
{
break;
}
if (*source_p == LIT_CHAR_BACKSLASH)
{
uint8_t conv_character;
source_p++;
JERRY_ASSERT (source_p < context_p->source_end_p);
/* Newline is ignored. */
if (*source_p == LIT_CHAR_CR)
{
source_p++;
JERRY_ASSERT (source_p < context_p->source_end_p);
if (*source_p == LIT_CHAR_LF)
{
source_p++;
}
continue;
}
else if (*source_p == LIT_CHAR_LF)
{
source_p++;
continue;
}
else if (*source_p == LEXER_NEWLINE_LS_PS_BYTE_1 && LEXER_NEWLINE_LS_PS_BYTE_23 (source_p))
{
source_p += 3;
continue;
}
if (*source_p >= LIT_CHAR_0 && *source_p <= LIT_CHAR_3)
{
lit_code_point_t octal_number = (uint32_t) (*source_p - LIT_CHAR_0);
source_p++;
JERRY_ASSERT (source_p < context_p->source_end_p);
if (*source_p >= LIT_CHAR_0 && *source_p <= LIT_CHAR_7)
{
octal_number = octal_number * 8 + (uint32_t) (*source_p - LIT_CHAR_0);
source_p++;
JERRY_ASSERT (source_p < context_p->source_end_p);
if (*source_p >= LIT_CHAR_0 && *source_p <= LIT_CHAR_7)
{
octal_number = octal_number * 8 + (uint32_t) (*source_p - LIT_CHAR_0);
source_p++;
JERRY_ASSERT (source_p < context_p->source_end_p);
}
}
destination_p += lit_code_point_to_cesu8_bytes (destination_p, octal_number);
continue;
}
if (*source_p >= LIT_CHAR_4 && *source_p <= LIT_CHAR_7)
{
uint32_t octal_number = (uint32_t) (*source_p - LIT_CHAR_0);
source_p++;
JERRY_ASSERT (source_p < context_p->source_end_p);
if (*source_p >= LIT_CHAR_0 && *source_p <= LIT_CHAR_7)
{
octal_number = octal_number * 8 + (uint32_t) (*source_p - LIT_CHAR_0);
source_p++;
JERRY_ASSERT (source_p < context_p->source_end_p);
}
*destination_p++ = (uint8_t) octal_number;
continue;
}
if (*source_p == LIT_CHAR_LOWERCASE_X || *source_p == LIT_CHAR_LOWERCASE_U)
{
source_p++;
destination_p += lit_code_point_to_cesu8_bytes (destination_p,
lexer_unchecked_hex_to_character (&source_p));
continue;
}
conv_character = *source_p;
switch (*source_p)
{
case LIT_CHAR_LOWERCASE_B:
{
conv_character = 0x08;
break;
}
case LIT_CHAR_LOWERCASE_T:
{
conv_character = 0x09;
break;
}
case LIT_CHAR_LOWERCASE_N:
{
conv_character = 0x0a;
break;
}
case LIT_CHAR_LOWERCASE_V:
{
conv_character = 0x0b;
break;
}
case LIT_CHAR_LOWERCASE_F:
{
conv_character = 0x0c;
break;
}
case LIT_CHAR_LOWERCASE_R:
{
conv_character = 0x0d;
break;
}
}
if (conv_character != *source_p)
{
*destination_p++ = conv_character;
source_p++;
continue;
}
}
#if ENABLED (JERRY_ES2015)
else if (str_end_character == LIT_CHAR_GRAVE_ACCENT
&& source_p[0] == LIT_CHAR_DOLLAR_SIGN
&& source_p[1] == LIT_CHAR_LEFT_BRACE)
{
source_p++;
JERRY_ASSERT (source_p < context_p->source_end_p);
break;
}
#endif /* ENABLED (JERRY_ES2015) */
if (*source_p >= LEXER_UTF8_4BYTE_START)
{
/* Processing 4 byte unicode sequence (even if it is
* after a backslash). Always converted to two 3 byte
* long sequence. */
lit_four_byte_utf8_char_to_cesu8 (destination_p, source_p);
destination_p += 6;
source_p += 4;
continue;
}
*destination_p++ = *source_p++;
/* There is no need to check the source_end_p
* since the string is terminated by a quotation mark. */
while (IS_UTF8_INTERMEDIATE_OCTET (*source_p))
{
*destination_p++ = *source_p++;
}
}
JERRY_ASSERT (destination_p == destination_start_p + literal_p->length);
}
source_p = destination_start_p;
}
const uint8_t *source_p = lexer_convert_literal_to_chars (context_p,
literal_p,
local_byte_array,
LEXER_STRING_NO_OPTS);
lexer_process_char_literal (context_p,
source_p,
@@ -2196,20 +2246,10 @@ lexer_construct_literal_object (parser_context_t *context_p, /**< context */
literal_type,
literal_p->has_escape);
if (destination_start_p != local_byte_array)
{
JERRY_ASSERT (context_p->u.allocated_buffer_p == destination_start_p);
context_p->u.allocated_buffer_p = NULL;
parser_free_local (destination_start_p,
context_p->allocated_buffer_size);
}
parser_free_allocated_buffer (context_p);
JERRY_ASSERT (context_p->u.allocated_buffer_p == NULL);
} /* lexer_construct_literal_object */
#undef LEXER_MAX_LITERAL_LOCAL_BUFFER_SIZE
/**
* Construct a number object.
*
@@ -2784,7 +2824,7 @@ lexer_expect_object_literal_id (parser_context_t *context_p, /**< context */
case LIT_CHAR_DOUBLE_QUOTE:
case LIT_CHAR_SINGLE_QUOTE:
{
lexer_parse_string (context_p);
lexer_parse_string (context_p, LEXER_STRING_NO_OPTS);
create_literal_object = true;
break;
}
+14
View File
@@ -214,6 +214,11 @@ typedef enum
#define LEXER_BINARY_LVALUE_OP_TOKEN_TO_OPCODE(token_type) \
((cbc_opcode_t) ((((token_type) - LEXER_ASSIGN_ADD) * 2) + CBC_ASSIGN_ADD))
/**
* Maximum local buffer size for identifiers which contains escape sequences.
*/
#define LEXER_MAX_LITERAL_LOCAL_BUFFER_SIZE 48
/**
* Lexer newline flags.
*/
@@ -234,6 +239,15 @@ typedef enum
LEXER_OBJ_IDENT_OBJECT_PATTERN = (1u << 3), /**< parse "get"/"set" as string literal in object pattern */
} lexer_obj_ident_opts_t;
/**
* Lexer string options.
*/
typedef enum
{
LEXER_STRING_NO_OPTS = (1u << 0), /**< no options */
LEXER_STRING_RAW = (1u << 1), /**< raw string ECMAScript v6, 11.8.6.1: TVR */
} lexer_string_options_t;
/**
* Lexer scan identifier parse options.
*/
+118 -41
View File
@@ -20,6 +20,7 @@
#include "ecma-helpers.h"
#include "lit-char-helpers.h"
#include "js-parser-tagged-template-literal.h"
/** \addtogroup parser Parser
* @{
@@ -1142,21 +1143,21 @@ parser_parse_template_literal (parser_context_t *context_p) /**< context */
{
if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS)
{
context_p->last_cbc_opcode = CBC_ADD_TWO_LITERALS;
context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_STRING_CONCAT_TWO_LITERALS);
}
else if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL)
{
context_p->last_cbc_opcode = CBC_ADD_RIGHT_LITERAL;
context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_STRING_CONCAT_RIGHT_LITERAL);
}
else
{
parser_emit_cbc (context_p, CBC_ADD);
parser_emit_cbc_ext (context_p, CBC_EXT_STRING_CONCAT);
}
}
context_p->source_p--;
context_p->column--;
lexer_parse_string (context_p);
lexer_parse_string (context_p, LEXER_STRING_NO_OPTS);
if (is_empty_head || context_p->token.lit_location.length > 0)
{
@@ -1166,20 +1167,21 @@ parser_parse_template_literal (parser_context_t *context_p) /**< context */
if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL)
{
context_p->last_cbc_opcode = CBC_ADD_TWO_LITERALS;
context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_STRING_CONCAT_TWO_LITERALS);
context_p->last_cbc.value = context_p->lit_object.index;
context_p->last_cbc.literal_type = context_p->token.lit_location.type;
context_p->last_cbc.literal_keyword_type = context_p->token.keyword_type;
}
else
{
parser_emit_cbc_literal_from_token (context_p, CBC_ADD_RIGHT_LITERAL);
parser_emit_cbc_ext_literal_from_token (context_p, CBC_EXT_STRING_CONCAT_RIGHT_LITERAL);
}
}
while (context_p->source_p[-1] != LIT_CHAR_GRAVE_ACCENT)
{
lexer_next_token (context_p);
parser_parse_expression (context_p, PARSE_EXPR);
if (context_p->token.type != LEXER_RIGHT_BRACE)
@@ -1189,16 +1191,16 @@ parser_parse_template_literal (parser_context_t *context_p) /**< context */
if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL)
{
context_p->last_cbc_opcode = CBC_ADD_RIGHT_LITERAL;
context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_STRING_CONCAT_RIGHT_LITERAL);
}
else
{
parser_emit_cbc (context_p, CBC_ADD);
parser_emit_cbc_ext (context_p, CBC_EXT_STRING_CONCAT);
}
context_p->source_p--;
context_p->column--;
lexer_parse_string (context_p);
lexer_parse_string (context_p, LEXER_STRING_NO_OPTS);
if (context_p->token.lit_location.length > 0)
{
@@ -1206,12 +1208,75 @@ parser_parse_template_literal (parser_context_t *context_p) /**< context */
&context_p->token.lit_location,
context_p->token.lit_location.type);
parser_emit_cbc_literal_from_token (context_p, CBC_ADD_RIGHT_LITERAL);
parser_emit_cbc_ext_literal_from_token (context_p, CBC_EXT_STRING_CONCAT_RIGHT_LITERAL);
}
}
} /* parser_parse_template_literal */
/**
* Parse tagged template literal.
*/
static size_t
parser_parse_tagged_template_literal (parser_context_t *context_p) /**< context */
{
JERRY_ASSERT (context_p->token.type == LEXER_TEMPLATE_LITERAL);
uint32_t call_arguments = 0;
ecma_collection_t *collection_p;
if (context_p->tagged_template_literal_cp == JMEM_CP_NULL)
{
collection_p = ecma_new_collection ();
ECMA_SET_INTERNAL_VALUE_POINTER (context_p->tagged_template_literal_cp, collection_p);
}
else
{
collection_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, context_p->tagged_template_literal_cp);
if (collection_p->item_count > CBC_MAXIMUM_BYTE_VALUE)
{
parser_raise_error (context_p, PARSER_ERR_ARGUMENT_LIMIT_REACHED);
}
}
return;
} /* parser_parse_template_literal */
const uint32_t tagged_id = collection_p->item_count;
uint32_t prop_idx = 0;
ecma_object_t *raw_strings_p;
ecma_object_t *template_obj_p = parser_new_tagged_template_literal (&raw_strings_p);
ecma_collection_push_back (collection_p, ecma_make_object_value (template_obj_p));
parser_tagged_template_literal_append_strings (context_p, template_obj_p, raw_strings_p, prop_idx++);
call_arguments++;
parser_emit_cbc_ext_call (context_p, CBC_EXT_GET_TAGGED_TEMPLATE_LITERAL, tagged_id);
while (context_p->source_p[-1] != LIT_CHAR_GRAVE_ACCENT)
{
JERRY_ASSERT (context_p->source_p[-1] == LIT_CHAR_LEFT_BRACE);
lexer_next_token (context_p);
if (++call_arguments > CBC_MAXIMUM_BYTE_VALUE)
{
parser_raise_error (context_p, PARSER_ERR_ARGUMENT_LIMIT_REACHED);
}
parser_parse_expression (context_p, PARSE_EXPR);
if (context_p->token.type != LEXER_RIGHT_BRACE)
{
parser_raise_error (context_p, PARSER_ERR_RIGHT_BRACE_EXPECTED);
}
context_p->source_p--;
context_p->column--;
lexer_parse_string (context_p, LEXER_STRING_NO_OPTS);
parser_tagged_template_literal_append_strings (context_p, template_obj_p, raw_strings_p, prop_idx++);
}
parser_tagged_template_literal_finalize (template_obj_p, raw_strings_p);
return call_arguments;
} /* parser_parse_tagged_template_literal */
/**
* Throws an error if the current expression is not an assignment expression.
@@ -1681,6 +1746,9 @@ parser_process_unary_expression (parser_context_t *context_p, /**< context */
continue;
}
#if ENABLED (JERRY_ES2015)
case LEXER_TEMPLATE_LITERAL:
#endif /* ENABLED (JERRY_ES2015) */
case LEXER_LEFT_PAREN:
{
size_t call_arguments = 0;
@@ -1724,45 +1792,54 @@ parser_process_unary_expression (parser_context_t *context_p, /**< context */
}
}
lexer_next_token (context_p);
#if ENABLED (JERRY_ES2015)
bool has_spread_element = false;
#endif /* ENABLED (JERRY_ES2015) */
if (context_p->token.type != LEXER_RIGHT_PAREN)
if (context_p->token.type == LEXER_TEMPLATE_LITERAL)
{
call_arguments = parser_parse_tagged_template_literal (context_p);
}
else
{
while (true)
{
if (++call_arguments > CBC_MAXIMUM_BYTE_VALUE)
{
parser_raise_error (context_p, PARSER_ERR_ARGUMENT_LIMIT_REACHED);
}
#if ENABLED (JERRY_ES2015)
if (context_p->token.type == LEXER_THREE_DOTS)
{
has_spread_element = true;
call_arguments++;
parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_SPREAD_ELEMENT);
lexer_next_token (context_p);
}
#endif /* ENABLED (JERRY_ES2015) */
parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA);
if (context_p->token.type != LEXER_COMMA)
{
break;
}
lexer_next_token (context_p);
}
lexer_next_token (context_p);
if (context_p->token.type != LEXER_RIGHT_PAREN)
{
parser_raise_error (context_p, PARSER_ERR_RIGHT_PAREN_EXPECTED);
while (true)
{
if (++call_arguments > CBC_MAXIMUM_BYTE_VALUE)
{
parser_raise_error (context_p, PARSER_ERR_ARGUMENT_LIMIT_REACHED);
}
#if ENABLED (JERRY_ES2015)
if (context_p->token.type == LEXER_THREE_DOTS)
{
has_spread_element = true;
call_arguments++;
parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_SPREAD_ELEMENT);
lexer_next_token (context_p);
}
#endif /* ENABLED (JERRY_ES2015) */
parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA);
if (context_p->token.type != LEXER_COMMA)
{
break;
}
lexer_next_token (context_p);
}
if (context_p->token.type != LEXER_RIGHT_PAREN)
{
parser_raise_error (context_p, PARSER_ERR_RIGHT_PAREN_EXPECTED);
}
}
#if ENABLED (JERRY_ES2015)
}
#endif /* ENABLED (JERRY_ES2015) */
lexer_next_token (context_p);
+9 -1
View File
@@ -421,6 +421,7 @@ typedef struct parser_saved_context_t
uint16_t scope_stack_reg_top; /**< preserved top register of scope stack */
#if ENABLED (JERRY_ES2015)
uint16_t scope_stack_global_end; /**< end of global declarations of a function */
ecma_value_t tagged_template_literal_cp; /**< compessed pointer to the tagged template literal collection */
#endif /* ENABLED (JERRY_ES2015) */
#ifndef JERRY_NDEBUG
@@ -491,6 +492,7 @@ typedef struct
uint16_t scope_stack_reg_top; /**< current top register of scope stack */
#if ENABLED (JERRY_ES2015)
uint16_t scope_stack_global_end; /**< end of global declarations of a function */
ecma_value_t tagged_template_literal_cp; /**< compessed pointer to the tagged template literal collection */
#endif /* ENABLED (JERRY_ES2015) */
uint8_t stack_top_uint8; /**< top byte stored on the stack */
@@ -533,6 +535,7 @@ void *parser_malloc (parser_context_t *context_p, size_t size);
void parser_free (void *ptr, size_t size);
void *parser_malloc_local (parser_context_t *context_p, size_t size);
void parser_free_local (void *ptr, size_t size);
void parser_free_allocated_buffer (parser_context_t *context_p);
/* Parser byte stream. */
@@ -602,6 +605,8 @@ void parser_set_continues_to_current_position (parser_context_t *context_p, pars
parser_emit_cbc ((context_p), PARSER_TO_EXT_OPCODE (opcode))
#define parser_emit_cbc_ext_literal(context_p, opcode, literal_index) \
parser_emit_cbc_literal ((context_p), PARSER_TO_EXT_OPCODE (opcode), (literal_index))
#define parser_emit_cbc_ext_literal_from_token(context_p, opcode) \
parser_emit_cbc_literal_from_token ((context_p), PARSER_TO_EXT_OPCODE (opcode))
#define parser_emit_cbc_ext_call(context_p, opcode, call_arguments) \
parser_emit_cbc_call ((context_p), PARSER_TO_EXT_OPCODE (opcode), (call_arguments))
#define parser_emit_cbc_ext_call(context_p, opcode, call_arguments) \
@@ -631,10 +636,13 @@ bool lexer_check_arrow (parser_context_t *context_p);
bool lexer_check_arrow_param (parser_context_t *context_p);
bool lexer_check_yield_no_arg (parser_context_t *context_p);
#endif /* ENABLED (JERRY_ES2015) */
void lexer_parse_string (parser_context_t *context_p);
void lexer_parse_string (parser_context_t *context_p, lexer_string_options_t opts);
void lexer_expect_identifier (parser_context_t *context_p, uint8_t literal_type);
void lexer_scan_identifier (parser_context_t *context_p, uint32_t ident_opts);
void lexer_convert_ident_to_cesu8 (uint8_t *destination_p, const uint8_t *source_p, prop_length_t length);
const uint8_t *lexer_convert_literal_to_chars (parser_context_t *context_p, const lexer_lit_location_t *literal_p,
uint8_t *local_byte_array_p, lexer_string_options_t opts);
void lexer_expect_object_literal_id (parser_context_t *context_p, uint32_t ident_opts);
void lexer_construct_literal_object (parser_context_t *context_p, const lexer_lit_location_t *literal_p,
uint8_t literal_type);
+17 -2
View File
@@ -82,12 +82,27 @@ parser_malloc_local (parser_context_t *context_p, /**< context */
/**
* Free memory allocated by parser_malloc_local.
*/
void parser_free_local (void *ptr, /**< pointer to free */
size_t size) /**< size of the memory */
void
parser_free_local (void *ptr, /**< pointer to free */
size_t size) /**< size of the memory */
{
jmem_heap_free_block (ptr, size);
} /* parser_free_local */
/**
* Free the dynamically allocated buffer stored in the context
*/
void
parser_free_allocated_buffer (parser_context_t *context_p) /**< context */
{
if (context_p->u.allocated_buffer_p != NULL)
{
parser_free_local (context_p->u.allocated_buffer_p,
context_p->allocated_buffer_size);
context_p->u.allocated_buffer_p = NULL;
}
} /* parser_free_allocated_buffer */
/**********************************************************************/
/* Parser data management functions */
/**********************************************************************/
@@ -0,0 +1,153 @@
/* 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 "js-parser-tagged-template-literal.h"
#include "js-lexer.h"
#include "ecma-array-object.h"
#include "ecma-builtin-helpers.h"
#include "ecma-gc.h"
#include "ecma-helpers.h"
/* \addtogroup parser Parser
* @{
*
* \addtogroup jsparser JavaScript
* @{
*
* \addtogroup jsparser_tagged_template_literal Tagged template literal
* @{
*/
#if ENABLED (JERRY_ES2015)
/**
* Append the cooked and raw string to the corresponding array
*/
void
parser_tagged_template_literal_append_strings (parser_context_t *context_p, /**< parser context */
ecma_object_t *template_obj_p, /**< template object */
ecma_object_t *raw_strings_p, /**< raw strings object */
uint32_t prop_idx) /**< property index to set the values */
{
lexer_lit_location_t *lit_loc_p = &context_p->token.lit_location;
if (lit_loc_p->length == 0)
{
ecma_builtin_helper_def_prop_by_index (template_obj_p,
prop_idx,
ecma_make_magic_string_value (LIT_MAGIC_STRING__EMPTY),
ECMA_PROPERTY_FIXED);
ecma_builtin_helper_def_prop_by_index (raw_strings_p,
prop_idx,
ecma_make_magic_string_value (LIT_MAGIC_STRING__EMPTY),
ECMA_PROPERTY_FIXED);
return;
}
uint8_t local_byte_array[LEXER_MAX_LITERAL_LOCAL_BUFFER_SIZE];
const uint8_t *source_p = lexer_convert_literal_to_chars (context_p,
&context_p->token.lit_location,
local_byte_array,
LEXER_STRING_NO_OPTS);
ecma_string_t *raw_str_p;
ecma_string_t *cooked_str_p = ecma_new_ecma_string_from_utf8 (source_p, lit_loc_p->length);
parser_free_allocated_buffer (context_p);
if (lit_loc_p->has_escape)
{
context_p->source_p = context_p->token.lit_location.char_p - 1;
lexer_parse_string (context_p, LEXER_STRING_RAW);
source_p = lexer_convert_literal_to_chars (context_p,
&context_p->token.lit_location,
local_byte_array,
LEXER_STRING_RAW);
raw_str_p = ecma_new_ecma_string_from_utf8 (source_p, lit_loc_p->length);
parser_free_allocated_buffer (context_p);
}
else
{
ecma_ref_ecma_string (cooked_str_p);
raw_str_p = cooked_str_p;
}
ecma_builtin_helper_def_prop_by_index (template_obj_p,
prop_idx,
ecma_make_string_value (cooked_str_p),
ECMA_PROPERTY_FIXED);
ecma_builtin_helper_def_prop_by_index (raw_strings_p,
prop_idx,
ecma_make_string_value (raw_str_p),
ECMA_PROPERTY_FIXED);
ecma_deref_ecma_string (cooked_str_p);
ecma_deref_ecma_string (raw_str_p);
} /* parser_tagged_template_literal_append_strings */
/**
* Create new tagged template literal object
*
* @return pointer to the allocated object
*/
ecma_object_t *
parser_new_tagged_template_literal (ecma_object_t **raw_strings_p) /**< [out] raw strings object */
{
ecma_object_t *template_obj_p = ecma_op_new_array_object (0);
*raw_strings_p = ecma_op_new_array_object (0);
ecma_builtin_helper_def_prop (template_obj_p,
ecma_get_magic_string (LIT_MAGIC_STRING_RAW),
ecma_make_object_value (*raw_strings_p),
ECMA_PROPERTY_FIXED);
ecma_deref_object (*raw_strings_p);
return template_obj_p;
} /* parser_new_tagged_template_literal */
/**
* Set integrity level of the given template array object to "frozen"
*/
static void
parser_tagged_template_literal_freeze_array (ecma_object_t *obj_p)
{
JERRY_ASSERT (ecma_get_object_type (obj_p) == ECMA_OBJECT_TYPE_ARRAY);
ecma_set_object_extensible (obj_p, false);
ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) obj_p;
uint8_t new_prop_value = (uint8_t) (ext_obj_p->u.array.u.length_prop & ~ECMA_PROPERTY_FLAG_WRITABLE);
ext_obj_p->u.array.u.length_prop = new_prop_value;
} /* parser_tagged_template_literal_freeze_array */
/**
* Finalize the tagged template object
*/
void
parser_tagged_template_literal_finalize (ecma_object_t *template_obj_p, /**< template object */
ecma_object_t *raw_strings_p) /**< raw strings object */
{
parser_tagged_template_literal_freeze_array (template_obj_p);
parser_tagged_template_literal_freeze_array (raw_strings_p);
ecma_deref_object (template_obj_p);
} /* parser_tagged_template_literal_finalize */
#endif /* ENABLED (JERRY_ES2015) */
/**
* @}
* @}
* @}
*/
@@ -0,0 +1,51 @@
/* 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_TAGGED_TEMPLATE_LITERAL_H
#define ECMA_TAGGED_TEMPLATE_LITERAL_H
/* \addtogroup parser Parser
* @{
*
* \addtogroup jsparser JavaScript
* @{
*
* \addtogroup jsparser_tagged_template_literal Tagged template literal
* @{
*/
#include "common.h"
#include "ecma-globals.h"
#include "js-parser-internal.h"
#if ENABLED (JERRY_ES2015)
ecma_object_t *
parser_new_tagged_template_literal (ecma_object_t **raw_strings_p);
void
parser_tagged_template_literal_append_strings (parser_context_t *context_p, ecma_object_t *template_obj_p,
ecma_object_t *raw_strings_p, uint32_t prop_index);
void
parser_tagged_template_literal_finalize (ecma_object_t *template_obj_p, ecma_object_t *raw_strings_p);
#endif /* ENABLED (JERRY_ES2015) */
#endif /* ECMA_TAGGED_TEMPLATE_LITERAL_H */
/**
* @}
* @}
* @}
*/
+55 -5
View File
@@ -1163,6 +1163,13 @@ parser_post_processing (parser_context_t *context_p) /**< context */
total_size += context_p->argument_count * sizeof (ecma_value_t);
}
#if ENABLED (JERRY_ES2015)
if (context_p->tagged_template_literal_cp != JMEM_CP_NULL)
{
total_size += sizeof (ecma_value_t);
}
#endif /* ENABLED (JERRY_ES2015) */
#if ENABLED (JERRY_LINE_INFO) || ENABLED (JERRY_ES2015_MODULE_SYSTEM)
if (JERRY_CONTEXT (resource_name) != ECMA_VALUE_UNDEFINED)
{
@@ -1290,6 +1297,11 @@ parser_post_processing (parser_context_t *context_p) /**< context */
{
compiled_code_p->status_flags |= CBC_CODE_FLAGS_REST_PARAMETER;
}
if (context_p->tagged_template_literal_cp != JMEM_CP_NULL)
{
compiled_code_p->status_flags |= CBC_CODE_FLAG_HAS_TAGGED_LITERALS;
}
#endif /* ENABLED (JERRY_ES2015) */
literal_pool_p = ((ecma_value_t *) byte_code_p) - context_p->register_count;
@@ -1572,6 +1584,20 @@ parser_post_processing (parser_context_t *context_p) /**< context */
}
}
#if ENABLED (JERRY_ES2015)
if (context_p->tagged_template_literal_cp != JMEM_CP_NULL)
{
ecma_value_t *tagged_base_p = (ecma_value_t *) (((uint8_t *) compiled_code_p) + total_size);
if (PARSER_NEEDS_MAPPED_ARGUMENTS (context_p->status_flags))
{
tagged_base_p -= context_p->argument_count;
}
tagged_base_p[-1] = (ecma_value_t) context_p->tagged_template_literal_cp;
}
#endif /* ENABLED (JERRY_ES2015) */
#if ENABLED (JERRY_LINE_INFO) || ENABLED (JERRY_ES2015_MODULE_SYSTEM)
if (JERRY_CONTEXT (resource_name) != ECMA_VALUE_UNDEFINED)
{
@@ -1582,6 +1608,13 @@ parser_post_processing (parser_context_t *context_p) /**< context */
resource_name_p -= context_p->argument_count;
}
#if ENABLED (JERRY_ES2015)
if (context_p->tagged_template_literal_cp != JMEM_CP_NULL)
{
resource_name_p--;
}
#endif /* ENABLED (JERRY_ES2015) */
resource_name_p[-1] = JERRY_CONTEXT (resource_name);
}
#endif /* ENABLED (JERRY_LINE_INFO) || ENABLED (JERRY_ES2015_MODULE_SYSTEM) */
@@ -1852,6 +1885,7 @@ parser_parse_source (const uint8_t *arg_list_p, /**< function argument list */
#if ENABLED (JERRY_ES2015)
context.status_flags |= PARSER_GET_CLASS_PARSER_OPTS (parse_opts);
context.tagged_template_literal_cp = JMEM_CP_NULL;
#endif /* ENABLED (JERRY_ES2015) */
context.stack_depth = 0;
@@ -1890,6 +1924,7 @@ parser_parse_source (const uint8_t *arg_list_p, /**< function argument list */
context.scope_stack_reg_top = 0;
#if ENABLED (JERRY_ES2015)
context.scope_stack_global_end = 0;
context.tagged_template_literal_cp = JMEM_CP_NULL;
#endif /* ENABLED (JERRY_ES2015) */
#ifndef JERRY_NDEBUG
@@ -2082,11 +2117,7 @@ parser_parse_source (const uint8_t *arg_list_p, /**< function argument list */
parser_free_jumps (context.last_statement);
}
if (context.u.allocated_buffer_p != NULL)
{
parser_free_local (context.u.allocated_buffer_p,
context.allocated_buffer_size);
}
parser_free_allocated_buffer (&context);
scanner_cleanup (&context);
@@ -2168,6 +2199,7 @@ parser_save_context (parser_context_t *context_p, /**< context */
saved_context_p->scope_stack_reg_top = context_p->scope_stack_reg_top;
#if ENABLED (JERRY_ES2015)
saved_context_p->scope_stack_global_end = context_p->scope_stack_global_end;
saved_context_p->tagged_template_literal_cp = context_p->tagged_template_literal_cp;
#endif /* ENABLED (JERRY_ES2015) */
#ifndef JERRY_NDEBUG
@@ -2195,6 +2227,7 @@ parser_save_context (parser_context_t *context_p, /**< context */
context_p->scope_stack_reg_top = 0;
#if ENABLED (JERRY_ES2015)
context_p->scope_stack_global_end = 0;
context_p->tagged_template_literal_cp = JMEM_CP_NULL;
#endif /* ENABLED (JERRY_ES2015) */
#ifndef JERRY_NDEBUG
@@ -2239,6 +2272,7 @@ parser_restore_context (parser_context_t *context_p, /**< context */
context_p->scope_stack_reg_top = saved_context_p->scope_stack_reg_top;
#if ENABLED (JERRY_ES2015)
context_p->scope_stack_global_end = saved_context_p->scope_stack_global_end;
context_p->tagged_template_literal_cp = saved_context_p->tagged_template_literal_cp;
#endif /* ENABLED (JERRY_ES2015) */
#ifndef JERRY_NDEBUG
@@ -2482,9 +2516,25 @@ parser_raise_error (parser_context_t *context_p, /**< context */
parser_free_jumps (saved_context_p->last_statement);
}
#if ENABLED (JERRY_ES2015)
if (saved_context_p->tagged_template_literal_cp != JMEM_CP_NULL)
{
ecma_collection_free (ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t,
saved_context_p->tagged_template_literal_cp));
}
#endif /* ENABLED (JERRY_ES2015) */
saved_context_p = saved_context_p->prev_context_p;
}
#if ENABLED (JERRY_ES2015)
if (context_p->tagged_template_literal_cp != JMEM_CP_NULL)
{
ecma_collection_free (ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t,
context_p->tagged_template_literal_cp));
}
#endif /* ENABLED (JERRY_ES2015) */
context_p->error = error;
PARSER_THROW (context_p->try_buffer);
/* Should never been reached. */
@@ -100,6 +100,7 @@ typedef enum
SCAN_STACK_COMPUTED_PROPERTY, /**< computed property name */
SCAN_STACK_COMPUTED_GENERATOR_FUNCTION, /**< computed property name */
SCAN_STACK_TEMPLATE_STRING, /**< template string */
SCAN_STACK_TAGGED_TEMPLATE_LITERAL, /**< tagged template literal */
SCAN_STACK_FOR_BLOCK_END, /**< end of "for" statement with let/const declaration */
SCAN_STACK_ARROW_ARGUMENTS, /**< might be arguments of an arrow function */
SCAN_STACK_ARROW_EXPRESSION, /**< expression body of an arrow function */
+13 -1
View File
@@ -613,6 +613,17 @@ scanner_scan_post_primary_expression (parser_context_t *context_p, /**< context
scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION;
return true;
}
#if ENABLED (JERRY_ES2015)
case LEXER_TEMPLATE_LITERAL:
{
if (JERRY_UNLIKELY (context_p->source_p[-1] != LIT_CHAR_GRAVE_ACCENT))
{
scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION;
parser_stack_push_uint8 (context_p, SCAN_STACK_TAGGED_TEMPLATE_LITERAL);
}
return true;
}
#endif /* ENABLED (JERRY_ES2015) */
case LEXER_LEFT_SQUARE:
{
parser_stack_push_uint8 (context_p, SCAN_STACK_PROPERTY_ACCESSOR);
@@ -1268,6 +1279,7 @@ scanner_scan_primary_expression_end (parser_context_t *context_p, /**< context *
return SCAN_KEEP_TOKEN;
}
case SCAN_STACK_TEMPLATE_STRING:
case SCAN_STACK_TAGGED_TEMPLATE_LITERAL:
{
if (type != LEXER_RIGHT_BRACE)
{
@@ -1276,7 +1288,7 @@ scanner_scan_primary_expression_end (parser_context_t *context_p, /**< context *
context_p->source_p--;
context_p->column--;
lexer_parse_string (context_p);
lexer_parse_string (context_p, LEXER_STRING_NO_OPTS);
if (context_p->source_p[-1] != LIT_CHAR_GRAVE_ACCENT)
{
+34 -1
View File
@@ -2098,7 +2098,40 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */
goto error;
}
#endif /* ENABLED (JERRY_ES2015) */
case VM_OC_STRING_CONCAT:
{
ecma_string_t *left_str_p = ecma_op_to_string (left_value);
if (JERRY_UNLIKELY (left_str_p == NULL))
{
result = ECMA_VALUE_ERROR;
goto error;
}
ecma_string_t *right_str_p = ecma_op_to_string (right_value);
if (JERRY_UNLIKELY (right_str_p == NULL))
{
ecma_deref_ecma_string (left_str_p);
result = ECMA_VALUE_ERROR;
goto error;
}
ecma_string_t *result_str_p = ecma_concat_ecma_strings (left_str_p, right_str_p);
ecma_deref_ecma_string (right_str_p);
*stack_top_p++ = ecma_make_string_value (result_str_p);
goto free_both_values;
}
case VM_OC_GET_TEMPLATE_OBJECT:
{
uint8_t tagged_idx = *byte_code_p++;
ecma_collection_t *collection_p = ecma_compiled_code_get_tagged_template_collection (bytecode_header_p);
JERRY_ASSERT (tagged_idx < collection_p->item_count);
*stack_top_p++ = ecma_copy_value (collection_p->buffer_p[tagged_idx]);
continue;
}
#endif /* ENABLED (JERRY_ES2015) */
case VM_OC_PUSH_ELISON:
{
*stack_top_p++ = ECMA_VALUE_ARRAY_HOLE;
+4
View File
@@ -258,6 +258,8 @@ typedef enum
VM_OC_CREATE_GENERATOR, /**< create a generator object */
VM_OC_YIELD, /**< yield operation */
VM_OC_EXT_RETURN, /**< return which also clears the stack */
VM_OC_STRING_CONCAT, /**< string concatenation */
VM_OC_GET_TEMPLATE_OBJECT, /**< GetTemplateObject operation */
#endif /* ENABLED (JERRY_ES2015) */
VM_OC_NONE, /**< a special opcode for unsupported byte codes */
} vm_oc_types;
@@ -311,6 +313,8 @@ typedef enum
VM_OC_CREATE_GENERATOR = VM_OC_NONE, /**< create a generator object */
VM_OC_YIELD = VM_OC_NONE, /**< yield operation */
VM_OC_EXT_RETURN = VM_OC_NONE, /**< return which also clears the stack */
VM_OC_STRING_CONCAT = VM_OC_NONE, /**< string concatenation */
VM_OC_GET_TEMPLATE_OBJECT = VM_OC_NONE, /**< GetTemplateObject operation */
#endif /* !ENABLED (JERRY_ES2015) */
VM_OC_UNUSED = VM_OC_NONE /**< placeholder if the list is empty */
+58
View File
@@ -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.
*/
function assertThrows(str, error) {
try {
eval(str);
assert(false);
} catch (e) {
if (typeof error === "function") {
assert(e instanceof error);
} else {
assert(e === error)
}
}
}
// https://www.ecma-international.org/ecma-262/6.0/#sec-string.raw 21.1.2.4
// Note: the string error messages represents the nth step of the routine
var abruptTests = [
["undefined", TypeError], // 3.
["{ get raw() { throw '5'; } }", '5'],
["{ raw : undefined }", TypeError], // 5.toObject
["{ raw : { get length() { throw '7.1'; } } }", '7.1'],
["{ raw : { length : { toString() { throw '7.2'; } } } }", '7.2'],
["{ raw : { length: 2, get '0'() { throw '12.b.1'} } }", '12.b.1'],
["{ raw : { length: 2, '0' : { toString() { throw '12.b.2';} } } }", '12.b.2'],
["{ raw : { length: 2, '0' : 1 } }, { toString() { throw '12.h';} }", '12.h'],
];
abruptTests.forEach(e => {
assertThrows("String.raw(" + e[0] + ")", e[1]);
});
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/raw
assert(String.raw`Hi\n${2+3}!` === 'Hi\\n5!');
assert(String.raw`Hi\u000A!` === 'Hi\\u000A!');
let name = 'Bob';
assert(String.raw`Hi\n${name}!` === "Hi\\nBob!");
let str = String.raw({
raw: ['foo', 'bar', 'baz']
}, 2 + 3, 'Java' + 'Script');
assert(str === "foo5barJavaScriptbaz");
assert(String.raw({ raw: 'test' }, 0, 1, 2) === "t0e1s2t");
@@ -24,7 +24,7 @@ var obj2 = {
return true;
}
};
assert(+obj2 === 10);
//assert(`${obj2}` === "hello"); //FIXME: template literals requires String hint during concatenation
assert(`${obj2}` === "hello");
assert(obj2 + '' === "true");
@@ -0,0 +1,128 @@
/* 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.
*/
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals
// Tagged templates
var person = 'Mike';
var age = 28;
function myTag(strings, personExp, ageExp) {
assert(strings[0] == "That ");
assert(strings[1] == " is a ");
var str0 = strings[0];
var str1 = strings[1];
var ageStr;
if (ageExp > 99){
ageStr = 'centenarian';
} else {
ageStr = 'youngster';
}
return `${str0}${personExp}${str1}${ageStr}`;
}
var output = myTag`That ${ person } is a ${ age }`;
assert(output === "That Mike is a youngster");
function template(strings, ...keys) {
return (function(...values) {
var dict = values[values.length - 1] || {};
var result = [strings[0]];
keys.forEach(function(key, i) {
var value = Number.isInteger(key) ? values[key] : dict[key];
result.push(value, strings[i + 1]);
});
return result.join('');
});
}
var t1Closure = template`${0}${1}${0}!`;
assert(t1Closure('Y', 'A') === "YAY!");
var t2Closure = template`${0} ${'foo'}!`;
assert(t2Closure('Hello', {foo: 'World'}) === "Hello World!");
// Raw strings
(function () {
function tag(strings) {
assert(strings.raw[0].length === 40);
}
tag`string text line 1 \n string text line 2`;
})();
assert (String.raw`Hi\n${2+3}!` === "Hi\\n5!");
(function () {
function empty(strings, ...params) {
assert(strings.length === 4);
assert(strings.raw.length === 4);
assert(params.length === 3);
strings.forEach ((e) => assert (e === ""));
strings.raw.forEach ((e) => assert (e === ""));
params.forEach ((e) => assert (e === 1));
}
empty`${1}${1}${1}`;
})();
(function () {
function f (str) {
return str.raw[0].length;
}
assert (eval("f`a\u2029b`") === 3);
})();
(function () {
function testRaw(parts, a, b) {
assert(parts instanceof Array);
assert(parts.raw instanceof Array);
assert(parts.length === 3);
assert(parts[0] === "str");
assert(parts[1] === "escaped\n");
assert(parts[2] === "");
assert(parts.raw.length === 3);
assert(parts.raw[0] === "str");
assert(parts.raw[1] === "escaped\\n");
assert(parts.raw[2] === "");
assert(a === 123);
assert(b === 456);
return true;
}
assert(testRaw `str${123}escaped\n${456}` === true);
})();
// TemplateStrings call site caching
(function () {
let str = (arr) => arr;
function getStr() {
return str`foo`;
}
var chainedCall = getStr();
var localCall = str`foo`;
assert(chainedCall === getStr() && chainedCall !== localCall);
})();
// TemplateStrings permanent caching
(function () {
let str = (arr) => arr;
function getStr() {
return str`foo`;
}
var chainedCall = getStr();
var localNew = new getStr();
assert(chainedCall === getStr() && chainedCall === localNew);
})();
+15 -3
View File
@@ -41,9 +41,9 @@ switch (1)
{
default:
``
`abc`
`ab${a+b}${ `x` }c`
``;
`abc`;
`ab${a+b}${ `x` }c`;
assert (`` === '');
assert (`abc` === 'abc');
@@ -90,3 +90,15 @@ must_throw ("`${}`");
must_throw ("`${1}");
must_throw ("`${1}.${");
must_throw ("`${1}.${2}");
// line break normalization
var cr = eval("`a" + String.fromCharCode(13) + "b`");
var lf = eval("`a" + String.fromCharCode(10) + "b`");
var crlf = eval("`a" + String.fromCharCode(13,10) + "b`");
assert(cr.length === 3);
assert(lf.length === 3);
assert(crlf.length === 3);
assert(cr[1] === lf[1]);
assert(lf[1] === crlf[1]);
assert(crlf[1] === '\n');
+1 -1
View File
@@ -223,7 +223,7 @@ main (void)
/* Check the snapshot data. Unused bytes should be filled with zeroes */
const uint8_t expected_data[] =
{
0x4A, 0x52, 0x52, 0x59, 0x22, 0x00, 0x00, 0x00,
0x4A, 0x52, 0x52, 0x59, 0x23, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
0x03, 0x00, 0x01, 0x00, 0x41, 0x00, 0x01, 0x00,