From 8a5bfd2279fcfafa68086b57ab1a65822ae0a0c9 Mon Sep 17 00:00:00 2001 From: Zoltan Herczeg Date: Wed, 27 Sep 2017 11:24:51 +0200 Subject: [PATCH] Implement template literals. (#2025) JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com --- jerry-core/parser/js/js-lexer.c | 160 ++++++++++++++++------ jerry-core/parser/js/js-lexer.h | 3 + jerry-core/parser/js/js-parser-expr.c | 139 +++++++++++++++++-- jerry-core/parser/js/js-parser-internal.h | 72 ++++++---- jerry-core/parser/js/js-parser-scanner.c | 41 ++++++ jerry-core/parser/js/js-parser-statm.c | 2 +- jerry-core/parser/js/js-parser-util.c | 8 +- jerry-core/parser/js/js-parser.c | 6 +- jerry-core/parser/js/js-parser.h | 3 + jerry-core/profiles/es5.1.profile | 1 + jerry-core/profiles/minimal.profile | 1 + targets/curie_bsp/setup.py | 1 + tests/jerry/es2015/template_string.js | 92 +++++++++++++ 13 files changed, 441 insertions(+), 88 deletions(-) create mode 100644 tests/jerry/es2015/template_string.js diff --git a/jerry-core/parser/js/js-lexer.c b/jerry-core/parser/js/js-lexer.c index f9b415b29..dc96c4621 100644 --- a/jerry-core/parser/js/js-lexer.c +++ b/jerry-core/parser/js/js-lexer.c @@ -553,7 +553,7 @@ lexer_parse_identifier (parser_context_t *context_p, /**< context */ /** * Parse string. */ -static void +void lexer_parse_string (parser_context_t *context_p) /**< context */ { uint8_t str_end_character = context_p->source_p[0]; @@ -567,6 +567,13 @@ lexer_parse_string (parser_context_t *context_p) /**< context */ size_t length = 0; uint8_t has_escape = false; +#ifndef CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS + if (str_end_character == LIT_CHAR_RIGHT_BRACE) + { + str_end_character = LIT_CHAR_GRAVE_ACCENT; + } +#endif /* !CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS */ + while (true) { if (source_p >= source_end_p) @@ -594,32 +601,33 @@ lexer_parse_string (parser_context_t *context_p) /**< context */ has_escape = true; /* Newline is ignored. */ - if (*source_p == LIT_CHAR_CR - || *source_p == LIT_CHAR_LF - || (*source_p == LEXER_NEWLINE_LS_PS_BYTE_1 && LEXER_NEWLINE_LS_PS_BYTE_23 (source_p))) + if (*source_p == LIT_CHAR_CR) { - if (*source_p == LIT_CHAR_CR) + source_p++; + if (source_p < source_end_p + && *source_p == LIT_CHAR_LF) { source_p++; - if (source_p < source_end_p - && *source_p == LIT_CHAR_LF) - { - source_p++; - } - } - else if (*source_p == LIT_CHAR_LF) - { - source_p++; - } - else - { - source_p += 3; } line++; column = 1; continue; } + else if (*source_p == LIT_CHAR_LF) + { + source_p++; + line++; + column = 1; + continue; + } + else if (*source_p == LEXER_NEWLINE_LS_PS_BYTE_1 && LEXER_NEWLINE_LS_PS_BYTE_23 (source_p)) + { + source_p += 3; + line++; + column = 1; + continue; + } if (*source_p == LIT_CHAR_0 && source_p + 1 < source_end_p @@ -719,6 +727,56 @@ lexer_parse_string (parser_context_t *context_p) /**< context */ column++; continue; } + else if (*source_p == LIT_CHAR_TAB) + { + column = align_column_to_tab (column); + /* Subtract -1 because column is increased below. */ + column--; + } +#ifndef CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS + else if (str_end_character == LIT_CHAR_GRAVE_ACCENT) + { + if (source_p[0] == LIT_CHAR_LEFT_BRACE + && source_p[-1] == LIT_CHAR_DOLLAR_SIGN + && source_p[-2] != LIT_CHAR_BACKSLASH) + { + length--; + break; + } + + /* Newline (without backslash) is part of the string. */ + if (*source_p == LIT_CHAR_CR) + { + source_p++; + length++; + if (source_p < source_end_p + && *source_p == LIT_CHAR_LF) + { + source_p++; + length++; + } + line++; + column = 1; + continue; + } + else if (*source_p == LIT_CHAR_LF) + { + source_p++; + length++; + line++; + column = 1; + continue; + } + else if (*source_p == LEXER_NEWLINE_LS_PS_BYTE_1 && LEXER_NEWLINE_LS_PS_BYTE_23 (source_p)) + { + source_p += 3; + length += 3; + line++; + column = 1; + continue; + } + } +#endif /* !CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS */ else if (*source_p == LIT_CHAR_CR || *source_p == LIT_CHAR_LF || (*source_p == LEXER_NEWLINE_LS_PS_BYTE_1 && LEXER_NEWLINE_LS_PS_BYTE_23 (source_p))) @@ -727,12 +785,6 @@ lexer_parse_string (parser_context_t *context_p) /**< context */ context_p->token.column = column; parser_raise_error (context_p, PARSER_ERR_NEWLINE_NOT_ALLOWED); } - else if (*source_p == LIT_CHAR_TAB) - { - column = align_column_to_tab (column); - /* Subtract -1 because column is increased below. */ - column--; - } source_p++; column++; @@ -751,7 +803,12 @@ lexer_parse_string (parser_context_t *context_p) /**< context */ parser_raise_error (context_p, PARSER_ERR_STRING_TOO_LONG); } +#ifndef CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS + context_p->token.type = ((str_end_character != LIT_CHAR_GRAVE_ACCENT) ? LEXER_LITERAL + : LEXER_TEMPLATE_LITERAL); +#else /* CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS */ context_p->token.type = LEXER_LITERAL; +#endif /* !CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS */ /* Fill literal data. */ context_p->token.lit_location.char_p = string_start_p; @@ -1176,6 +1233,9 @@ lexer_next_token (parser_context_t *context_p) /**< context */ case LIT_CHAR_SINGLE_QUOTE: case LIT_CHAR_DOUBLE_QUOTE: +#ifndef CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS + case LIT_CHAR_GRAVE_ACCENT: +#endif /* !CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS */ { lexer_parse_string (context_p); return; @@ -1398,6 +1458,13 @@ lexer_construct_literal_object (parser_context_t *context_p, /**< context */ { uint8_t str_end_character = source_p[-1]; +#ifndef CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS + if (str_end_character == LIT_CHAR_RIGHT_BRACE) + { + str_end_character = LIT_CHAR_GRAVE_ACCENT; + } +#endif /* !CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS */ + while (true) { if (*source_p == str_end_character) @@ -1413,28 +1480,25 @@ lexer_construct_literal_object (parser_context_t *context_p, /**< context */ JERRY_ASSERT (source_p < context_p->source_end_p); /* Newline is ignored. */ - if (*source_p == LIT_CHAR_CR - || *source_p == LIT_CHAR_LF - || (*source_p == LEXER_NEWLINE_LS_PS_BYTE_1 && LEXER_NEWLINE_LS_PS_BYTE_23 (source_p))) + if (*source_p == LIT_CHAR_CR) { - if (*source_p == LIT_CHAR_CR) - { - source_p++; - JERRY_ASSERT (source_p < context_p->source_end_p); + source_p++; + JERRY_ASSERT (source_p < context_p->source_end_p); - if (*source_p == LIT_CHAR_LF) - { - source_p++; - } - } - else if (*source_p == LIT_CHAR_LF) + if (*source_p == LIT_CHAR_LF) { source_p++; } - else - { - source_p += 3; - } + 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; } @@ -1536,6 +1600,16 @@ lexer_construct_literal_object (parser_context_t *context_p, /**< context */ continue; } } +#ifndef CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS + 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 /* !CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS */ if (*source_p >= LEXER_UTF8_4BYTE_START) { @@ -1741,9 +1815,9 @@ lexer_construct_function_object (parser_context_t *context_p, /**< context */ parser_raise_error (context_p, PARSER_ERR_LITERAL_LIMIT_REACHED); } - if (context_p->status_flags & PARSER_RESOLVE_THIS_FOR_CALLS) + if (context_p->status_flags & (PARSER_RESOLVE_BASE_FOR_CALLS | PARSER_INSIDE_WITH)) { - extra_status_flags |= PARSER_RESOLVE_THIS_FOR_CALLS; + extra_status_flags |= PARSER_RESOLVE_BASE_FOR_CALLS; } literal_p = (lexer_literal_t *) parser_list_append (context_p, &context_p->literal_pool); diff --git a/jerry-core/parser/js/js-lexer.h b/jerry-core/parser/js/js-lexer.h index 06c56642e..855276f6e 100644 --- a/jerry-core/parser/js/js-lexer.h +++ b/jerry-core/parser/js/js-lexer.h @@ -39,6 +39,9 @@ typedef enum LEXER_LIT_TRUE, /**< true (not a keyword!) */ LEXER_LIT_FALSE, /**< false (not a keyword!) */ LEXER_LIT_NULL, /**< null (not a keyword!) */ +#ifndef CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS + LEXER_TEMPLATE_LITERAL, /**< multi segment template literal */ +#endif /* !CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS */ /* Unary operators * IMPORTANT: update CBC_UNARY_OP_TOKEN_TO_OPCODE and diff --git a/jerry-core/parser/js/js-parser-expr.c b/jerry-core/parser/js/js-parser-expr.c index 91a0a18f3..1132c4c12 100644 --- a/jerry-core/parser/js/js-parser-expr.c +++ b/jerry-core/parser/js/js-parser-expr.c @@ -15,6 +15,10 @@ #include "js-parser-internal.h" +#ifndef CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS +#include "lit-char-helpers.h" +#endif /* !CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS */ + #if JERRY_JS_PARSER /** \addtogroup parser Parser @@ -393,11 +397,6 @@ parser_parse_object_literal (parser_context_t *context_p) /**< context */ item_type = PARSER_OBJECT_PROPERTY_SETTER; } - if (context_p->status_flags & PARSER_INSIDE_WITH) - { - status_flags |= PARSER_RESOLVE_THIS_FOR_CALLS; - } - lexer_expect_object_literal_id (context_p, true); literal_index = context_p->lit_object.index; @@ -484,11 +483,6 @@ parser_parse_function_expression (parser_context_t *context_p, /**< context */ parser_flush_cbc (context_p); } - if (context_p->status_flags & PARSER_INSIDE_WITH) - { - status_flags |= PARSER_RESOLVE_THIS_FOR_CALLS; - } - function_literal_index = lexer_construct_function_object (context_p, status_flags); JERRY_ASSERT (context_p->last_cbc_opcode == PARSER_CBC_UNAVAILABLE); @@ -590,6 +584,112 @@ parser_check_arrow_function (parser_context_t *context_p) /**< context */ #endif /* !CONFIG_DISABLE_ES2015_ARROW_FUNCTION */ +#ifndef CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS + +/** + * Parse template literal. + */ +static void +parser_parse_template_literal (parser_context_t *context_p) /**< context */ +{ + bool is_empty_head = true; + + if (context_p->token.lit_location.length > 0) + { + is_empty_head = false; + + lexer_construct_literal_object (context_p, + &context_p->token.lit_location, + context_p->token.lit_location.type); + + parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_LITERAL); + } + + lexer_next_token (context_p); + 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); + } + + if (!is_empty_head) + { + if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS) + { + context_p->last_cbc_opcode = CBC_ADD_TWO_LITERALS; + } + else if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) + { + context_p->last_cbc_opcode = CBC_ADD_RIGHT_LITERAL; + } + else + { + parser_emit_cbc (context_p, CBC_ADD); + } + } + + context_p->source_p--; + context_p->column--; + lexer_parse_string (context_p); + + if (is_empty_head || context_p->token.lit_location.length > 0) + { + lexer_construct_literal_object (context_p, + &context_p->token.lit_location, + context_p->token.lit_location.type); + + if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) + { + context_p->last_cbc_opcode = CBC_ADD_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_object_type = context_p->lit_object.type; + } + else + { + parser_emit_cbc_literal_from_token (context_p, CBC_ADD_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) + { + parser_raise_error (context_p, PARSER_ERR_RIGHT_BRACE_EXPECTED); + } + + if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) + { + context_p->last_cbc_opcode = CBC_ADD_RIGHT_LITERAL; + } + else + { + parser_emit_cbc (context_p, CBC_ADD); + } + + context_p->source_p--; + context_p->column--; + lexer_parse_string (context_p); + + if (context_p->token.lit_location.length > 0) + { + lexer_construct_literal_object (context_p, + &context_p->token.lit_location, + context_p->token.lit_location.type); + + parser_emit_cbc_literal_from_token (context_p, CBC_ADD_RIGHT_LITERAL); + } + } + + return; +} /* parser_parse_template_literal */ + +#endif /* !CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS */ + /** * Parse and record unary operators, and parse the primary literal. */ @@ -637,6 +737,19 @@ parser_parse_unary_expression (parser_context_t *context_p, /**< context */ /* Parse primary expression. */ switch (context_p->token.type) { +#ifndef CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS + case LEXER_TEMPLATE_LITERAL: + { + if (context_p->source_p[-1] != LIT_CHAR_GRAVE_ACCENT) + { + parser_parse_template_literal (context_p); + break; + } + + /* The string is a normal string literal. */ + /* FALLTHRU */ + } +#endif /* !CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS */ case LEXER_LITERAL: { #ifndef CONFIG_DISABLE_ES2015_ARROW_FUNCTION @@ -670,8 +783,6 @@ parser_parse_unary_expression (parser_context_t *context_p, /**< context */ } #endif /* !CONFIG_DISABLE_ES2015_ARROW_FUNCTION */ - cbc_opcode_t opcode = CBC_PUSH_LITERAL; - if (context_p->token.lit_location.type == LEXER_IDENT_LITERAL || context_p->token.lit_location.type == LEXER_STRING_LITERAL) { @@ -708,6 +819,8 @@ parser_parse_unary_expression (parser_context_t *context_p, /**< context */ } } + cbc_opcode_t opcode = CBC_PUSH_LITERAL; + if (context_p->lit_object.type != LEXER_LITERAL_OBJECT_EVAL) { if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) @@ -942,7 +1055,7 @@ parser_process_unary_expression (parser_context_t *context_p) /**< context */ context_p->last_cbc_opcode = CBC_PUSH_PROP_THIS_LITERAL_REFERENCE; opcode = CBC_CALL_PROP; } - else if ((context_p->status_flags & (PARSER_INSIDE_WITH | PARSER_RESOLVE_THIS_FOR_CALLS)) + else if ((context_p->status_flags & (PARSER_INSIDE_WITH | PARSER_RESOLVE_BASE_FOR_CALLS)) && PARSER_IS_PUSH_LITERAL (context_p->last_cbc_opcode) && context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL) { diff --git a/jerry-core/parser/js/js-parser-internal.h b/jerry-core/parser/js/js-parser-internal.h index 55d0c506a..06f5873dc 100644 --- a/jerry-core/parser/js/js-parser-internal.h +++ b/jerry-core/parser/js/js-parser-internal.h @@ -34,36 +34,53 @@ * @{ */ -/* General parser flags. */ -#define PARSER_IS_STRICT 0x00000001u -#define PARSER_IS_FUNCTION 0x00000002u -#define PARSER_IS_CLOSURE 0x00000004u -#define PARSER_IS_PROPERTY_GETTER 0x00000008u -#define PARSER_IS_PROPERTY_SETTER 0x00000010u -#define PARSER_IS_FUNC_EXPRESSION 0x00000020u -#define PARSER_NAMED_FUNCTION_EXP 0x00000040u -#define PARSER_HAS_NON_STRICT_ARG 0x00000080u -#define PARSER_ARGUMENTS_NEEDED 0x00000100u -#define PARSER_ARGUMENTS_NOT_NEEDED 0x00000200u -#define PARSER_LEXICAL_ENV_NEEDED 0x00000400u -#define PARSER_INSIDE_WITH 0x00000800u -#define PARSER_RESOLVE_THIS_FOR_CALLS 0x00001000u -#define PARSER_HAS_INITIALIZED_VARS 0x00002000u -#define PARSER_HAS_LATE_LIT_INIT 0x00004000u -#define PARSER_NO_END_LABEL 0x00008000u -#define PARSER_NO_REG_STORE 0x00010000u -#define PARSER_DEBUGGER_BREAKPOINT_APPENDED 0x00020000u +/** + * General parser flags. + */ +typedef enum +{ + PARSER_IS_STRICT = (1u << 0), /**< strict mode code */ + PARSER_IS_FUNCTION = (1u << 1), /**< function body is parsed */ + PARSER_IS_CLOSURE = (1u << 2), /**< function body is encapsulated in {} block */ + PARSER_IS_FUNC_EXPRESSION = (1u << 3), /**< a function expression is parsed */ + PARSER_IS_PROPERTY_GETTER = (1u << 4), /**< a property getter function is parsed */ + PARSER_IS_PROPERTY_SETTER = (1u << 5), /**< a property setter function is parsed */ + PARSER_NAMED_FUNCTION_EXP = (1u << 6), /**< a function expression has a name binding */ + PARSER_HAS_NON_STRICT_ARG = (1u << 7), /**< the function has arguments which + * are not supported in strict mode */ + PARSER_ARGUMENTS_NEEDED = (1u << 8), /**< arguments object must be created */ + PARSER_ARGUMENTS_NOT_NEEDED = (1u << 9), /**< arguments object must NOT be created */ + PARSER_LEXICAL_ENV_NEEDED = (1u << 10), /**< lexical environment object must be created */ + PARSER_NO_REG_STORE = (1u << 11), /**< all local variables must be stored + * in the lexical environment object */ + PARSER_INSIDE_WITH = (1u << 12), /**< code block is inside a with statement */ + PARSER_RESOLVE_BASE_FOR_CALLS = (1u << 13), /**< the this object must be resolved when + * a function without a base object is called */ + PARSER_HAS_INITIALIZED_VARS = (1u << 14), /**< a CBC_INITIALIZE_VARS instruction must be emitted */ + PARSER_HAS_LATE_LIT_INIT = (1u << 15), /**< allocate memory for this string after + * the local parser data is freed */ + PARSER_NO_END_LABEL = (1u << 16), /**< return instruction must be inserted + * after the last byte code */ + PARSER_DEBUGGER_BREAKPOINT_APPENDED = (1u << 17), /**< pending (unsent) breakpoint + * info is available */ #ifndef CONFIG_DISABLE_ES2015_ARROW_FUNCTION -#define PARSER_IS_ARROW_FUNCTION 0x00040000u -#define PARSER_ARROW_PARSE_ARGS 0x00080000u + PARSER_IS_ARROW_FUNCTION = (1u << 18), /**< an arrow function is parsed */ + PARSER_ARROW_PARSE_ARGS = (1u << 19), /**< parse the argument list of an arrow function */ #endif /* !CONFIG_DISABLE_ES2015_ARROW_FUNCTION */ +} parser_general_flags_t; -/* Expression parsing flags. */ -#define PARSE_EXPR 0x00 -#define PARSE_EXPR_STATEMENT 0x01 -#define PARSE_EXPR_BLOCK 0x02 -#define PARSE_EXPR_NO_COMMA 0x04 -#define PARSE_EXPR_HAS_LITERAL 0x08 +/** + * Expression parsing flags. + */ +typedef enum +{ + PARSE_EXPR = 0, /**< parse an expression without any special flags */ + PARSE_EXPR_STATEMENT = (1u << 0), /**< discard the result of the expression */ + PARSE_EXPR_BLOCK = (1u << 1), /**< copy the expression result into the block result */ + PARSE_EXPR_NO_COMMA = (1u << 2), /**< do not parse comma operator */ + PARSE_EXPR_HAS_LITERAL = (1u << 3), /**< a primary literal is provided by a + * CBC_PUSH_LITERAL instruction */ +} parser_expression_flags_t; /* The maximum of PARSER_CBC_STREAM_PAGE_SIZE is 127. */ #define PARSER_CBC_STREAM_PAGE_SIZE \ @@ -400,6 +417,7 @@ bool lexer_check_colon (parser_context_t *context_p); #ifndef CONFIG_DISABLE_ES2015_ARROW_FUNCTION lexer_token_type_t lexer_check_arrow (parser_context_t *context_p); #endif /* !CONFIG_DISABLE_ES2015_ARROW_FUNCTION */ +void lexer_parse_string (parser_context_t *context_p); void lexer_expect_identifier (parser_context_t *context_p, uint8_t literal_type); void lexer_scan_identifier (parser_context_t *context_p, bool propety_name); ecma_char_t lexer_hex_to_character (parser_context_t *context_p, const uint8_t *source_p, int length); diff --git a/jerry-core/parser/js/js-parser-scanner.c b/jerry-core/parser/js/js-parser-scanner.c index 91a003b07..68a56c1c9 100644 --- a/jerry-core/parser/js/js-parser-scanner.c +++ b/jerry-core/parser/js/js-parser-scanner.c @@ -15,6 +15,10 @@ #include "js-parser-internal.h" +#ifndef CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS +#include "lit-char-helpers.h" +#endif /* !CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS */ + #if JERRY_JS_PARSER /** \addtogroup parser Parser @@ -59,6 +63,9 @@ typedef enum SCAN_STACK_BLOCK_STATEMENT, /**< block statement group */ SCAN_STACK_BLOCK_EXPRESSION, /**< block expression group */ SCAN_STACK_BLOCK_PROPERTY, /**< block property group */ +#ifndef CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS + SCAN_STACK_TEMPLATE_STRING, /**< template string */ +#endif /* !CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS */ } scan_stack_modes_t; /** @@ -110,6 +117,20 @@ parser_scan_primary_expression (parser_context_t *context_p, /**< context */ *mode = SCAN_MODE_PROPERTY_NAME; return true; } +#ifndef CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS + case LEXER_TEMPLATE_LITERAL: + { + if (context_p->source_p[-1] != LIT_CHAR_GRAVE_ACCENT) + { + parser_stack_push_uint8 (context_p, SCAN_STACK_TEMPLATE_STRING); + *mode = SCAN_MODE_PRIMARY_EXPRESSION; + break; + } + + /* The string is a normal string literal. */ + /* FALLTHRU */ + } +#endif /* !CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS */ case LEXER_LITERAL: #ifndef CONFIG_DISABLE_ES2015_ARROW_FUNCTION { @@ -309,6 +330,26 @@ parser_scan_primary_expression_end (parser_context_t *context_p, /**< context */ return false; } +#ifndef CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS + if (type == LEXER_RIGHT_BRACE && stack_top == SCAN_STACK_TEMPLATE_STRING) + { + context_p->source_p--; + context_p->column--; + lexer_parse_string (context_p); + + if (context_p->source_p[-1] != LIT_CHAR_GRAVE_ACCENT) + { + *mode = SCAN_MODE_PRIMARY_EXPRESSION; + } + else + { + parser_stack_pop_uint8 (context_p); + *mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; + } + return false; + } +#endif /* !CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS */ + *mode = SCAN_MODE_STATEMENT; if (type == LEXER_RIGHT_PAREN && stack_top == SCAN_STACK_PAREN_STATEMENT) { diff --git a/jerry-core/parser/js/js-parser-statm.c b/jerry-core/parser/js/js-parser-statm.c index 57866a9d3..2ea192490 100644 --- a/jerry-core/parser/js/js-parser-statm.c +++ b/jerry-core/parser/js/js-parser-statm.c @@ -588,7 +588,7 @@ parser_parse_with_statement_end (parser_context_t *context_p) /**< context */ if (type == PARSER_STATEMENT_START) { - context_p->status_flags &= ~PARSER_INSIDE_WITH; + context_p->status_flags &= (uint32_t) ~PARSER_INSIDE_WITH; return; } diff --git a/jerry-core/parser/js/js-parser-util.c b/jerry-core/parser/js/js-parser-util.c index 7a298f169..3c81a94b5 100644 --- a/jerry-core/parser/js/js-parser-util.c +++ b/jerry-core/parser/js/js-parser-util.c @@ -577,7 +577,7 @@ parser_set_branch_to_current_position (parser_context_t *context_p, /**< context parser_flush_cbc (context_p); } - context_p->status_flags &= ~PARSER_NO_END_LABEL; + context_p->status_flags &= (uint32_t) ~PARSER_NO_END_LABEL; JERRY_ASSERT (context_p->byte_code_size > (branch_p->offset >> 8)); @@ -824,6 +824,12 @@ parser_error_to_string (parser_error_t error) /**< error code */ { return "Expected ']' token."; } +#ifndef CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS + case PARSER_ERR_RIGHT_BRACE_EXPECTED: + { + return "Expected '}' token."; + } +#endif /* !CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS */ case PARSER_ERR_COLON_EXPECTED: { return "Expected ':' token."; diff --git a/jerry-core/parser/js/js-parser.c b/jerry-core/parser/js/js-parser.c index 1ca283b54..5dc9cacf1 100644 --- a/jerry-core/parser/js/js-parser.c +++ b/jerry-core/parser/js/js-parser.c @@ -154,7 +154,7 @@ parser_compute_indicies (parser_context_t *context_p, /**< context */ if (status_flags & PARSER_ARGUMENTS_NOT_NEEDED) { - status_flags &= ~PARSER_ARGUMENTS_NEEDED; + status_flags &= (uint32_t) ~PARSER_ARGUMENTS_NEEDED; context_p->status_flags = status_flags; } @@ -1587,7 +1587,7 @@ parser_post_processing (parser_context_t *context_p) /**< context */ if (!(context_p->status_flags & PARSER_NO_END_LABEL) || !(PARSER_OPCODE_IS_RETURN (last_opcode))) { - context_p->status_flags &= ~PARSER_NO_END_LABEL; + context_p->status_flags &= (uint32_t) ~PARSER_NO_END_LABEL; length++; } @@ -2006,7 +2006,7 @@ parser_parse_function_arguments (parser_context_t *context_p, /**< context */ context_p->lit_object.literal_p->status_flags = literal_status_flags; context_p->status_flags |= PARSER_ARGUMENTS_NOT_NEEDED; - context_p->status_flags &= ~(PARSER_LEXICAL_ENV_NEEDED | PARSER_ARGUMENTS_NEEDED); + context_p->status_flags &= (uint32_t) ~(PARSER_LEXICAL_ENV_NEEDED | PARSER_ARGUMENTS_NEEDED); } if (context_p->literal_count == literal_count) diff --git a/jerry-core/parser/js/js-parser.h b/jerry-core/parser/js/js-parser.h index f8272cb40..e5b90c331 100644 --- a/jerry-core/parser/js/js-parser.h +++ b/jerry-core/parser/js/js-parser.h @@ -84,6 +84,9 @@ typedef enum PARSER_ERR_LEFT_BRACE_EXPECTED, /**< left brace expected */ PARSER_ERR_RIGHT_PAREN_EXPECTED, /**< right paren expected */ PARSER_ERR_RIGHT_SQUARE_EXPECTED, /**< right square expected */ +#ifndef CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS + PARSER_ERR_RIGHT_BRACE_EXPECTED, /**< right brace expected */ +#endif /* !CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS */ PARSER_ERR_COLON_EXPECTED, /**< colon expected */ PARSER_ERR_COLON_FOR_CONDITIONAL_EXPECTED, /**< colon expected for conditional expression */ PARSER_ERR_SEMICOLON_EXPECTED, /**< semicolon expected */ diff --git a/jerry-core/profiles/es5.1.profile b/jerry-core/profiles/es5.1.profile index 57ce9cf8f..79fecd842 100644 --- a/jerry-core/profiles/es5.1.profile +++ b/jerry-core/profiles/es5.1.profile @@ -1,4 +1,5 @@ CONFIG_DISABLE_ES2015_ARROW_FUNCTION CONFIG_DISABLE_ES2015_BUILTIN CONFIG_DISABLE_ES2015_PROMISE_BUILTIN +CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN diff --git a/jerry-core/profiles/minimal.profile b/jerry-core/profiles/minimal.profile index f561545b3..f32fab9f2 100644 --- a/jerry-core/profiles/minimal.profile +++ b/jerry-core/profiles/minimal.profile @@ -6,6 +6,7 @@ CONFIG_DISABLE_ERROR_BUILTINS CONFIG_DISABLE_ES2015_ARROW_FUNCTION CONFIG_DISABLE_ES2015_BUILTIN CONFIG_DISABLE_ES2015_PROMISE_BUILTIN +CONFIG_DISABLE_ES2015_TEMPLATE_STRINGS CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN CONFIG_DISABLE_JSON_BUILTIN CONFIG_DISABLE_MATH_BUILTIN diff --git a/targets/curie_bsp/setup.py b/targets/curie_bsp/setup.py index bc918c2b6..45e8dabb9 100755 --- a/targets/curie_bsp/setup.py +++ b/targets/curie_bsp/setup.py @@ -99,6 +99,7 @@ def build_jerry_data(jerry_path): '-DCONFIG_DISABLE_ANNEXB_BUILTIN', '-DCONFIG_DISABLE_ES2015_ARROW_FUNCTION' '-DCONFIG_DISABLE_ES2015_ARRAYBUFFER_BUILTIN', + '-DCONFIG_DISABLE_ES2015_TEMPLATE_STRINGS', '-DCONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN', '-DCONFIG_ECMA_LCACHE_DISABLE', '-DCONFIG_ECMA_PROPERTY_HASHMAP_DISABLE', diff --git a/tests/jerry/es2015/template_string.js b/tests/jerry/es2015/template_string.js new file mode 100644 index 000000000..fc5c28610 --- /dev/null +++ b/tests/jerry/es2015/template_string.js @@ -0,0 +1,92 @@ +/* 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 must_throw (str) +{ + try + { + eval ("switch (1) { default: " + str + "}"); + assert (false); + } + catch (e) + { + } + + try + { + eval (str); + assert (false); + } + catch (e) + { + } +} + +var a = 'A'; +var b = 'B'; + +switch (1) +{ +default: + + `` + `abc` + `ab${a+b}${ `x` }c` + + assert (`` === ''); + assert (`abc` === 'abc'); + assert (`ab\ + c` === 'ab c'); + assert (`ab + c` === 'ab\n c'); + + assert (`prefix${a}` === 'prefixA'); + assert (`${a}postfix` === 'Apostfix'); + assert (`prefix${a}postfix` === 'prefixApostfix'); + + assert (`${a}${b}` === 'AB'); + assert (`${a},${b}` === 'A,B'); + assert (`${a}${b}${a}${b}` === 'ABAB'); + assert (`${a},${b},${a},${b}` === 'A,B,A,B'); + assert (`$${a},${b},${a},${b}$` === '$A,B,A,B$'); + + assert (`\${}` === '${}'); + assert (`$\{}` === '${}'); + assert (`x${ `y` + `z` }x` === 'xyzx'); + assert (`x${ `y` , `z` }x` === 'xzx'); + + function f(x) { return x + 1; } + + /* Precedence. */ + var c = 1; + assert (`x${ f(1) * f(2) }x${ c = 4 }` === 'x6x4'); + assert (c === 4); + assert (`m${0 || 93}n${7 && 0}o` === 'm93n0o'); + + /* Result is always a string. */ + assert (`${ function() { return true } () }` === 'true'); + assert (`${ function() { return a.length } () }` === '1'); + + /* Result is a single string with its properties. */ + assert(`${a}${b}${a}${b}`.length === 4); +} + +must_throw ("`"); +must_throw ("`${"); +must_throw ("`${7"); +must_throw ("`${}`"); +must_throw ("`${1}"); +must_throw ("`${1}.${"); +must_throw ("`${1}.${2}");