mirror of
https://github.com/jerryscript-project/jerryscript.git
synced 2025-12-15 16:29:21 +00:00
This patch substitutes all binary lvalue operators with an assigment + the corresponding binary operator. E.g. A += (expression) is pasred as A = A + (expression). Due to this replacement, all the related binary lvalue CBC opcodes can be removed. Also the arithmetic related VM instructions can put their result directly onto the stack, since no more checking is needed. JerryScript-DCO-1.0-Signed-off-by: Robert Fancsik frobert@inf.u-szeged.hu
2321 lines
72 KiB
C
2321 lines
72 KiB
C
/* 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-internal.h"
|
|
|
|
#ifndef JERRY_DISABLE_JS_PARSER
|
|
#include "jcontext.h"
|
|
#include "lit-char-helpers.h"
|
|
|
|
/** \addtogroup parser Parser
|
|
* @{
|
|
*
|
|
* \addtogroup jsparser JavaScript
|
|
* @{
|
|
*
|
|
* \addtogroup jsparser_expr Expression parser
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* Precedence of the binary tokens.
|
|
*
|
|
* See also:
|
|
* lexer_token_type_t
|
|
*/
|
|
static const uint8_t parser_binary_precedence_table[36] =
|
|
{
|
|
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
|
4, 5, 6, 7, 8, 9, 10, 10, 10, 10,
|
|
11, 11, 11, 11, 11, 11, 12, 12, 12,
|
|
13, 13, 14, 14, 14
|
|
};
|
|
|
|
/**
|
|
* Generate byte code for operators with lvalue.
|
|
*/
|
|
static inline void
|
|
parser_push_result (parser_context_t *context_p) /**< context */
|
|
{
|
|
if (CBC_NO_RESULT_OPERATION (context_p->last_cbc_opcode))
|
|
{
|
|
JERRY_ASSERT (CBC_SAME_ARGS (context_p->last_cbc_opcode, context_p->last_cbc_opcode + 1));
|
|
|
|
if ((context_p->last_cbc_opcode == CBC_POST_INCR
|
|
|| context_p->last_cbc_opcode == CBC_POST_DECR)
|
|
&& context_p->stack_depth >= context_p->stack_limit)
|
|
{
|
|
/* Stack limit is increased for CBC_POST_INCR_PUSH_RESULT
|
|
* and CBC_POST_DECR_PUSH_RESULT opcodes. Needed by vm.c. */
|
|
JERRY_ASSERT (context_p->stack_depth == context_p->stack_limit);
|
|
|
|
context_p->stack_limit++;
|
|
|
|
if (context_p->stack_limit > PARSER_MAXIMUM_STACK_LIMIT)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_STACK_LIMIT_REACHED);
|
|
}
|
|
}
|
|
|
|
context_p->last_cbc_opcode++;
|
|
parser_flush_cbc (context_p);
|
|
}
|
|
} /* parser_push_result */
|
|
|
|
/**
|
|
* Generate byte code for operators with lvalue.
|
|
*/
|
|
static void
|
|
parser_emit_unary_lvalue_opcode (parser_context_t *context_p, /**< context */
|
|
cbc_opcode_t opcode) /**< opcode */
|
|
{
|
|
if (PARSER_IS_PUSH_LITERAL (context_p->last_cbc_opcode)
|
|
&& context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL)
|
|
{
|
|
if (context_p->status_flags & PARSER_IS_STRICT)
|
|
{
|
|
if (context_p->last_cbc.literal_object_type != LEXER_LITERAL_OBJECT_ANY)
|
|
{
|
|
parser_error_t error;
|
|
|
|
if (context_p->last_cbc.literal_object_type == LEXER_LITERAL_OBJECT_EVAL)
|
|
{
|
|
error = PARSER_ERR_EVAL_CANNOT_ASSIGNED;
|
|
}
|
|
else
|
|
{
|
|
JERRY_ASSERT (context_p->last_cbc.literal_object_type == LEXER_LITERAL_OBJECT_ARGUMENTS);
|
|
error = PARSER_ERR_ARGUMENTS_CANNOT_ASSIGNED;
|
|
}
|
|
parser_raise_error (context_p, error);
|
|
}
|
|
if (opcode == CBC_DELETE_PUSH_RESULT)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_DELETE_IDENT_NOT_ALLOWED);
|
|
}
|
|
}
|
|
|
|
if (opcode == CBC_DELETE_PUSH_RESULT)
|
|
{
|
|
context_p->status_flags |= PARSER_LEXICAL_ENV_NEEDED;
|
|
|
|
if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL)
|
|
{
|
|
context_p->last_cbc_opcode = CBC_DELETE_IDENT_PUSH_RESULT;
|
|
}
|
|
else if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS)
|
|
{
|
|
context_p->last_cbc_opcode = CBC_PUSH_LITERAL;
|
|
parser_emit_cbc_literal (context_p,
|
|
CBC_DELETE_IDENT_PUSH_RESULT,
|
|
context_p->last_cbc.value);
|
|
}
|
|
else
|
|
{
|
|
JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_THREE_LITERALS);
|
|
|
|
context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS;
|
|
parser_emit_cbc_literal (context_p,
|
|
CBC_DELETE_IDENT_PUSH_RESULT,
|
|
context_p->last_cbc.third_literal_index);
|
|
}
|
|
return;
|
|
}
|
|
|
|
JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_LITERAL, opcode + CBC_UNARY_LVALUE_WITH_IDENT));
|
|
|
|
if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL)
|
|
{
|
|
context_p->last_cbc_opcode = (uint16_t) (opcode + CBC_UNARY_LVALUE_WITH_IDENT);
|
|
}
|
|
else if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS)
|
|
{
|
|
context_p->last_cbc_opcode = CBC_PUSH_LITERAL;
|
|
parser_emit_cbc_literal (context_p,
|
|
(uint16_t) (opcode + CBC_UNARY_LVALUE_WITH_IDENT),
|
|
context_p->last_cbc.value);
|
|
}
|
|
else
|
|
{
|
|
JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_THREE_LITERALS);
|
|
|
|
context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS;
|
|
parser_emit_cbc_literal (context_p,
|
|
(uint16_t) (opcode + CBC_UNARY_LVALUE_WITH_IDENT),
|
|
context_p->last_cbc.third_literal_index);
|
|
}
|
|
}
|
|
else if (context_p->last_cbc_opcode == CBC_PUSH_PROP)
|
|
{
|
|
JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_PROP, opcode));
|
|
context_p->last_cbc_opcode = (uint16_t) opcode;
|
|
}
|
|
else
|
|
{
|
|
switch (context_p->last_cbc_opcode)
|
|
{
|
|
case CBC_PUSH_PROP_LITERAL:
|
|
{
|
|
JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_PROP_LITERAL, CBC_PUSH_LITERAL));
|
|
context_p->last_cbc_opcode = CBC_PUSH_LITERAL;
|
|
break;
|
|
}
|
|
case CBC_PUSH_PROP_LITERAL_LITERAL:
|
|
{
|
|
JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_PROP_LITERAL_LITERAL, CBC_PUSH_TWO_LITERALS));
|
|
context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS;
|
|
break;
|
|
}
|
|
case CBC_PUSH_PROP_THIS_LITERAL:
|
|
{
|
|
JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_PROP_THIS_LITERAL, CBC_PUSH_THIS_LITERAL));
|
|
context_p->last_cbc_opcode = CBC_PUSH_THIS_LITERAL;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
/* Invalid LeftHandSide expression. */
|
|
parser_emit_cbc_ext (context_p, (opcode == CBC_DELETE_PUSH_RESULT) ? CBC_EXT_PUSH_UNDEFINED_BASE
|
|
: CBC_EXT_THROW_REFERENCE_ERROR);
|
|
break;
|
|
}
|
|
}
|
|
parser_emit_cbc (context_p, (uint16_t) opcode);
|
|
}
|
|
} /* parser_emit_unary_lvalue_opcode */
|
|
|
|
/**
|
|
* Parse array literal.
|
|
*/
|
|
static void
|
|
parser_parse_array_literal (parser_context_t *context_p) /**< context */
|
|
{
|
|
uint32_t pushed_items = 0;
|
|
|
|
JERRY_ASSERT (context_p->token.type == LEXER_LEFT_SQUARE);
|
|
|
|
parser_emit_cbc (context_p, CBC_CREATE_ARRAY);
|
|
lexer_next_token (context_p);
|
|
|
|
while (true)
|
|
{
|
|
if (context_p->token.type == LEXER_RIGHT_SQUARE)
|
|
{
|
|
if (pushed_items > 0)
|
|
{
|
|
parser_emit_cbc_call (context_p, CBC_ARRAY_APPEND, pushed_items);
|
|
}
|
|
return;
|
|
}
|
|
|
|
pushed_items++;
|
|
|
|
if (context_p->token.type == LEXER_COMMA)
|
|
{
|
|
parser_emit_cbc (context_p, CBC_PUSH_ELISION);
|
|
lexer_next_token (context_p);
|
|
}
|
|
else
|
|
{
|
|
parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA);
|
|
|
|
if (context_p->token.type == LEXER_COMMA)
|
|
{
|
|
lexer_next_token (context_p);
|
|
}
|
|
else if (context_p->token.type != LEXER_RIGHT_SQUARE)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_ARRAY_ITEM_SEPARATOR_EXPECTED);
|
|
}
|
|
}
|
|
|
|
if (pushed_items >= 64)
|
|
{
|
|
parser_emit_cbc_call (context_p, CBC_ARRAY_APPEND, pushed_items);
|
|
pushed_items = 0;
|
|
}
|
|
}
|
|
} /* parser_parse_array_literal */
|
|
|
|
#ifdef CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER
|
|
/**
|
|
* Object literal item types.
|
|
*/
|
|
typedef enum
|
|
{
|
|
PARSER_OBJECT_PROPERTY_START, /**< marks the start of the property list */
|
|
PARSER_OBJECT_PROPERTY_VALUE, /**< value property */
|
|
PARSER_OBJECT_PROPERTY_GETTER, /**< getter property */
|
|
PARSER_OBJECT_PROPERTY_SETTER, /**< setter property */
|
|
PARSER_OBJECT_PROPERTY_BOTH_ACCESSORS, /**< both getter and setter properties are set */
|
|
} parser_object_literal_item_types_t;
|
|
|
|
/**
|
|
* Parse object literal.
|
|
*/
|
|
static void
|
|
parser_append_object_literal_item (parser_context_t *context_p, /**< context */
|
|
uint16_t item_index, /**< index of the item name */
|
|
parser_object_literal_item_types_t item_type) /**< type of the item */
|
|
{
|
|
parser_stack_iterator_t iterator;
|
|
uint8_t *current_item_type_p;
|
|
|
|
iterator.current_p = context_p->stack.first_p;
|
|
iterator.current_position = context_p->stack.last_position;
|
|
|
|
while (true)
|
|
{
|
|
current_item_type_p = iterator.current_p->bytes + iterator.current_position - 1;
|
|
|
|
if (*current_item_type_p == PARSER_OBJECT_PROPERTY_START)
|
|
{
|
|
parser_stack_push_uint16 (context_p, item_index);
|
|
parser_stack_push_uint8 (context_p, (uint8_t) item_type);
|
|
return;
|
|
}
|
|
|
|
iterator.current_position--;
|
|
if (iterator.current_position == 0)
|
|
{
|
|
iterator.current_p = iterator.current_p->next_p;
|
|
iterator.current_position = PARSER_STACK_PAGE_SIZE;
|
|
}
|
|
|
|
uint32_t current_item_index = iterator.current_p->bytes[iterator.current_position - 1];
|
|
|
|
iterator.current_position--;
|
|
if (iterator.current_position == 0)
|
|
{
|
|
iterator.current_p = iterator.current_p->next_p;
|
|
iterator.current_position = PARSER_STACK_PAGE_SIZE;
|
|
}
|
|
|
|
current_item_index |= ((uint32_t) iterator.current_p->bytes[iterator.current_position - 1]) << 8;
|
|
|
|
iterator.current_position--;
|
|
if (iterator.current_position == 0)
|
|
{
|
|
iterator.current_p = iterator.current_p->next_p;
|
|
iterator.current_position = PARSER_STACK_PAGE_SIZE;
|
|
}
|
|
|
|
if (current_item_index == item_index)
|
|
{
|
|
if (item_type == PARSER_OBJECT_PROPERTY_VALUE
|
|
&& *current_item_type_p == PARSER_OBJECT_PROPERTY_VALUE
|
|
&& !(context_p->status_flags & PARSER_IS_STRICT))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (item_type == PARSER_OBJECT_PROPERTY_GETTER
|
|
&& *current_item_type_p == PARSER_OBJECT_PROPERTY_SETTER)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (item_type == PARSER_OBJECT_PROPERTY_SETTER
|
|
&& *current_item_type_p == PARSER_OBJECT_PROPERTY_GETTER)
|
|
{
|
|
break;
|
|
}
|
|
|
|
parser_raise_error (context_p, PARSER_ERR_OBJECT_PROPERTY_REDEFINED);
|
|
}
|
|
}
|
|
|
|
uint8_t *last_page_p = context_p->stack.first_p->bytes;
|
|
|
|
*current_item_type_p = PARSER_OBJECT_PROPERTY_BOTH_ACCESSORS;
|
|
|
|
if (current_item_type_p == (last_page_p + context_p->stack.last_position - 1))
|
|
{
|
|
context_p->stack_top_uint8 = PARSER_OBJECT_PROPERTY_BOTH_ACCESSORS;
|
|
}
|
|
} /* parser_append_object_literal_item */
|
|
#endif /* CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER */
|
|
|
|
#ifndef CONFIG_DISABLE_ES2015_CLASS
|
|
|
|
#ifdef CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER
|
|
#error "Class support requires ES2015 object literal support"
|
|
#endif /* CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER */
|
|
|
|
/**
|
|
* Parse class as an object literal.
|
|
*/
|
|
static void
|
|
parser_parse_class_literal (parser_context_t *context_p) /**< context */
|
|
{
|
|
JERRY_ASSERT (context_p->token.type == LEXER_LEFT_BRACE);
|
|
parser_emit_cbc (context_p, CBC_CREATE_OBJECT);
|
|
|
|
bool super_called = false;
|
|
uint32_t status_flags = PARSER_IS_FUNCTION | PARSER_IS_CLOSURE | (context_p->status_flags & PARSER_CLASS_HAS_SUPER);
|
|
|
|
while (true)
|
|
{
|
|
if (!(status_flags & PARSER_CLASS_STATIC_FUNCTION))
|
|
{
|
|
lexer_skip_empty_statements (context_p);
|
|
}
|
|
|
|
lexer_expect_object_literal_id (context_p, LEXER_OBJ_IDENT_CLASS_METHOD);
|
|
|
|
if (context_p->token.type == LEXER_RIGHT_BRACE)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (context_p->token.type == LEXER_PROPERTY_GETTER || context_p->token.type == LEXER_PROPERTY_SETTER)
|
|
{
|
|
uint16_t literal_index, function_literal_index;
|
|
bool is_getter = (context_p->token.type == LEXER_PROPERTY_GETTER);
|
|
|
|
uint32_t accessor_status_flags = PARSER_IS_FUNCTION | PARSER_IS_CLOSURE;
|
|
accessor_status_flags |= (is_getter ? PARSER_IS_PROPERTY_GETTER : PARSER_IS_PROPERTY_SETTER);
|
|
|
|
lexer_expect_object_literal_id (context_p, LEXER_OBJ_IDENT_CLASS_METHOD | LEXER_OBJ_IDENT_ONLY_IDENTIFIERS);
|
|
literal_index = context_p->lit_object.index;
|
|
|
|
bool is_computed = false;
|
|
|
|
if (context_p->token.type == LEXER_RIGHT_SQUARE)
|
|
{
|
|
is_computed = true;
|
|
}
|
|
else if (!(status_flags & PARSER_CLASS_STATIC_FUNCTION)
|
|
&& lexer_compare_raw_identifier_to_current (context_p, "constructor", 11))
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_CLASS_CONSTRUCTOR_AS_ACCESSOR);
|
|
}
|
|
|
|
parser_flush_cbc (context_p);
|
|
function_literal_index = lexer_construct_function_object (context_p, accessor_status_flags);
|
|
|
|
parser_emit_cbc_literal (context_p,
|
|
CBC_PUSH_LITERAL,
|
|
literal_index);
|
|
|
|
JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_LITERAL);
|
|
|
|
cbc_ext_opcode_t opcode;
|
|
bool is_static = (status_flags & PARSER_CLASS_STATIC_FUNCTION) != 0;
|
|
|
|
if (is_computed)
|
|
{
|
|
context_p->last_cbc.literal_index = function_literal_index;
|
|
|
|
if (is_getter)
|
|
{
|
|
opcode = is_static ? CBC_EXT_SET_STATIC_COMPUTED_GETTER : CBC_EXT_SET_COMPUTED_GETTER;
|
|
}
|
|
else
|
|
{
|
|
opcode = is_static ? CBC_EXT_SET_STATIC_COMPUTED_SETTER : CBC_EXT_SET_COMPUTED_SETTER;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
context_p->last_cbc.value = function_literal_index;
|
|
|
|
if (is_getter)
|
|
{
|
|
opcode = is_static ? CBC_EXT_SET_STATIC_GETTER : CBC_EXT_SET_GETTER;
|
|
}
|
|
else
|
|
{
|
|
opcode = is_static ? CBC_EXT_SET_STATIC_SETTER : CBC_EXT_SET_SETTER;
|
|
}
|
|
}
|
|
|
|
context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (opcode);
|
|
status_flags &= (uint32_t) ~PARSER_CLASS_STATIC_FUNCTION;
|
|
continue;
|
|
}
|
|
|
|
if (!(status_flags & PARSER_CLASS_STATIC_FUNCTION) && context_p->token.type == LEXER_CLASS_CONSTRUCTOR)
|
|
{
|
|
if (super_called)
|
|
{
|
|
/* 14.5.1 */
|
|
parser_raise_error (context_p, PARSER_ERR_MULTIPLE_CLASS_CONSTRUCTORS);
|
|
}
|
|
else
|
|
{
|
|
super_called = true;
|
|
}
|
|
|
|
parser_flush_cbc (context_p);
|
|
uint32_t constructor_status_flags = status_flags | PARSER_CLASS_CONSTRUCTOR;
|
|
|
|
if (context_p->status_flags & PARSER_CLASS_HAS_SUPER)
|
|
{
|
|
constructor_status_flags |= PARSER_LEXICAL_ENV_NEEDED;
|
|
}
|
|
|
|
if (context_p->literal_count >= PARSER_MAXIMUM_NUMBER_OF_LITERALS)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_LITERAL_LIMIT_REACHED);
|
|
}
|
|
|
|
lexer_literal_t *literal_p = (lexer_literal_t *) parser_list_append (context_p, &context_p->literal_pool);
|
|
literal_p->type = LEXER_UNUSED_LITERAL;
|
|
literal_p->status_flags = 0;
|
|
literal_p->u.bytecode_p = parser_parse_function (context_p, constructor_status_flags);
|
|
literal_p->type = LEXER_FUNCTION_LITERAL;
|
|
parser_emit_cbc_literal (context_p, PARSER_TO_EXT_OPCODE (CBC_EXT_SET_CLASS_LITERAL), context_p->literal_count);
|
|
context_p->literal_count++;
|
|
continue;
|
|
}
|
|
|
|
if (!(status_flags & PARSER_CLASS_STATIC_FUNCTION) && context_p->token.type == LEXER_KEYW_STATIC)
|
|
{
|
|
status_flags |= PARSER_CLASS_STATIC_FUNCTION;
|
|
continue;
|
|
}
|
|
|
|
bool is_computed = false;
|
|
|
|
if (context_p->token.type == LEXER_RIGHT_SQUARE)
|
|
{
|
|
is_computed = true;
|
|
}
|
|
else if ((status_flags & PARSER_CLASS_STATIC_FUNCTION)
|
|
&& lexer_compare_raw_identifier_to_current (context_p, "prototype", 9))
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_CLASS_STATIC_PROTOTYPE);
|
|
}
|
|
|
|
parser_flush_cbc (context_p);
|
|
|
|
uint16_t literal_index = context_p->lit_object.index;
|
|
uint16_t function_literal_index = lexer_construct_function_object (context_p, status_flags);
|
|
|
|
parser_emit_cbc_literal (context_p,
|
|
CBC_PUSH_LITERAL,
|
|
function_literal_index);
|
|
|
|
JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_LITERAL);
|
|
|
|
context_p->last_cbc.value = literal_index;
|
|
|
|
if ((status_flags & PARSER_CLASS_STATIC_FUNCTION))
|
|
{
|
|
context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (is_computed ? CBC_EXT_SET_STATIC_COMPUTED_PROPERTY_LITERAL
|
|
: CBC_EXT_SET_STATIC_PROPERTY_LITERAL);
|
|
status_flags &= (uint32_t) ~PARSER_CLASS_STATIC_FUNCTION;
|
|
}
|
|
else
|
|
{
|
|
context_p->last_cbc_opcode = (is_computed ? PARSER_TO_EXT_OPCODE (CBC_EXT_SET_COMPUTED_PROPERTY_LITERAL)
|
|
: CBC_SET_LITERAL_PROPERTY);
|
|
}
|
|
}
|
|
|
|
if (!super_called && (context_p->status_flags & PARSER_CLASS_HAS_SUPER))
|
|
{
|
|
parser_emit_cbc_ext (context_p, CBC_EXT_IMPLICIT_CONSTRUCTOR_CALL);
|
|
}
|
|
|
|
if (context_p->status_flags & PARSER_CLASS_HAS_SUPER)
|
|
{
|
|
parser_emit_cbc_ext (context_p, CBC_EXT_INHERIT_AND_SET_CONSTRUCTOR);
|
|
}
|
|
} /* parser_parse_class_literal */
|
|
|
|
/**
|
|
* Description of "prototype" literal string.
|
|
*/
|
|
static const lexer_lit_location_t lexer_prototype_literal =
|
|
{
|
|
(const uint8_t *) "prototype", 9, LEXER_STRING_LITERAL, false
|
|
};
|
|
|
|
/**
|
|
* Parse class statement or expression.
|
|
*/
|
|
void
|
|
parser_parse_class (parser_context_t *context_p, /**< context */
|
|
bool is_statement) /**< true - if class is parsed as a statement
|
|
* false - otherwise (as an expression) */
|
|
{
|
|
JERRY_ASSERT (context_p->token.type == LEXER_KEYW_CLASS);
|
|
|
|
context_p->status_flags &= (uint32_t) ~PARSER_CLASS_HAS_SUPER;
|
|
|
|
uint16_t class_ident_index = PARSER_MAXIMUM_NUMBER_OF_LITERALS;
|
|
|
|
if (is_statement)
|
|
{
|
|
/* Class statement must contain an identifier. */
|
|
lexer_expect_identifier (context_p, LEXER_IDENT_LITERAL);
|
|
JERRY_ASSERT (context_p->token.type == LEXER_LITERAL
|
|
&& context_p->token.lit_location.type == LEXER_IDENT_LITERAL);
|
|
|
|
class_ident_index = context_p->lit_object.index;
|
|
context_p->lit_object.literal_p->status_flags |= LEXER_FLAG_VAR;
|
|
lexer_next_token (context_p);
|
|
}
|
|
else
|
|
{
|
|
lexer_next_token (context_p);
|
|
|
|
/* Class expression may contain an identifier. */
|
|
if (context_p->token.type == LEXER_LITERAL && context_p->token.lit_location.type == LEXER_IDENT_LITERAL)
|
|
{
|
|
/* NOTE: If 'Function.name' will be supported, the current literal object must be set to 'name' property. */
|
|
lexer_next_token (context_p);
|
|
}
|
|
}
|
|
|
|
if (context_p->token.type == LEXER_KEYW_EXTENDS)
|
|
{
|
|
parser_parse_super_class_context_start (context_p);
|
|
}
|
|
|
|
if (context_p->token.type != LEXER_LEFT_BRACE)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_LEFT_BRACE_EXPECTED);
|
|
}
|
|
|
|
parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_CLASS_CONSTRUCTOR);
|
|
|
|
bool is_strict = context_p->status_flags & PARSER_IS_STRICT;
|
|
|
|
/* 14.5. A ClassBody is always strict code. */
|
|
context_p->status_flags |= PARSER_IS_STRICT;
|
|
|
|
/* ClassDeclaration is parsed. Continue with class body. */
|
|
parser_parse_class_literal (context_p);
|
|
|
|
JERRY_ASSERT (context_p->token.type == LEXER_RIGHT_BRACE);
|
|
|
|
lexer_construct_literal_object (context_p,
|
|
(lexer_lit_location_t *) &lexer_prototype_literal,
|
|
lexer_prototype_literal.type);
|
|
|
|
parser_emit_cbc_literal (context_p, CBC_SET_PROPERTY, context_p->lit_object.index);
|
|
|
|
if (is_statement)
|
|
{
|
|
parser_emit_cbc_literal (context_p, CBC_ASSIGN_SET_IDENT, class_ident_index);
|
|
}
|
|
|
|
if (context_p->status_flags & PARSER_CLASS_HAS_SUPER)
|
|
{
|
|
parser_parse_super_class_context_end (context_p, is_statement);
|
|
context_p->status_flags &= (uint32_t) ~PARSER_CLASS_HAS_SUPER;
|
|
}
|
|
|
|
parser_flush_cbc (context_p);
|
|
|
|
if (!is_strict)
|
|
{
|
|
/* Restore flag */
|
|
context_p->status_flags &= (uint32_t) ~PARSER_IS_STRICT;
|
|
}
|
|
|
|
lexer_next_token (context_p);
|
|
} /* parser_parse_class */
|
|
#endif /* !CONFIG_DISABLE_ES2015_CLASS */
|
|
|
|
#ifndef CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER
|
|
/**
|
|
* Parse object initializer method definition.
|
|
*
|
|
* See also: ES2015 14.3
|
|
*/
|
|
static void
|
|
parser_parse_object_method (parser_context_t *context_p) /**< context */
|
|
{
|
|
parser_flush_cbc (context_p);
|
|
|
|
context_p->source_p--;
|
|
context_p->column--;
|
|
uint16_t function_literal_index = lexer_construct_function_object (context_p,
|
|
PARSER_IS_FUNCTION | PARSER_IS_CLOSURE);
|
|
|
|
parser_emit_cbc_literal (context_p,
|
|
CBC_PUSH_LITERAL,
|
|
function_literal_index);
|
|
|
|
lexer_next_token (context_p);
|
|
} /* parser_parse_object_method */
|
|
#endif /* !CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER */
|
|
|
|
/**
|
|
* Parse object literal.
|
|
*/
|
|
static void
|
|
parser_parse_object_literal (parser_context_t *context_p) /**< context */
|
|
{
|
|
JERRY_ASSERT (context_p->token.type == LEXER_LEFT_BRACE);
|
|
|
|
parser_emit_cbc (context_p, CBC_CREATE_OBJECT);
|
|
|
|
#ifdef CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER
|
|
parser_stack_push_uint8 (context_p, PARSER_OBJECT_PROPERTY_START);
|
|
#endif /* CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER */
|
|
|
|
while (true)
|
|
{
|
|
lexer_expect_object_literal_id (context_p, LEXER_OBJ_IDENT_NO_OPTS);
|
|
|
|
switch (context_p->token.type)
|
|
{
|
|
case LEXER_RIGHT_BRACE:
|
|
{
|
|
break;
|
|
}
|
|
case LEXER_PROPERTY_GETTER:
|
|
case LEXER_PROPERTY_SETTER:
|
|
{
|
|
uint32_t status_flags;
|
|
cbc_ext_opcode_t opcode;
|
|
uint16_t literal_index, function_literal_index;
|
|
#ifdef CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER
|
|
parser_object_literal_item_types_t item_type;
|
|
#endif /* CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER */
|
|
|
|
if (context_p->token.type == LEXER_PROPERTY_GETTER)
|
|
{
|
|
status_flags = PARSER_IS_FUNCTION | PARSER_IS_CLOSURE | PARSER_IS_PROPERTY_GETTER;
|
|
opcode = CBC_EXT_SET_GETTER;
|
|
#ifdef CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER
|
|
item_type = PARSER_OBJECT_PROPERTY_GETTER;
|
|
#endif /* CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER */
|
|
}
|
|
else
|
|
{
|
|
status_flags = PARSER_IS_FUNCTION | PARSER_IS_CLOSURE | PARSER_IS_PROPERTY_SETTER;
|
|
opcode = CBC_EXT_SET_SETTER;
|
|
#ifdef CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER
|
|
item_type = PARSER_OBJECT_PROPERTY_SETTER;
|
|
#endif /* CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER */
|
|
}
|
|
|
|
lexer_expect_object_literal_id (context_p, LEXER_OBJ_IDENT_ONLY_IDENTIFIERS);
|
|
|
|
/* This assignment is a nop for computed getters/setters. */
|
|
literal_index = context_p->lit_object.index;
|
|
|
|
#ifndef CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER
|
|
if (context_p->token.type == LEXER_RIGHT_SQUARE)
|
|
{
|
|
opcode = ((opcode == CBC_EXT_SET_GETTER) ? CBC_EXT_SET_COMPUTED_GETTER
|
|
: CBC_EXT_SET_COMPUTED_SETTER);
|
|
}
|
|
#else /* CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER */
|
|
parser_append_object_literal_item (context_p, literal_index, item_type);
|
|
#endif /* !CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER */
|
|
|
|
parser_flush_cbc (context_p);
|
|
function_literal_index = lexer_construct_function_object (context_p, status_flags);
|
|
|
|
#ifndef CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER
|
|
if (opcode >= CBC_EXT_SET_COMPUTED_GETTER)
|
|
{
|
|
literal_index = function_literal_index;
|
|
}
|
|
#endif /* !CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER */
|
|
|
|
parser_emit_cbc_literal (context_p,
|
|
CBC_PUSH_LITERAL,
|
|
literal_index);
|
|
|
|
JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_LITERAL);
|
|
context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (opcode);
|
|
context_p->last_cbc.value = function_literal_index;
|
|
|
|
lexer_next_token (context_p);
|
|
break;
|
|
}
|
|
#ifndef CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER
|
|
case LEXER_RIGHT_SQUARE:
|
|
{
|
|
lexer_next_token (context_p);
|
|
|
|
if (context_p->token.type == LEXER_LEFT_PAREN)
|
|
{
|
|
parser_parse_object_method (context_p);
|
|
|
|
JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_LITERAL);
|
|
context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_SET_COMPUTED_PROPERTY_LITERAL);
|
|
break;
|
|
}
|
|
|
|
if (context_p->token.type != LEXER_COLON)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_COLON_EXPECTED);
|
|
}
|
|
|
|
lexer_next_token (context_p);
|
|
parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA);
|
|
|
|
if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL)
|
|
{
|
|
context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_SET_COMPUTED_PROPERTY_LITERAL);
|
|
}
|
|
else
|
|
{
|
|
parser_emit_cbc_ext (context_p, CBC_EXT_SET_COMPUTED_PROPERTY);
|
|
}
|
|
break;
|
|
}
|
|
#endif /* !CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER */
|
|
default:
|
|
{
|
|
uint16_t literal_index = context_p->lit_object.index;
|
|
|
|
#ifdef CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER
|
|
parser_append_object_literal_item (context_p,
|
|
literal_index,
|
|
PARSER_OBJECT_PROPERTY_VALUE);
|
|
#else /* !CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER */
|
|
parser_line_counter_t start_line = context_p->token.line;
|
|
parser_line_counter_t start_column = context_p->token.column;
|
|
#endif /* CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER */
|
|
|
|
lexer_next_token (context_p);
|
|
|
|
#ifndef CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER
|
|
if (context_p->token.type == LEXER_LEFT_PAREN)
|
|
{
|
|
parser_parse_object_method (context_p);
|
|
|
|
JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_LITERAL);
|
|
context_p->last_cbc_opcode = CBC_SET_LITERAL_PROPERTY;
|
|
context_p->last_cbc.value = literal_index;
|
|
break;
|
|
}
|
|
|
|
if (context_p->token.type == LEXER_RIGHT_BRACE
|
|
|| context_p->token.type == LEXER_COMMA)
|
|
{
|
|
/* Re-parse the literal as common identifier. */
|
|
context_p->source_p = context_p->token.lit_location.char_p;
|
|
context_p->line = start_line;
|
|
context_p->column = start_column;
|
|
|
|
lexer_next_token (context_p);
|
|
|
|
if (context_p->token.type != LEXER_LITERAL
|
|
|| context_p->token.lit_location.type != LEXER_IDENT_LITERAL)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_IDENTIFIER_EXPECTED);
|
|
}
|
|
|
|
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);
|
|
|
|
context_p->last_cbc_opcode = CBC_SET_LITERAL_PROPERTY;
|
|
context_p->last_cbc.value = literal_index;
|
|
|
|
lexer_next_token (context_p);
|
|
break;
|
|
}
|
|
#endif /* !CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER */
|
|
|
|
if (context_p->token.type != LEXER_COLON)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_COLON_EXPECTED);
|
|
}
|
|
|
|
lexer_next_token (context_p);
|
|
parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA);
|
|
|
|
if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL)
|
|
{
|
|
context_p->last_cbc_opcode = CBC_SET_LITERAL_PROPERTY;
|
|
context_p->last_cbc.value = literal_index;
|
|
}
|
|
else
|
|
{
|
|
parser_emit_cbc_literal (context_p, CBC_SET_PROPERTY, literal_index);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (context_p->token.type == LEXER_RIGHT_BRACE)
|
|
{
|
|
break;
|
|
}
|
|
else if (context_p->token.type != LEXER_COMMA)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_OBJECT_ITEM_SEPARATOR_EXPECTED);
|
|
}
|
|
}
|
|
|
|
#ifdef CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER
|
|
while (context_p->stack_top_uint8 != PARSER_OBJECT_PROPERTY_START)
|
|
{
|
|
parser_stack_pop (context_p, NULL, 3);
|
|
}
|
|
|
|
parser_stack_pop_uint8 (context_p);
|
|
#endif /* CONFIG_DISABLE_ES2015_OBJECT_INITIALIZER */
|
|
} /* parser_parse_object_literal */
|
|
|
|
/**
|
|
* Parse function literal.
|
|
*/
|
|
static void
|
|
parser_parse_function_expression (parser_context_t *context_p, /**< context */
|
|
uint32_t status_flags) /**< function status flags */
|
|
{
|
|
int literals = 0;
|
|
uint16_t literal1 = 0;
|
|
uint16_t literal2 = 0;
|
|
uint16_t function_literal_index;
|
|
int32_t function_name_index = -1;
|
|
|
|
if (status_flags & PARSER_IS_FUNC_EXPRESSION)
|
|
{
|
|
#ifdef JERRY_DEBUGGER
|
|
parser_line_counter_t debugger_line = context_p->token.line;
|
|
parser_line_counter_t debugger_column = context_p->token.column;
|
|
#endif /* JERRY_DEBUGGER */
|
|
|
|
if (!lexer_check_next_character (context_p, LIT_CHAR_LEFT_PAREN))
|
|
{
|
|
lexer_next_token (context_p);
|
|
|
|
if (context_p->token.type != LEXER_LITERAL
|
|
|| context_p->token.lit_location.type != LEXER_IDENT_LITERAL)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_IDENTIFIER_EXPECTED);
|
|
}
|
|
|
|
parser_flush_cbc (context_p);
|
|
|
|
lexer_construct_literal_object (context_p, &context_p->token.lit_location, LEXER_STRING_LITERAL);
|
|
|
|
#ifdef JERRY_DEBUGGER
|
|
if (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED)
|
|
{
|
|
jerry_debugger_send_string (JERRY_DEBUGGER_FUNCTION_NAME,
|
|
JERRY_DEBUGGER_NO_SUBTYPE,
|
|
context_p->lit_object.literal_p->u.char_p,
|
|
context_p->lit_object.literal_p->prop.length);
|
|
|
|
/* Reset token position for the function. */
|
|
context_p->token.line = debugger_line;
|
|
context_p->token.column = debugger_column;
|
|
}
|
|
#endif /* JERRY_DEBUGGER */
|
|
|
|
if (context_p->token.literal_is_reserved
|
|
|| context_p->lit_object.type != LEXER_LITERAL_OBJECT_ANY)
|
|
{
|
|
status_flags |= PARSER_HAS_NON_STRICT_ARG;
|
|
}
|
|
|
|
function_name_index = context_p->lit_object.index;
|
|
}
|
|
}
|
|
|
|
if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL)
|
|
{
|
|
literals = 1;
|
|
literal1 = context_p->last_cbc.literal_index;
|
|
context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE;
|
|
}
|
|
else if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS)
|
|
{
|
|
literals = 2;
|
|
literal1 = context_p->last_cbc.literal_index;
|
|
literal2 = context_p->last_cbc.value;
|
|
context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE;
|
|
}
|
|
else
|
|
{
|
|
parser_flush_cbc (context_p);
|
|
}
|
|
|
|
function_literal_index = lexer_construct_function_object (context_p, status_flags);
|
|
|
|
JERRY_ASSERT (context_p->last_cbc_opcode == PARSER_CBC_UNAVAILABLE);
|
|
|
|
if (literals == 1)
|
|
{
|
|
context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS;
|
|
context_p->last_cbc.literal_index = literal1;
|
|
context_p->last_cbc.value = function_literal_index;
|
|
}
|
|
else if (literals == 2)
|
|
{
|
|
context_p->last_cbc_opcode = CBC_PUSH_THREE_LITERALS;
|
|
context_p->last_cbc.literal_index = literal1;
|
|
context_p->last_cbc.value = literal2;
|
|
context_p->last_cbc.third_literal_index = function_literal_index;
|
|
}
|
|
else
|
|
{
|
|
parser_emit_cbc_literal (context_p,
|
|
CBC_PUSH_LITERAL,
|
|
function_literal_index);
|
|
|
|
if (function_name_index != -1)
|
|
{
|
|
context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_NAMED_FUNC_EXPRESSION);
|
|
context_p->last_cbc.value = (uint16_t) function_name_index;
|
|
}
|
|
}
|
|
|
|
context_p->last_cbc.literal_type = LEXER_FUNCTION_LITERAL;
|
|
context_p->last_cbc.literal_object_type = LEXER_LITERAL_OBJECT_ANY;
|
|
} /* parser_parse_function_expression */
|
|
|
|
#ifndef CONFIG_DISABLE_ES2015_ARROW_FUNCTION
|
|
|
|
/**
|
|
* Checks whether the bracketed expression is an argument list of an arrow function.
|
|
*
|
|
* @return true - if an arrow function is found
|
|
* false - otherwise
|
|
*/
|
|
static bool
|
|
parser_check_arrow_function (parser_context_t *context_p) /**< context */
|
|
{
|
|
lexer_range_t range;
|
|
|
|
range.source_p = context_p->token.lit_location.char_p;
|
|
range.line = context_p->token.line;
|
|
range.column = context_p->token.column;
|
|
|
|
lexer_next_token (context_p);
|
|
|
|
bool is_arrow_function = true;
|
|
|
|
while (true)
|
|
{
|
|
if (context_p->token.type == LEXER_RIGHT_PAREN)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (context_p->token.type == LEXER_COMMA)
|
|
{
|
|
lexer_next_token (context_p);
|
|
|
|
if (context_p->token.type == LEXER_LITERAL
|
|
&& context_p->token.lit_location.type == LEXER_IDENT_LITERAL)
|
|
{
|
|
lexer_next_token (context_p);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
is_arrow_function = false;
|
|
break;
|
|
}
|
|
|
|
if (is_arrow_function)
|
|
{
|
|
lexer_next_token (context_p);
|
|
|
|
if (context_p->token.type != LEXER_ARROW)
|
|
{
|
|
is_arrow_function = false;
|
|
}
|
|
}
|
|
|
|
context_p->source_p = range.source_p;
|
|
context_p->line = range.line;
|
|
context_p->column = range.column;
|
|
|
|
/* Re-parse the original identifier. */
|
|
lexer_next_token (context_p);
|
|
|
|
if (is_arrow_function)
|
|
{
|
|
parser_parse_function_expression (context_p,
|
|
PARSER_IS_FUNCTION | PARSER_IS_ARROW_FUNCTION | PARSER_ARROW_PARSE_ARGS);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
} /* parser_check_arrow_function */
|
|
|
|
#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.
|
|
*/
|
|
static void
|
|
parser_parse_unary_expression (parser_context_t *context_p, /**< context */
|
|
size_t *grouping_level_p) /**< grouping level */
|
|
{
|
|
int new_was_seen = 0;
|
|
|
|
/* Collect unary operators. */
|
|
while (true)
|
|
{
|
|
/* Convert plus and minus binary operators to unary operators. */
|
|
if (context_p->token.type == LEXER_ADD)
|
|
{
|
|
context_p->token.type = LEXER_PLUS;
|
|
}
|
|
else if (context_p->token.type == LEXER_SUBTRACT)
|
|
{
|
|
context_p->token.type = LEXER_NEGATE;
|
|
}
|
|
|
|
/* Bracketed expressions are primary expressions. At this
|
|
* point their left paren is pushed onto the stack and
|
|
* they are processed when their closing paren is reached. */
|
|
if (context_p->token.type == LEXER_LEFT_PAREN)
|
|
{
|
|
(*grouping_level_p)++;
|
|
new_was_seen = 0;
|
|
}
|
|
else if (context_p->token.type == LEXER_KEYW_NEW)
|
|
{
|
|
/* After 'new' unary operators are not allowed. */
|
|
new_was_seen = 1;
|
|
}
|
|
else if (new_was_seen || !LEXER_IS_UNARY_OP_TOKEN (context_p->token.type))
|
|
{
|
|
break;
|
|
}
|
|
|
|
parser_stack_push_uint8 (context_p, context_p->token.type);
|
|
lexer_next_token (context_p);
|
|
}
|
|
|
|
/* 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
|
|
if (context_p->token.lit_location.type == LEXER_IDENT_LITERAL)
|
|
{
|
|
switch (lexer_check_arrow (context_p))
|
|
{
|
|
case LEXER_COMMA:
|
|
case LEXER_RIGHT_PAREN:
|
|
{
|
|
if (context_p->stack_top_uint8 == LEXER_LEFT_PAREN
|
|
&& parser_check_arrow_function (context_p))
|
|
{
|
|
(*grouping_level_p)--;
|
|
parser_stack_pop_uint8 (context_p);
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
case LEXER_ARROW:
|
|
{
|
|
parser_parse_function_expression (context_p,
|
|
PARSER_IS_FUNCTION | PARSER_IS_ARROW_FUNCTION);
|
|
return;
|
|
}
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif /* !CONFIG_DISABLE_ES2015_ARROW_FUNCTION */
|
|
|
|
if (context_p->token.lit_location.type == LEXER_IDENT_LITERAL
|
|
|| context_p->token.lit_location.type == LEXER_STRING_LITERAL)
|
|
{
|
|
lexer_construct_literal_object (context_p,
|
|
&context_p->token.lit_location,
|
|
context_p->token.lit_location.type);
|
|
}
|
|
else if (context_p->token.lit_location.type == LEXER_NUMBER_LITERAL)
|
|
{
|
|
bool is_negative_number = false;
|
|
|
|
while (context_p->stack_top_uint8 == LEXER_PLUS
|
|
|| context_p->stack_top_uint8 == LEXER_NEGATE)
|
|
{
|
|
if (context_p->stack_top_uint8 == LEXER_NEGATE)
|
|
{
|
|
is_negative_number = !is_negative_number;
|
|
}
|
|
parser_stack_pop_uint8 (context_p);
|
|
}
|
|
|
|
if (lexer_construct_number_object (context_p, true, is_negative_number))
|
|
{
|
|
JERRY_ASSERT (context_p->lit_object.index <= CBC_PUSH_NUMBER_BYTE_RANGE_END);
|
|
|
|
parser_emit_cbc_push_number (context_p, is_negative_number);
|
|
break;
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
context_p->last_cbc_opcode = CBC_PUSH_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;
|
|
break;
|
|
}
|
|
|
|
if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS)
|
|
{
|
|
context_p->last_cbc_opcode = CBC_PUSH_THREE_LITERALS;
|
|
context_p->last_cbc.third_literal_index = 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;
|
|
break;
|
|
}
|
|
|
|
if (context_p->last_cbc_opcode == CBC_PUSH_THIS)
|
|
{
|
|
context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE;
|
|
opcode = CBC_PUSH_THIS_LITERAL;
|
|
}
|
|
}
|
|
|
|
parser_emit_cbc_literal_from_token (context_p, (uint16_t) opcode);
|
|
break;
|
|
}
|
|
case LEXER_KEYW_FUNCTION:
|
|
{
|
|
parser_parse_function_expression (context_p,
|
|
PARSER_IS_FUNCTION | PARSER_IS_FUNC_EXPRESSION | PARSER_IS_CLOSURE);
|
|
break;
|
|
}
|
|
case LEXER_LEFT_BRACE:
|
|
{
|
|
parser_parse_object_literal (context_p);
|
|
break;
|
|
}
|
|
case LEXER_LEFT_SQUARE:
|
|
{
|
|
parser_parse_array_literal (context_p);
|
|
break;
|
|
}
|
|
case LEXER_DIVIDE:
|
|
case LEXER_ASSIGN_DIVIDE:
|
|
{
|
|
lexer_construct_regexp_object (context_p, false);
|
|
|
|
if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL)
|
|
{
|
|
context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS;
|
|
context_p->last_cbc.value = (uint16_t) (context_p->literal_count - 1);
|
|
}
|
|
else if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS)
|
|
{
|
|
context_p->last_cbc_opcode = CBC_PUSH_THREE_LITERALS;
|
|
context_p->last_cbc.third_literal_index = (uint16_t) (context_p->literal_count - 1);
|
|
}
|
|
else
|
|
{
|
|
parser_emit_cbc_literal (context_p,
|
|
CBC_PUSH_LITERAL,
|
|
(uint16_t) (context_p->literal_count - 1));
|
|
}
|
|
|
|
context_p->last_cbc.literal_type = LEXER_REGEXP_LITERAL;
|
|
context_p->last_cbc.literal_object_type = LEXER_LITERAL_OBJECT_ANY;
|
|
break;
|
|
}
|
|
case LEXER_KEYW_THIS:
|
|
{
|
|
#ifndef CONFIG_DISABLE_ES2015_CLASS
|
|
if (PARSER_IS_CLASS_CONSTRUCTOR_SUPER (context_p->status_flags))
|
|
{
|
|
parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_CONSTRUCTOR_THIS);
|
|
}
|
|
else
|
|
{
|
|
#endif /* !CONFIG_DISABLE_ES2015_CLASS */
|
|
parser_emit_cbc (context_p, CBC_PUSH_THIS);
|
|
#ifndef CONFIG_DISABLE_ES2015_CLASS
|
|
}
|
|
#endif /* !CONFIG_DISABLE_ES2015_CLASS */
|
|
break;
|
|
}
|
|
case LEXER_LIT_TRUE:
|
|
{
|
|
parser_emit_cbc (context_p, CBC_PUSH_TRUE);
|
|
break;
|
|
}
|
|
case LEXER_LIT_FALSE:
|
|
{
|
|
parser_emit_cbc (context_p, CBC_PUSH_FALSE);
|
|
break;
|
|
}
|
|
case LEXER_LIT_NULL:
|
|
{
|
|
parser_emit_cbc (context_p, CBC_PUSH_NULL);
|
|
break;
|
|
}
|
|
#ifndef CONFIG_DISABLE_ES2015_CLASS
|
|
case LEXER_KEYW_CLASS:
|
|
{
|
|
parser_parse_class (context_p, false);
|
|
return;
|
|
}
|
|
case LEXER_KEYW_SUPER:
|
|
{
|
|
if ((lexer_check_next_character (context_p, LIT_CHAR_DOT)
|
|
|| lexer_check_next_character (context_p, LIT_CHAR_LEFT_SQUARE))
|
|
&& context_p->status_flags & (PARSER_CLASS_HAS_SUPER | PARSER_IS_ARROW_FUNCTION))
|
|
{
|
|
if (!LEXER_IS_BINARY_LVALUE_TOKEN (context_p->stack_top_uint8))
|
|
{
|
|
context_p->status_flags |= PARSER_CLASS_SUPER_PROP_REFERENCE;
|
|
}
|
|
|
|
if (context_p->status_flags & PARSER_CLASS_CONSTRUCTOR)
|
|
{
|
|
parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_CONSTRUCTOR_SUPER_PROP);
|
|
break;
|
|
}
|
|
|
|
bool is_static = (context_p->status_flags & PARSER_CLASS_STATIC_FUNCTION) != 0;
|
|
parser_emit_cbc_ext (context_p, is_static ? CBC_EXT_PUSH_STATIC_SUPER : CBC_EXT_PUSH_SUPER);
|
|
break;
|
|
}
|
|
|
|
if (lexer_check_next_character (context_p, LIT_CHAR_LEFT_PAREN)
|
|
&& (context_p->status_flags & PARSER_CLASS_HAS_SUPER)
|
|
&& (context_p->status_flags & (PARSER_IS_ARROW_FUNCTION | PARSER_CLASS_CONSTRUCTOR)))
|
|
{
|
|
parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_CONSTRUCTOR_SUPER);
|
|
break;
|
|
}
|
|
|
|
parser_raise_error (context_p, PARSER_ERR_UNEXPECTED_SUPER_REFERENCE);
|
|
}
|
|
#endif /* !CONFIG_DISABLE_ES2015_CLASS */
|
|
#ifndef CONFIG_DISABLE_ES2015_ARROW_FUNCTION
|
|
case LEXER_RIGHT_PAREN:
|
|
{
|
|
if (context_p->stack_top_uint8 == LEXER_LEFT_PAREN
|
|
&& lexer_check_arrow (context_p) == LEXER_ARROW)
|
|
{
|
|
parser_parse_function_expression (context_p,
|
|
PARSER_IS_FUNCTION | PARSER_IS_ARROW_FUNCTION | PARSER_ARROW_PARSE_ARGS);
|
|
|
|
(*grouping_level_p)--;
|
|
parser_stack_pop_uint8 (context_p);
|
|
return;
|
|
}
|
|
/* FALLTHRU */
|
|
}
|
|
#endif /* !CONFIG_DISABLE_ES2015_ARROW_FUNCTION */
|
|
default:
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_PRIMARY_EXP_EXPECTED);
|
|
break;
|
|
}
|
|
}
|
|
lexer_next_token (context_p);
|
|
} /* parser_parse_unary_expression */
|
|
|
|
/**
|
|
* Parse the postfix part of unary operators, and
|
|
* generate byte code for the whole expression.
|
|
*/
|
|
static void
|
|
parser_process_unary_expression (parser_context_t *context_p) /**< context */
|
|
{
|
|
/* Parse postfix part of a primary expression. */
|
|
while (true)
|
|
{
|
|
/* Since break would only break the switch, we use
|
|
* continue to continue this loop. Without continue,
|
|
* the code abandons the loop. */
|
|
switch (context_p->token.type)
|
|
{
|
|
case LEXER_DOT:
|
|
{
|
|
parser_push_result (context_p);
|
|
|
|
lexer_expect_identifier (context_p, LEXER_STRING_LITERAL);
|
|
JERRY_ASSERT (context_p->token.type == LEXER_LITERAL
|
|
&& context_p->token.lit_location.type == LEXER_STRING_LITERAL);
|
|
|
|
if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL)
|
|
{
|
|
JERRY_ASSERT (CBC_ARGS_EQ (CBC_PUSH_PROP_LITERAL_LITERAL,
|
|
CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2));
|
|
context_p->last_cbc_opcode = CBC_PUSH_PROP_LITERAL_LITERAL;
|
|
context_p->last_cbc.value = context_p->lit_object.index;
|
|
}
|
|
else if (context_p->last_cbc_opcode == CBC_PUSH_THIS)
|
|
{
|
|
context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE;
|
|
parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_PROP_THIS_LITERAL);
|
|
}
|
|
else
|
|
{
|
|
parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_PROP_LITERAL);
|
|
}
|
|
lexer_next_token (context_p);
|
|
continue;
|
|
}
|
|
|
|
case LEXER_LEFT_SQUARE:
|
|
{
|
|
parser_push_result (context_p);
|
|
|
|
lexer_next_token (context_p);
|
|
parser_parse_expression (context_p, PARSE_EXPR);
|
|
if (context_p->token.type != LEXER_RIGHT_SQUARE)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_RIGHT_SQUARE_EXPECTED);
|
|
}
|
|
lexer_next_token (context_p);
|
|
|
|
if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL)
|
|
{
|
|
context_p->last_cbc_opcode = CBC_PUSH_PROP_LITERAL;
|
|
}
|
|
else if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS)
|
|
{
|
|
context_p->last_cbc_opcode = CBC_PUSH_PROP_LITERAL_LITERAL;
|
|
}
|
|
else if (context_p->last_cbc_opcode == CBC_PUSH_THIS_LITERAL)
|
|
{
|
|
context_p->last_cbc_opcode = CBC_PUSH_PROP_THIS_LITERAL;
|
|
}
|
|
else
|
|
{
|
|
parser_emit_cbc (context_p, CBC_PUSH_PROP);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
case LEXER_LEFT_PAREN:
|
|
{
|
|
size_t call_arguments = 0;
|
|
uint16_t opcode = CBC_CALL;
|
|
bool is_eval = false;
|
|
|
|
parser_push_result (context_p);
|
|
|
|
if (context_p->stack_top_uint8 == LEXER_KEYW_NEW)
|
|
{
|
|
parser_stack_pop_uint8 (context_p);
|
|
opcode = CBC_NEW;
|
|
}
|
|
else
|
|
{
|
|
if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL
|
|
&& context_p->last_cbc.literal_object_type == LEXER_LITERAL_OBJECT_EVAL)
|
|
{
|
|
JERRY_ASSERT (context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL);
|
|
context_p->status_flags |= PARSER_ARGUMENTS_NEEDED | PARSER_LEXICAL_ENV_NEEDED | PARSER_NO_REG_STORE;
|
|
is_eval = true;
|
|
}
|
|
|
|
if (context_p->last_cbc_opcode == CBC_PUSH_PROP)
|
|
{
|
|
context_p->last_cbc_opcode = CBC_PUSH_PROP_REFERENCE;
|
|
opcode = CBC_CALL_PROP;
|
|
}
|
|
else if (context_p->last_cbc_opcode == CBC_PUSH_PROP_LITERAL)
|
|
{
|
|
context_p->last_cbc_opcode = CBC_PUSH_PROP_LITERAL_REFERENCE;
|
|
opcode = CBC_CALL_PROP;
|
|
}
|
|
else if (context_p->last_cbc_opcode == CBC_PUSH_PROP_LITERAL_LITERAL)
|
|
{
|
|
context_p->last_cbc_opcode = CBC_PUSH_PROP_LITERAL_LITERAL_REFERENCE;
|
|
opcode = CBC_CALL_PROP;
|
|
}
|
|
else if (context_p->last_cbc_opcode == CBC_PUSH_PROP_THIS_LITERAL)
|
|
{
|
|
context_p->last_cbc_opcode = CBC_PUSH_PROP_THIS_LITERAL_REFERENCE;
|
|
opcode = CBC_CALL_PROP;
|
|
}
|
|
#ifndef CONFIG_DISABLE_ES2015_CLASS
|
|
else if (context_p->last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_CONSTRUCTOR_SUPER))
|
|
{
|
|
opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_SUPER_CALL);
|
|
}
|
|
#endif /* !CONFIG_DISABLE_ES2015_CLASS */
|
|
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)
|
|
{
|
|
opcode = CBC_CALL_PROP;
|
|
|
|
if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL)
|
|
{
|
|
context_p->last_cbc_opcode = CBC_PUSH_IDENT_REFERENCE;
|
|
}
|
|
else if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS)
|
|
{
|
|
context_p->last_cbc_opcode = CBC_PUSH_LITERAL;
|
|
parser_emit_cbc_literal (context_p,
|
|
CBC_PUSH_IDENT_REFERENCE,
|
|
context_p->last_cbc.value);
|
|
}
|
|
else
|
|
{
|
|
JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_THREE_LITERALS);
|
|
|
|
context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS;
|
|
parser_emit_cbc_literal (context_p,
|
|
CBC_PUSH_IDENT_REFERENCE,
|
|
context_p->last_cbc.third_literal_index);
|
|
}
|
|
|
|
parser_emit_cbc_ext (context_p, CBC_EXT_RESOLVE_BASE);
|
|
}
|
|
}
|
|
|
|
lexer_next_token (context_p);
|
|
|
|
if (context_p->token.type != LEXER_RIGHT_PAREN)
|
|
{
|
|
while (true)
|
|
{
|
|
if (++call_arguments > CBC_MAXIMUM_BYTE_VALUE)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_ARGUMENT_LIMIT_REACHED);
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
lexer_next_token (context_p);
|
|
|
|
if (is_eval)
|
|
{
|
|
#ifndef CONFIG_DISABLE_ES2015_CLASS
|
|
if (context_p->status_flags & PARSER_CLASS_HAS_SUPER)
|
|
{
|
|
parser_flush_cbc (context_p);
|
|
context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_CLASS_EVAL);
|
|
context_p->last_cbc.value = PARSER_GET_CLASS_ECMA_PARSE_OPTS (context_p->status_flags);
|
|
}
|
|
else
|
|
{
|
|
#endif /* !CONFIG_DISABLE_ES2015_CLASS */
|
|
parser_emit_cbc (context_p, CBC_EVAL);
|
|
#ifndef CONFIG_DISABLE_ES2015_CLASS
|
|
}
|
|
#endif /* !CONFIG_DISABLE_ES2015_CLASS */
|
|
}
|
|
|
|
#ifndef CONFIG_DISABLE_ES2015_CLASS
|
|
if ((context_p->status_flags & PARSER_CLASS_SUPER_PROP_REFERENCE) && opcode == CBC_CALL_PROP)
|
|
{
|
|
parser_emit_cbc_ext (context_p, CBC_EXT_SUPER_PROP_CALL);
|
|
context_p->status_flags &= (uint32_t) ~PARSER_CLASS_SUPER_PROP_REFERENCE;
|
|
}
|
|
#endif /* !CONFIG_DISABLE_ES2015_CLASS */
|
|
|
|
if (call_arguments == 0)
|
|
{
|
|
if (opcode == CBC_CALL)
|
|
{
|
|
parser_emit_cbc (context_p, CBC_CALL0);
|
|
continue;
|
|
}
|
|
if (opcode == CBC_CALL_PROP)
|
|
{
|
|
parser_emit_cbc (context_p, CBC_CALL0_PROP);
|
|
continue;
|
|
}
|
|
if (opcode == CBC_NEW)
|
|
{
|
|
parser_emit_cbc (context_p, CBC_NEW0);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (call_arguments == 1)
|
|
{
|
|
if (opcode == CBC_CALL)
|
|
{
|
|
parser_emit_cbc (context_p, CBC_CALL1);
|
|
continue;
|
|
}
|
|
if (opcode == CBC_CALL_PROP)
|
|
{
|
|
parser_emit_cbc (context_p, CBC_CALL1_PROP);
|
|
continue;
|
|
}
|
|
if (opcode == CBC_NEW)
|
|
{
|
|
parser_emit_cbc (context_p, CBC_NEW1);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (call_arguments == 2)
|
|
{
|
|
if (opcode == CBC_CALL)
|
|
{
|
|
parser_emit_cbc (context_p, CBC_CALL2);
|
|
continue;
|
|
}
|
|
if (opcode == CBC_CALL_PROP)
|
|
{
|
|
parser_flush_cbc (context_p);
|
|
/* Manually adjusting stack usage. */
|
|
JERRY_ASSERT (context_p->stack_depth > 0);
|
|
context_p->stack_depth--;
|
|
parser_emit_cbc (context_p, CBC_CALL2_PROP);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
parser_emit_cbc_call (context_p, opcode, call_arguments);
|
|
continue;
|
|
}
|
|
|
|
default:
|
|
{
|
|
if (context_p->stack_top_uint8 == LEXER_KEYW_NEW)
|
|
{
|
|
parser_push_result (context_p);
|
|
parser_emit_cbc (context_p, CBC_NEW0);
|
|
parser_stack_pop_uint8 (context_p);
|
|
continue;
|
|
}
|
|
|
|
if (!(context_p->token.flags & LEXER_WAS_NEWLINE)
|
|
&& (context_p->token.type == LEXER_INCREASE || context_p->token.type == LEXER_DECREASE))
|
|
{
|
|
cbc_opcode_t opcode = (context_p->token.type == LEXER_INCREASE) ? CBC_POST_INCR : CBC_POST_DECR;
|
|
parser_push_result (context_p);
|
|
parser_emit_unary_lvalue_opcode (context_p, opcode);
|
|
lexer_next_token (context_p);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* Generate byte code for the unary operators. */
|
|
while (true)
|
|
{
|
|
uint8_t token = context_p->stack_top_uint8;
|
|
if (!LEXER_IS_UNARY_OP_TOKEN (token))
|
|
{
|
|
break;
|
|
}
|
|
|
|
parser_push_result (context_p);
|
|
parser_stack_pop_uint8 (context_p);
|
|
|
|
if (LEXER_IS_UNARY_LVALUE_OP_TOKEN (token))
|
|
{
|
|
if (token == LEXER_KEYW_DELETE)
|
|
{
|
|
token = CBC_DELETE_PUSH_RESULT;
|
|
}
|
|
else
|
|
{
|
|
token = (uint8_t) (LEXER_UNARY_LVALUE_OP_TOKEN_TO_OPCODE (token));
|
|
}
|
|
parser_emit_unary_lvalue_opcode (context_p, (cbc_opcode_t) token);
|
|
}
|
|
else
|
|
{
|
|
token = (uint8_t) (LEXER_UNARY_OP_TOKEN_TO_OPCODE (token));
|
|
|
|
if (token == CBC_TYPEOF)
|
|
{
|
|
if (PARSER_IS_PUSH_LITERAL (context_p->last_cbc_opcode)
|
|
&& context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL)
|
|
{
|
|
if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL)
|
|
{
|
|
context_p->last_cbc_opcode = CBC_TYPEOF_IDENT;
|
|
}
|
|
else if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS)
|
|
{
|
|
context_p->last_cbc_opcode = CBC_PUSH_LITERAL;
|
|
parser_emit_cbc_literal (context_p,
|
|
CBC_TYPEOF_IDENT,
|
|
context_p->last_cbc.value);
|
|
}
|
|
else
|
|
{
|
|
JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_THREE_LITERALS);
|
|
|
|
context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS;
|
|
parser_emit_cbc_literal (context_p,
|
|
CBC_TYPEOF_IDENT,
|
|
context_p->last_cbc.third_literal_index);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
parser_emit_cbc (context_p, token);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL)
|
|
{
|
|
/* It is not worth to combine with push multiple literals
|
|
* since the byte code size will not decrease. */
|
|
JERRY_ASSERT (CBC_SAME_ARGS (context_p->last_cbc_opcode, token + 1));
|
|
context_p->last_cbc_opcode = (uint16_t) (token + 1);
|
|
}
|
|
else
|
|
{
|
|
parser_emit_cbc (context_p, token);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} /* parser_process_unary_expression */
|
|
|
|
/**
|
|
* Append a binary token.
|
|
*/
|
|
static void
|
|
parser_append_binary_token (parser_context_t *context_p) /**< context */
|
|
{
|
|
JERRY_ASSERT (LEXER_IS_BINARY_OP_TOKEN (context_p->token.type));
|
|
|
|
parser_push_result (context_p);
|
|
|
|
if (context_p->token.type == LEXER_ASSIGN)
|
|
{
|
|
/* Unlike other tokens, the whole byte code is saved for binary
|
|
* assignment, since it has multiple forms depending on the
|
|
* previous instruction. */
|
|
|
|
if (PARSER_IS_PUSH_LITERAL (context_p->last_cbc_opcode)
|
|
&& context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL)
|
|
{
|
|
JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_LITERAL, CBC_ASSIGN_SET_IDENT));
|
|
|
|
if ((context_p->status_flags & PARSER_IS_STRICT)
|
|
&& context_p->last_cbc.literal_object_type != LEXER_LITERAL_OBJECT_ANY)
|
|
{
|
|
parser_error_t error;
|
|
|
|
if (context_p->last_cbc.literal_object_type == LEXER_LITERAL_OBJECT_EVAL)
|
|
{
|
|
error = PARSER_ERR_EVAL_CANNOT_ASSIGNED;
|
|
}
|
|
else
|
|
{
|
|
JERRY_ASSERT (context_p->last_cbc.literal_object_type == LEXER_LITERAL_OBJECT_ARGUMENTS);
|
|
error = PARSER_ERR_ARGUMENTS_CANNOT_ASSIGNED;
|
|
}
|
|
parser_raise_error (context_p, error);
|
|
}
|
|
|
|
if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL)
|
|
{
|
|
parser_stack_push_uint16 (context_p, context_p->last_cbc.literal_index);
|
|
context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE;
|
|
}
|
|
else if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS)
|
|
{
|
|
parser_stack_push_uint16 (context_p, context_p->last_cbc.value);
|
|
context_p->last_cbc_opcode = CBC_PUSH_LITERAL;
|
|
}
|
|
else
|
|
{
|
|
JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_THREE_LITERALS);
|
|
|
|
parser_stack_push_uint16 (context_p, context_p->last_cbc.third_literal_index);
|
|
context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS;
|
|
}
|
|
parser_stack_push_uint8 (context_p, CBC_ASSIGN_SET_IDENT);
|
|
}
|
|
else if (context_p->last_cbc_opcode == CBC_PUSH_PROP)
|
|
{
|
|
JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_PROP, CBC_ASSIGN));
|
|
parser_stack_push_uint8 (context_p, CBC_ASSIGN);
|
|
context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE;
|
|
}
|
|
else if (context_p->last_cbc_opcode == CBC_PUSH_PROP_LITERAL)
|
|
{
|
|
if (context_p->last_cbc.literal_type != LEXER_IDENT_LITERAL)
|
|
{
|
|
JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_PROP_LITERAL, CBC_ASSIGN_PROP_LITERAL));
|
|
parser_stack_push_uint16 (context_p, context_p->last_cbc.literal_index);
|
|
parser_stack_push_uint8 (context_p, CBC_ASSIGN_PROP_LITERAL);
|
|
context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE;
|
|
#ifndef CONFIG_DISABLE_ES2015_CLASS
|
|
if (context_p->status_flags & PARSER_CLASS_SUPER_PROP_REFERENCE)
|
|
{
|
|
parser_emit_cbc_ext (context_p, CBC_EXT_SUPER_PROP_ASSIGN);
|
|
parser_flush_cbc (context_p);
|
|
}
|
|
context_p->status_flags &= (uint32_t) ~PARSER_CLASS_SUPER_PROP_REFERENCE;
|
|
#endif /* !CONFIG_DISABLE_ES2015_CLASS */
|
|
}
|
|
else
|
|
{
|
|
context_p->last_cbc_opcode = CBC_PUSH_LITERAL;
|
|
parser_stack_push_uint8 (context_p, CBC_ASSIGN);
|
|
}
|
|
}
|
|
else if (context_p->last_cbc_opcode == CBC_PUSH_PROP_LITERAL_LITERAL)
|
|
{
|
|
JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_PROP_LITERAL_LITERAL, CBC_PUSH_TWO_LITERALS));
|
|
context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS;
|
|
parser_stack_push_uint8 (context_p, CBC_ASSIGN);
|
|
}
|
|
else if (context_p->last_cbc_opcode == CBC_PUSH_PROP_THIS_LITERAL)
|
|
{
|
|
if (context_p->last_cbc.literal_type != LEXER_IDENT_LITERAL)
|
|
{
|
|
JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_PROP_THIS_LITERAL, CBC_ASSIGN_PROP_THIS_LITERAL));
|
|
parser_stack_push_uint16 (context_p, context_p->last_cbc.literal_index);
|
|
parser_stack_push_uint8 (context_p, CBC_ASSIGN_PROP_THIS_LITERAL);
|
|
context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE;
|
|
}
|
|
else
|
|
{
|
|
context_p->last_cbc_opcode = CBC_PUSH_THIS_LITERAL;
|
|
parser_stack_push_uint8 (context_p, CBC_ASSIGN);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Invalid LeftHandSide expression. */
|
|
parser_emit_cbc_ext (context_p, CBC_EXT_THROW_REFERENCE_ERROR);
|
|
parser_stack_push_uint8 (context_p, CBC_ASSIGN);
|
|
}
|
|
}
|
|
else if (LEXER_IS_BINARY_LVALUE_TOKEN (context_p->token.type))
|
|
{
|
|
if (PARSER_IS_PUSH_LITERAL (context_p->last_cbc_opcode)
|
|
&& context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL)
|
|
{
|
|
if ((context_p->status_flags & PARSER_IS_STRICT)
|
|
&& context_p->last_cbc.literal_object_type != LEXER_LITERAL_OBJECT_ANY)
|
|
{
|
|
parser_error_t error;
|
|
|
|
if (context_p->last_cbc.literal_object_type == LEXER_LITERAL_OBJECT_EVAL)
|
|
{
|
|
error = PARSER_ERR_EVAL_CANNOT_ASSIGNED;
|
|
}
|
|
else
|
|
{
|
|
JERRY_ASSERT (context_p->last_cbc.literal_object_type == LEXER_LITERAL_OBJECT_ARGUMENTS);
|
|
error = PARSER_ERR_ARGUMENTS_CANNOT_ASSIGNED;
|
|
}
|
|
parser_raise_error (context_p, error);
|
|
}
|
|
|
|
if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL)
|
|
{
|
|
context_p->last_cbc_opcode = CBC_PUSH_IDENT_REFERENCE;
|
|
}
|
|
else if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS)
|
|
{
|
|
context_p->last_cbc_opcode = CBC_PUSH_LITERAL;
|
|
parser_emit_cbc_literal (context_p,
|
|
CBC_PUSH_IDENT_REFERENCE,
|
|
context_p->last_cbc.value);
|
|
}
|
|
else
|
|
{
|
|
JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_THREE_LITERALS);
|
|
|
|
context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS;
|
|
parser_emit_cbc_literal (context_p,
|
|
CBC_PUSH_IDENT_REFERENCE,
|
|
context_p->last_cbc.third_literal_index);
|
|
}
|
|
}
|
|
else if (context_p->last_cbc_opcode == CBC_PUSH_PROP)
|
|
{
|
|
JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_PROP, CBC_PUSH_PROP_REFERENCE));
|
|
context_p->last_cbc_opcode = CBC_PUSH_PROP_REFERENCE;
|
|
}
|
|
else if (context_p->last_cbc_opcode == CBC_PUSH_PROP_LITERAL)
|
|
{
|
|
JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_PROP_LITERAL,
|
|
CBC_PUSH_PROP_LITERAL_REFERENCE));
|
|
context_p->last_cbc_opcode = CBC_PUSH_PROP_LITERAL_REFERENCE;
|
|
}
|
|
else if (context_p->last_cbc_opcode == CBC_PUSH_PROP_LITERAL_LITERAL)
|
|
{
|
|
JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_PROP_LITERAL_LITERAL,
|
|
CBC_PUSH_PROP_LITERAL_LITERAL_REFERENCE));
|
|
context_p->last_cbc_opcode = CBC_PUSH_PROP_LITERAL_LITERAL_REFERENCE;
|
|
}
|
|
else if (context_p->last_cbc_opcode == CBC_PUSH_PROP_THIS_LITERAL)
|
|
{
|
|
JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_PROP_THIS_LITERAL,
|
|
CBC_PUSH_PROP_THIS_LITERAL_REFERENCE));
|
|
context_p->last_cbc_opcode = CBC_PUSH_PROP_THIS_LITERAL_REFERENCE;
|
|
}
|
|
else
|
|
{
|
|
/* Invalid LeftHandSide expression. */
|
|
parser_emit_cbc_ext (context_p, CBC_EXT_THROW_REFERENCE_ERROR);
|
|
parser_emit_cbc (context_p, CBC_PUSH_PROP_REFERENCE);
|
|
}
|
|
}
|
|
else if (context_p->token.type == LEXER_LOGICAL_OR
|
|
|| context_p->token.type == LEXER_LOGICAL_AND)
|
|
{
|
|
parser_branch_t branch;
|
|
uint16_t opcode = CBC_BRANCH_IF_LOGICAL_TRUE;
|
|
|
|
if (context_p->token.type == LEXER_LOGICAL_AND)
|
|
{
|
|
opcode = CBC_BRANCH_IF_LOGICAL_FALSE;
|
|
}
|
|
|
|
parser_emit_cbc_forward_branch (context_p, opcode, &branch);
|
|
parser_stack_push (context_p, &branch, sizeof (parser_branch_t));
|
|
}
|
|
|
|
parser_stack_push_uint8 (context_p, context_p->token.type);
|
|
} /* parser_append_binary_token */
|
|
|
|
/**
|
|
* Emit opcode for binary computations.
|
|
*/
|
|
static void
|
|
parser_process_binary_opcodes (parser_context_t *context_p, /**< context */
|
|
uint8_t min_prec_treshold) /**< minimal precedence of tokens */
|
|
{
|
|
while (true)
|
|
{
|
|
uint8_t token = context_p->stack_top_uint8;
|
|
cbc_opcode_t opcode;
|
|
|
|
/* For left-to-right operators (all binary operators except assignment
|
|
* and logical operators), the byte code is flushed if the precedence
|
|
* of the next operator is less or equal than the current operator. For
|
|
* assignment and logical operators, we add 1 to the min precendence to
|
|
* force right-to-left evaluation order. */
|
|
|
|
if (!LEXER_IS_BINARY_OP_TOKEN (token)
|
|
|| parser_binary_precedence_table[token - LEXER_FIRST_BINARY_OP] < min_prec_treshold)
|
|
{
|
|
return;
|
|
}
|
|
|
|
parser_push_result (context_p);
|
|
parser_stack_pop_uint8 (context_p);
|
|
|
|
if (token == LEXER_ASSIGN)
|
|
{
|
|
opcode = (cbc_opcode_t) context_p->stack_top_uint8;
|
|
parser_stack_pop_uint8 (context_p);
|
|
|
|
if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL
|
|
&& opcode == CBC_ASSIGN_SET_IDENT)
|
|
{
|
|
JERRY_ASSERT (CBC_ARGS_EQ (CBC_ASSIGN_LITERAL_SET_IDENT,
|
|
CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2));
|
|
context_p->last_cbc.value = parser_stack_pop_uint16 (context_p);
|
|
context_p->last_cbc_opcode = CBC_ASSIGN_LITERAL_SET_IDENT;
|
|
continue;
|
|
}
|
|
|
|
if (cbc_flags[opcode] & CBC_HAS_LITERAL_ARG)
|
|
{
|
|
uint16_t index = parser_stack_pop_uint16 (context_p);
|
|
parser_emit_cbc_literal (context_p, (uint16_t) opcode, index);
|
|
|
|
if (opcode == CBC_ASSIGN_PROP_THIS_LITERAL
|
|
&& (context_p->stack_depth >= context_p->stack_limit))
|
|
{
|
|
/* Stack limit is increased for VM_OC_ASSIGN_PROP_THIS. Needed by vm.c. */
|
|
JERRY_ASSERT (context_p->stack_depth == context_p->stack_limit);
|
|
|
|
context_p->stack_limit++;
|
|
|
|
if (context_p->stack_limit > PARSER_MAXIMUM_STACK_LIMIT)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_STACK_LIMIT_REACHED);
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
else if (LEXER_IS_BINARY_LVALUE_TOKEN (token))
|
|
{
|
|
parser_stack_push_uint8 (context_p, CBC_ASSIGN);
|
|
parser_stack_push_uint8 (context_p, LEXER_ASSIGN);
|
|
parser_stack_push_uint8 (context_p, lexer_convert_binary_lvalue_token_to_binary (token));
|
|
continue;
|
|
}
|
|
else if (token == LEXER_LOGICAL_OR || token == LEXER_LOGICAL_AND)
|
|
{
|
|
parser_branch_t branch;
|
|
parser_stack_pop (context_p, &branch, sizeof (parser_branch_t));
|
|
parser_set_branch_to_current_position (context_p, &branch);
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
opcode = LEXER_BINARY_OP_TOKEN_TO_OPCODE (token);
|
|
|
|
if (PARSER_IS_PUSH_NUMBER (context_p->last_cbc_opcode))
|
|
{
|
|
lexer_convert_push_number_to_push_literal (context_p);
|
|
}
|
|
|
|
if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL)
|
|
{
|
|
JERRY_ASSERT (CBC_SAME_ARGS (context_p->last_cbc_opcode, opcode + CBC_BINARY_WITH_LITERAL));
|
|
context_p->last_cbc_opcode = (uint16_t) (opcode + CBC_BINARY_WITH_LITERAL);
|
|
continue;
|
|
}
|
|
else if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS)
|
|
{
|
|
JERRY_ASSERT (CBC_ARGS_EQ (opcode + CBC_BINARY_WITH_TWO_LITERALS,
|
|
CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2));
|
|
context_p->last_cbc_opcode = (uint16_t) (opcode + CBC_BINARY_WITH_TWO_LITERALS);
|
|
continue;
|
|
}
|
|
}
|
|
parser_emit_cbc (context_p, (uint16_t) opcode);
|
|
}
|
|
} /* parser_process_binary_opcodes */
|
|
|
|
/**
|
|
* Parse expression.
|
|
*/
|
|
void
|
|
parser_parse_expression (parser_context_t *context_p, /**< context */
|
|
int options) /**< option flags */
|
|
{
|
|
size_t grouping_level = 0;
|
|
|
|
parser_stack_push_uint8 (context_p, LEXER_EXPRESSION_START);
|
|
|
|
while (true)
|
|
{
|
|
if (options & PARSE_EXPR_HAS_LITERAL)
|
|
{
|
|
JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_LITERAL);
|
|
/* True only for the first expression. */
|
|
options &= ~PARSE_EXPR_HAS_LITERAL;
|
|
}
|
|
else
|
|
{
|
|
parser_parse_unary_expression (context_p, &grouping_level);
|
|
}
|
|
|
|
while (true)
|
|
{
|
|
parser_process_unary_expression (context_p);
|
|
|
|
/* The engine flush binary opcodes above this precedence. */
|
|
uint8_t min_prec_treshold = CBC_MAXIMUM_BYTE_VALUE;
|
|
|
|
if (LEXER_IS_BINARY_OP_TOKEN (context_p->token.type))
|
|
{
|
|
min_prec_treshold = parser_binary_precedence_table[context_p->token.type - LEXER_FIRST_BINARY_OP];
|
|
if (LEXER_IS_BINARY_LVALUE_TOKEN (context_p->token.type)
|
|
|| context_p->token.type == LEXER_LOGICAL_OR
|
|
|| context_p->token.type == LEXER_LOGICAL_AND)
|
|
{
|
|
/* Right-to-left evaluation order. */
|
|
min_prec_treshold++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
min_prec_treshold = 0;
|
|
}
|
|
|
|
parser_process_binary_opcodes (context_p, min_prec_treshold);
|
|
|
|
if (context_p->token.type == LEXER_RIGHT_PAREN)
|
|
{
|
|
if (context_p->stack_top_uint8 == LEXER_LEFT_PAREN
|
|
|| context_p->stack_top_uint8 == LEXER_COMMA_SEP_LIST)
|
|
{
|
|
JERRY_ASSERT (grouping_level > 0);
|
|
grouping_level--;
|
|
|
|
if (context_p->stack_top_uint8 == LEXER_COMMA_SEP_LIST)
|
|
{
|
|
parser_push_result (context_p);
|
|
parser_flush_cbc (context_p);
|
|
}
|
|
|
|
parser_stack_pop_uint8 (context_p);
|
|
lexer_next_token (context_p);
|
|
continue;
|
|
}
|
|
}
|
|
else if (context_p->token.type == LEXER_QUESTION_MARK)
|
|
{
|
|
cbc_opcode_t opcode = CBC_BRANCH_IF_FALSE_FORWARD;
|
|
parser_branch_t cond_branch;
|
|
parser_branch_t uncond_branch;
|
|
|
|
parser_push_result (context_p);
|
|
|
|
if (context_p->last_cbc_opcode == CBC_LOGICAL_NOT)
|
|
{
|
|
context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE;
|
|
opcode = CBC_BRANCH_IF_TRUE_FORWARD;
|
|
}
|
|
|
|
parser_emit_cbc_forward_branch (context_p, (uint16_t) opcode, &cond_branch);
|
|
|
|
lexer_next_token (context_p);
|
|
parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA);
|
|
parser_emit_cbc_forward_branch (context_p, CBC_JUMP_FORWARD, &uncond_branch);
|
|
parser_set_branch_to_current_position (context_p, &cond_branch);
|
|
|
|
/* Although byte code is constructed for two branches,
|
|
* only one of them will be executed. To reflect this
|
|
* the stack is manually adjusted. */
|
|
JERRY_ASSERT (context_p->stack_depth > 0);
|
|
context_p->stack_depth--;
|
|
|
|
if (context_p->token.type != LEXER_COLON)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_COLON_FOR_CONDITIONAL_EXPECTED);
|
|
}
|
|
|
|
lexer_next_token (context_p);
|
|
|
|
parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA);
|
|
parser_set_branch_to_current_position (context_p, &uncond_branch);
|
|
|
|
/* Last opcode rewrite is not allowed because
|
|
* the result may come from the first branch. */
|
|
parser_flush_cbc (context_p);
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (context_p->token.type == LEXER_COMMA)
|
|
{
|
|
if (!(options & PARSE_EXPR_NO_COMMA) || grouping_level > 0)
|
|
{
|
|
if (!CBC_NO_RESULT_OPERATION (context_p->last_cbc_opcode))
|
|
{
|
|
parser_emit_cbc (context_p, CBC_POP);
|
|
}
|
|
if (context_p->stack_top_uint8 == LEXER_LEFT_PAREN)
|
|
{
|
|
parser_mem_page_t *page_p = context_p->stack.first_p;
|
|
|
|
JERRY_ASSERT (page_p != NULL);
|
|
|
|
page_p->bytes[context_p->stack.last_position - 1] = LEXER_COMMA_SEP_LIST;
|
|
context_p->stack_top_uint8 = LEXER_COMMA_SEP_LIST;
|
|
}
|
|
lexer_next_token (context_p);
|
|
continue;
|
|
}
|
|
}
|
|
else if (LEXER_IS_BINARY_OP_TOKEN (context_p->token.type))
|
|
{
|
|
parser_append_binary_token (context_p);
|
|
lexer_next_token (context_p);
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (grouping_level != 0)
|
|
{
|
|
parser_raise_error (context_p, PARSER_ERR_RIGHT_PAREN_EXPECTED);
|
|
}
|
|
|
|
JERRY_ASSERT (context_p->stack_top_uint8 == LEXER_EXPRESSION_START);
|
|
parser_stack_pop_uint8 (context_p);
|
|
|
|
if (options & PARSE_EXPR_STATEMENT)
|
|
{
|
|
if (!CBC_NO_RESULT_OPERATION (context_p->last_cbc_opcode))
|
|
{
|
|
parser_emit_cbc (context_p, CBC_POP);
|
|
}
|
|
}
|
|
else if (options & PARSE_EXPR_BLOCK)
|
|
{
|
|
if (CBC_NO_RESULT_OPERATION (context_p->last_cbc_opcode))
|
|
{
|
|
JERRY_ASSERT (CBC_SAME_ARGS (context_p->last_cbc_opcode, context_p->last_cbc_opcode + 2));
|
|
PARSER_PLUS_EQUAL_U16 (context_p->last_cbc_opcode, 2);
|
|
parser_flush_cbc (context_p);
|
|
}
|
|
else
|
|
{
|
|
parser_emit_cbc (context_p, CBC_POP_BLOCK);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
parser_push_result (context_p);
|
|
}
|
|
} /* parser_parse_expression */
|
|
|
|
/**
|
|
* @}
|
|
* @}
|
|
* @}
|
|
*/
|
|
|
|
#endif /* !JERRY_DISABLE_JS_PARSER */
|