mirror of
https://github.com/jerryscript-project/jerryscript.git
synced 2025-12-15 16:29:21 +00:00
- parser is now non-recursive (i.e. parse function is not called recursively in any case);
- byte-code is now more compact:
- constants are now not immediately dumped upon occurence, but later - where necessary;
- assignments are combined with unary / binary operations;
- binary operations are encoded more compactly in many cases;
- byte-code arrays are now allocated separately for each scope (so, GC of the scopes now becomes possible);
- byte-code is dumped directly into corresponding byte-code arrays:
- linked lists of op_meta are not now used for main code of a scope.
JerryScript-DCO-1.0-Signed-off-by: Ruben Ayrapetyan r.ayrapetyan@samsung.com
JerryScript-DCO-1.0-Signed-off-by: Andrey Shitov a.shitov@samsung.com
5691 lines
180 KiB
C++
5691 lines
180 KiB
C++
/* Copyright 2014-2015 Samsung Electronics Co., Ltd.
|
|
* Copyright 2015 University of Szeged.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* 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 "bytecode-data.h"
|
|
#include "ecma-helpers.h"
|
|
#include "jrt-libc-includes.h"
|
|
#include "jsp-mm.h"
|
|
#include "opcodes.h"
|
|
#include "opcodes-dumper.h"
|
|
#include "parser.h"
|
|
#include "re-parser.h"
|
|
#include "scopes-tree.h"
|
|
#include "stack.h"
|
|
#include "jsp-early-error.h"
|
|
#include "jsp-internal.h"
|
|
#include "vm.h"
|
|
|
|
/**
|
|
* Flag, indicating whether result of expression
|
|
* evaluation should be stored to 'eval result'
|
|
* temporary variable.
|
|
*
|
|
* In other words, the flag indicates whether
|
|
* 'eval result' store code should be dumped.
|
|
*
|
|
* See also:
|
|
* parse_expression
|
|
*/
|
|
typedef enum
|
|
{
|
|
JSP_EVAL_RET_STORE_NOT_DUMP, /**< do not dump */
|
|
JSP_EVAL_RET_STORE_DUMP, /**< dump */
|
|
} jsp_eval_ret_store_t;
|
|
|
|
static token tok;
|
|
static bool parser_show_instrs = false;
|
|
|
|
#define EMIT_ERROR(type, MESSAGE) PARSE_ERROR(type, MESSAGE, tok.loc)
|
|
#define EMIT_ERROR_VARG(type, MESSAGE, ...) PARSE_ERROR_VARG(type, MESSAGE, tok.loc, __VA_ARGS__)
|
|
|
|
/**
|
|
* Parse procedure
|
|
*
|
|
* Parse of any Program production (ECMA-262 v5, 14) is performed in two stages:
|
|
* - preparse stage:
|
|
* - build tree of scopes (global / eval as root and functions as other nodes)
|
|
* - gather necessary properties for each scope:
|
|
* - maximum number of instructions
|
|
* - maximum number of per-opcode-block unique literals
|
|
* - flags, indicating whether scope contains `with`, `try`, etc.
|
|
* - perform syntax checks
|
|
* - dump stage:
|
|
* - dump byte-code instructions
|
|
* - fill idx-to-literal hash table
|
|
* - perform byte-code optimizations
|
|
*
|
|
* Parser context
|
|
* See also:
|
|
* jsp_ctx_t
|
|
*
|
|
* General description
|
|
* Each parse stage is performed using jsp_parse_source_element_list function with common general flow,
|
|
* and local branching for actions, specific for particular stage.
|
|
*
|
|
* Parse of each element is performed in a specific parser state (see also, jsp_state_expr_t and jsp_state_t)
|
|
* that determines productions expected by parser in the state.
|
|
*
|
|
* States are located on managed (non-native) parser stack, so the parser is recursive.
|
|
*
|
|
* Expression parse flow
|
|
* Whenever parser expects an expression, it pushes new state that is initially either JSP_STATE_EXPR_EMPTY or a
|
|
* particular fixed helper state.
|
|
*
|
|
* The new state also contains required expression type, indicating what type of expression is currently expected.
|
|
* For states starting with JSP_STATE_EXPR_EMPTY, it is the expression type that indicates final production,
|
|
* that is expected to be parsed in the pushed state (AssignmentExpression, LeftHandSideExpression, etc.).
|
|
* In case of fixed helper states required type is the same as starting expression type.
|
|
*
|
|
* In each state parser checks current production for whether it can be extended in some way, acceptable by grammar.
|
|
* If it can be extended, it is extended, possibly with pushing new state for parsing subexpression.
|
|
* Otherwise, it is just promoted to next (higher / more general) expression type to repeat check with rules,
|
|
* corresponding to the next type, or finish parse of current expression.
|
|
* Upon finishing parse, associated with the current expression / helper state, `is_completed` flag of the state is
|
|
* raised.
|
|
*
|
|
* Finish of an expression parse is indicated using `is_subexpr_end` local flag in jsp_parse_source_element_list
|
|
* function. Upon finish, upper state is popped from stack and becomes current, merging the parsed subexpression
|
|
* into its state.
|
|
*
|
|
* Statements parse flow
|
|
* Parse of statements is very similar to parse of expressions.
|
|
*
|
|
* The states are either fixed (like JSP_STATE_STAT_STATEMENT_LIST), or starting from JSP_STATE_STAT_EMPTY
|
|
* with required type equal to JSP_STATE_STAT_STATEMENT.
|
|
*
|
|
* Source elements list
|
|
* Source elements parse state is represented with the single type - JSP_STATE_SOURCE_ELEMENTS.
|
|
*
|
|
* Labelled statements
|
|
* Named labels are pushed as helper states to parser stack and are searched for upon occurence of break or continue
|
|
* on label.
|
|
*
|
|
* Targets of simple break / continue are also searched through stack iteration, looking for iterational or switch
|
|
* statements.
|
|
*/
|
|
typedef enum __attr_packed___
|
|
{
|
|
/* ECMA-262 v5 expression types */
|
|
JSP_STATE_EXPR__BEGIN,
|
|
|
|
JSP_STATE_EXPR_EMPTY, /**< no expression yet (at start) */
|
|
JSP_STATE_EXPR_FUNCTION, /**< FunctionExpression (11.2.5) */
|
|
JSP_STATE_EXPR_MEMBER, /**< MemberExpression (11.2) */
|
|
JSP_STATE_EXPR_CALL, /**< CallExpression (11.2) */
|
|
JSP_STATE_EXPR_LEFTHANDSIDE, /**< LeftHandSideExpression (11.2) */
|
|
|
|
JSP_STATE_EXPR__SIMPLE_BEGIN,
|
|
|
|
JSP_STATE_EXPR_UNARY, /**< UnaryExpression (11.4) */
|
|
JSP_STATE_EXPR_MULTIPLICATIVE, /**< MultiplicativeExpression (11.5) */
|
|
JSP_STATE_EXPR_ADDITIVE, /**< AdditiveExpression (11.6) */
|
|
JSP_STATE_EXPR_SHIFT, /**< ShiftExpression (11.7) */
|
|
JSP_STATE_EXPR_RELATIONAL, /**< RelationalExpression (11.8) */
|
|
JSP_STATE_EXPR_EQUALITY, /**< EqualityExpression (11.9) */
|
|
JSP_STATE_EXPR_BITWISE_AND, /**< BitwiseAndExpression (11.10) */
|
|
JSP_STATE_EXPR_BITWISE_XOR, /**< BitwiseXorExpression (11.10) */
|
|
JSP_STATE_EXPR_BITWISE_OR, /**< BitwiseOrExpression (11.10) */
|
|
|
|
JSP_STATE_EXPR__SIMPLE_END,
|
|
|
|
JSP_STATE_EXPR_LOGICAL_AND, /**< LogicalAndExpression (11.11) */
|
|
JSP_STATE_EXPR_LOGICAL_OR, /**< LogicalOrExpression (11.11) */
|
|
JSP_STATE_EXPR_CONDITION, /**< ConditionalExpression (11.12) */
|
|
JSP_STATE_EXPR_ASSIGNMENT, /**< AssignmentExpression (11.13) */
|
|
JSP_STATE_EXPR_EXPRESSION, /**< Expression (11.14) */
|
|
|
|
JSP_STATE_EXPR_ARRAY_LITERAL, /**< ArrayLiteral (11.1.4) */
|
|
JSP_STATE_EXPR_OBJECT_LITERAL, /**< ObjectLiteral (11.1.5) */
|
|
|
|
JSP_STATE_EXPR_DATA_PROP_DECL, /**< a data property (ObjectLiteral, 11.1.5) */
|
|
JSP_STATE_EXPR_ACCESSOR_PROP_DECL, /**< an accessor's property getter / setter (ObjectLiteral, 11.1.5) */
|
|
|
|
JSP_STATE_EXPR__END,
|
|
|
|
JSP_STATE_STAT_EMPTY, /**< no statement yet (at start) */
|
|
JSP_STATE_STAT_IF_BRANCH_START, /**< IfStatement branch start */
|
|
JSP_STATE_STAT_IF_BRANCH_END, /**< IfStatement branch start */
|
|
JSP_STATE_STAT_STATEMENT, /**< Statement */
|
|
JSP_STATE_STAT_STATEMENT_LIST, /**< Statement list */
|
|
JSP_STATE_STAT_VAR_DECL, /**< VariableStatement */
|
|
JSP_STATE_STAT_VAR_DECL_FINISH,
|
|
JSP_STATE_STAT_DO_WHILE, /**< IterationStatement */
|
|
JSP_STATE_STAT_WHILE,
|
|
JSP_STATE_STAT_FOR_INIT_END,
|
|
JSP_STATE_STAT_FOR_INCREMENT,
|
|
JSP_STATE_STAT_FOR_COND,
|
|
JSP_STATE_STAT_FOR_FINISH,
|
|
JSP_STATE_STAT_FOR_IN,
|
|
JSP_STATE_STAT_FOR_IN_EXPR,
|
|
JSP_STATE_STAT_FOR_IN_FINISH,
|
|
JSP_STATE_STAT_ITER_FINISH,
|
|
JSP_STATE_STAT_SWITCH,
|
|
JSP_STATE_STAT_SWITCH_BRANCH_EXPR,
|
|
JSP_STATE_STAT_SWITCH_BRANCH,
|
|
JSP_STATE_STAT_SWITCH_FINISH,
|
|
JSP_STATE_STAT_TRY,
|
|
JSP_STATE_STAT_CATCH_FINISH,
|
|
JSP_STATE_STAT_FINALLY_FINISH,
|
|
JSP_STATE_STAT_TRY_FINISH,
|
|
JSP_STATE_STAT_WITH,
|
|
JSP_STATE_STAT_EXPRESSION,
|
|
JSP_STATE_STAT_RETURN,
|
|
JSP_STATE_STAT_THROW,
|
|
|
|
JSP_STATE_FUNC_DECL_FINISH,
|
|
|
|
JSP_STATE_SOURCE_ELEMENTS,
|
|
|
|
JSP_STATE_STAT_BLOCK,
|
|
|
|
JSP_STATE_STAT_NAMED_LABEL
|
|
} jsp_state_expr_t;
|
|
|
|
typedef struct jsp_state_t
|
|
{
|
|
mem_cpointer_t prev_state_cp; /** pointer to previous state on the stack, or MEM_CP_NULL - for bottom stack element */
|
|
mem_cpointer_t next_state_cp; /** pointer to next state on the stack, or MEM_CP_NULL - for top stack element */
|
|
jsp_state_expr_t state; /**< current state */
|
|
jsp_state_expr_t req_state; /**< required state */
|
|
|
|
uint8_t is_completed : 1; /**< the expression parse completed,
|
|
* no more tokens can be added to the expression */
|
|
uint8_t is_list_in_process : 1; /**< parsing a list, associated with the expression
|
|
* (details depend on current expression type) */
|
|
uint8_t is_no_in_mode : 1; /**< expression is being parsed in NoIn mode (see also: ECMA-262 v5, 11.8) */
|
|
uint8_t is_fixed_ret_operand : 1; /**< the expression's evaluation should produce value that should be
|
|
* put to register, specified by operand, specified in state */
|
|
uint8_t is_value_based_reference : 1; /**< flag, indicating whether current state represents evaluated expression
|
|
* that evaluated to a value-based reference */
|
|
uint8_t is_get_value_dumped_for_main_operand : 1;
|
|
uint8_t is_get_value_dumped_for_prop_operand : 1;
|
|
uint8_t is_need_retval : 1; /**< flag, indicating whether result of the expression's
|
|
* evaluation, if it is value, is used */
|
|
uint8_t is_complex_production : 1; /**< the expression is being parsed in complex production mode */
|
|
uint8_t var_decl : 1; /**< this flag tells that we are parsing VariableStatement, not
|
|
VariableDeclarationList or VariableDeclaration inside
|
|
IterationStatement */
|
|
uint8_t is_var_decl_no_in : 1; /**< this flag tells that we are parsing VariableDeclrationNoIn inside
|
|
ForIn iteration statement */
|
|
uint8_t was_default : 1; /**< was default branch seen */
|
|
uint8_t is_default_branch : 1; /**< marks default branch of switch statement */
|
|
uint8_t is_simply_jumpable_border : 1; /**< flag, indicating whether simple jump could be performed
|
|
* from current statement outside of the statement */
|
|
uint8_t is_dump_eval_ret_store : 1; /**< expression's result should be stored to eval's return register */
|
|
uint8_t is_stmt_list_control_flow_exit_stmt_occured : 1; /**< flag, indicating whether one of the following statements
|
|
* occured immediately in the statement list, corresponding
|
|
* to the statement:
|
|
* - return
|
|
* - break
|
|
* - continue
|
|
* - throw */
|
|
|
|
union u
|
|
{
|
|
u (void)
|
|
{
|
|
}
|
|
|
|
struct expression
|
|
{
|
|
union u
|
|
{
|
|
struct
|
|
{
|
|
uint32_t list_length;
|
|
vm_instr_counter_t header_pos; /**< position of a varg header instruction */
|
|
vm_idx_t reg_alloc_saved_state1;
|
|
vm_idx_t reg_alloc_saved_state2;
|
|
} varg_sequence;
|
|
JERRY_STATIC_ASSERT (sizeof (varg_sequence) == 8); // Please, update size if changed
|
|
|
|
struct
|
|
{
|
|
jsp_operand_t prop_name;
|
|
bool is_setter;
|
|
} accessor_prop_decl;
|
|
JERRY_STATIC_ASSERT (sizeof (accessor_prop_decl) == 6); // Please, update size if changed
|
|
|
|
struct
|
|
{
|
|
vm_instr_counter_t rewrite_chain; /**< chain of jmp instructions to rewrite */
|
|
} logical_and;
|
|
JERRY_STATIC_ASSERT (sizeof (logical_and) == 2); // Please, update size if changed
|
|
|
|
struct
|
|
{
|
|
vm_instr_counter_t rewrite_chain; /**< chain of jmp instructions to rewrite */
|
|
} logical_or;
|
|
JERRY_STATIC_ASSERT (sizeof (logical_or) == 2); // Please, update size if changed
|
|
|
|
struct
|
|
{
|
|
vm_instr_counter_t conditional_check_pos;
|
|
vm_instr_counter_t jump_to_end_pos;
|
|
} conditional;
|
|
JERRY_STATIC_ASSERT (sizeof (conditional) == 4); // Please, update size if changed
|
|
} u;
|
|
JERRY_STATIC_ASSERT (sizeof (u) == 8); // Please, update size if changed
|
|
|
|
jsp_operand_t operand; /**< operand, associated with expression */
|
|
jsp_operand_t prop_name_operand; /**< operand, describing second part of a value-based reference,
|
|
* or empty operand (for Identifier references, values, or constants) */
|
|
jsp_token_type_t token_type; /**< token, related to current and, if binary, to previous expression */
|
|
} expression;
|
|
JERRY_STATIC_ASSERT (sizeof (expression) == 20); // Please, update size if changed
|
|
|
|
struct statement
|
|
{
|
|
union u
|
|
{
|
|
struct iterational
|
|
{
|
|
union u
|
|
{
|
|
struct loop_for_in
|
|
{
|
|
union u
|
|
{
|
|
locus iterator_expr_loc;
|
|
locus body_loc;
|
|
} u;
|
|
|
|
lit_cpointer_t var_name_lit_cp;
|
|
vm_instr_counter_t header_pos;
|
|
} loop_for_in;
|
|
JERRY_STATIC_ASSERT (sizeof (loop_for_in) == 8); // Please, update size if changed
|
|
|
|
struct loop_while
|
|
{
|
|
union u
|
|
{
|
|
locus cond_expr_start_loc;
|
|
locus end_loc;
|
|
} u;
|
|
|
|
vm_instr_counter_t next_iter_tgt_pos;
|
|
vm_instr_counter_t jump_to_end_pos;
|
|
} loop_while;
|
|
JERRY_STATIC_ASSERT (sizeof (loop_while) == 8); // Please, update size if changed
|
|
|
|
struct loop_do_while
|
|
{
|
|
vm_instr_counter_t next_iter_tgt_pos;
|
|
} loop_do_while;
|
|
JERRY_STATIC_ASSERT (sizeof (loop_do_while) == 2); // Please, update size if changed
|
|
|
|
struct loop_for
|
|
{
|
|
union u1
|
|
{
|
|
locus body_loc;
|
|
locus condition_expr_loc;
|
|
} u1;
|
|
|
|
union u2
|
|
{
|
|
locus increment_expr_loc;
|
|
locus end_loc;
|
|
} u2;
|
|
|
|
vm_instr_counter_t next_iter_tgt_pos;
|
|
vm_instr_counter_t jump_to_end_pos;
|
|
} loop_for;
|
|
JERRY_STATIC_ASSERT (sizeof (loop_for) == 12); // Please, update size if changed
|
|
} u;
|
|
JERRY_STATIC_ASSERT (sizeof (u) == 12); // Please, update size if changed
|
|
|
|
vm_instr_counter_t continues_rewrite_chain;
|
|
vm_instr_counter_t continue_tgt_oc;
|
|
} iterational;
|
|
JERRY_STATIC_ASSERT (sizeof (iterational) == 16); // Please, update size if changed
|
|
|
|
struct if_statement
|
|
{
|
|
vm_instr_counter_t conditional_check_pos;
|
|
vm_instr_counter_t jump_to_end_pos;
|
|
} if_statement;
|
|
JERRY_STATIC_ASSERT (sizeof (if_statement) == 4); // Please, update size if changed
|
|
|
|
struct switch_statement
|
|
{
|
|
jsp_operand_t expr;
|
|
|
|
vm_instr_counter_t default_label_oc; /**< MAX_OPCODES - if DefaultClause didn't occur,
|
|
* start of StatementList for the DefaultClause, otherwise */
|
|
vm_instr_counter_t last_cond_check_jmp_oc; /**< position of last clause's check,
|
|
* of MAX_OPCODES (at start, or after DefaultClause) */
|
|
vm_instr_counter_t skip_check_jmp_oc; /**< position of check for whether next condition check
|
|
* should be performed or skipped */
|
|
vm_idx_t saved_reg_next;
|
|
vm_idx_t saved_reg_max_for_temps;
|
|
} switch_statement;
|
|
JERRY_STATIC_ASSERT (sizeof (switch_statement) == 12); // Please, update size if changed
|
|
|
|
struct with_statement
|
|
{
|
|
vm_instr_counter_t header_pos;
|
|
} with_statement;
|
|
JERRY_STATIC_ASSERT (sizeof (with_statement) == 2); // Please, update size if changed
|
|
|
|
struct try_statement
|
|
{
|
|
vm_instr_counter_t try_pos;
|
|
vm_instr_counter_t catch_pos;
|
|
vm_instr_counter_t finally_pos;
|
|
} try_statement;
|
|
JERRY_STATIC_ASSERT (sizeof (try_statement) == 6); // Please, update size if changed
|
|
} u;
|
|
JERRY_STATIC_ASSERT (sizeof (u) == 16); // Please, update size if changed
|
|
|
|
vm_instr_counter_t breaks_rewrite_chain;
|
|
} statement;
|
|
JERRY_STATIC_ASSERT (sizeof (statement) == 20); // Please, update size if changed
|
|
|
|
struct named_label
|
|
{
|
|
lit_cpointer_t name_cp;
|
|
} named_label;
|
|
JERRY_STATIC_ASSERT (sizeof (named_label) == 2); // Please, update size if changed
|
|
|
|
struct source_elements
|
|
{
|
|
uint16_t parent_scope_child_scopes_counter;
|
|
|
|
vm_instr_counter_t reg_var_decl_oc;
|
|
|
|
scope_type_t parent_scope_type;
|
|
|
|
vm_idx_t saved_reg_next;
|
|
vm_idx_t saved_reg_max_for_temps;
|
|
|
|
union
|
|
{
|
|
mem_cpointer_t parent_scopes_tree_node_cp;
|
|
mem_cpointer_t parent_bc_header_cp;
|
|
} u;
|
|
} source_elements;
|
|
JERRY_STATIC_ASSERT (sizeof (source_elements) == 10); // Please, update size if changed
|
|
} u;
|
|
JERRY_STATIC_ASSERT (sizeof (u) == 20); // Please, update size if changed
|
|
} jsp_state_t;
|
|
|
|
JERRY_STATIC_ASSERT (sizeof (jsp_state_t) == 28); // Please, update if size is changed
|
|
|
|
static void jsp_parse_source_element_list (jsp_ctx_t *, scope_type_t);
|
|
|
|
/**
|
|
* Initialize parser context
|
|
*/
|
|
void
|
|
jsp_init_ctx (jsp_ctx_t *ctx_p, /**< parser context */
|
|
scope_type_t scope_type) /**< starting scope type (SCOPE_TYPE_GLOBAL or SCOPE_TYPE_EVAL) */
|
|
{
|
|
JERRY_ASSERT (scope_type == SCOPE_TYPE_GLOBAL || scope_type == SCOPE_TYPE_EVAL);
|
|
|
|
ctx_p->mode = PREPARSE;
|
|
ctx_p->scope_type = scope_type;
|
|
ctx_p->state_stack_p = NULL;
|
|
ctx_p->u.preparse_stage.current_scope_p = NULL;
|
|
} /* jsp_init_ctx */
|
|
|
|
/**
|
|
* Check whether current stage is the dump stage
|
|
*
|
|
* @return true / false
|
|
*/
|
|
bool
|
|
jsp_is_dump_mode (jsp_ctx_t *ctx_p) /**< parser context */
|
|
{
|
|
return (ctx_p->mode == DUMP);
|
|
} /* jsp_is_dump_mode */
|
|
|
|
/**
|
|
* Switch to dump stage
|
|
*/
|
|
void
|
|
jsp_switch_to_dump_mode (jsp_ctx_t *ctx_p, /**< parser context */
|
|
scopes_tree root_scope_p) /**< root of scopes tree */
|
|
{
|
|
JERRY_ASSERT (ctx_p->mode == PREPARSE);
|
|
JERRY_ASSERT (ctx_p->u.preparse_stage.current_scope_p == NULL);
|
|
|
|
ctx_p->mode = DUMP;
|
|
ctx_p->u.dump_stage.current_bc_header_p = NULL;
|
|
ctx_p->u.dump_stage.next_scope_to_dump_p = root_scope_p;
|
|
} /* jsp_switch_to_dump_mode */
|
|
|
|
/**
|
|
* Check whether currently parsed code is strict mode code
|
|
*
|
|
* @return true / false
|
|
*/
|
|
bool
|
|
jsp_is_strict_mode (jsp_ctx_t *ctx_p) /**< parser context */
|
|
{
|
|
if (jsp_is_dump_mode (ctx_p))
|
|
{
|
|
bytecode_data_header_t *bc_header_p = jsp_get_current_bytecode_header (ctx_p);
|
|
|
|
JERRY_ASSERT (bc_header_p != NULL);
|
|
|
|
return bc_header_p->is_strict;
|
|
}
|
|
else
|
|
{
|
|
scopes_tree scope_p = jsp_get_current_scopes_tree_node (ctx_p);
|
|
|
|
JERRY_ASSERT (scope_p != NULL);
|
|
|
|
return scope_p->strict_mode;
|
|
}
|
|
} /* jsp_is_strict_mode */
|
|
|
|
/**
|
|
* Indicate that currently parsed code is strict mode code
|
|
*/
|
|
void
|
|
jsp_set_strict_mode (jsp_ctx_t *ctx_p) /**< parser context */
|
|
{
|
|
if (jsp_is_dump_mode (ctx_p))
|
|
{
|
|
bytecode_data_header_t *bc_header_p = jsp_get_current_bytecode_header (ctx_p);
|
|
|
|
JERRY_ASSERT (bc_header_p != NULL && bc_header_p->is_strict);
|
|
}
|
|
else
|
|
{
|
|
scopes_tree scope_p = jsp_get_current_scopes_tree_node (ctx_p);
|
|
|
|
JERRY_ASSERT (scope_p != NULL);
|
|
|
|
scope_p->strict_mode = true;
|
|
}
|
|
} /* jsp_set_strict_mode */
|
|
|
|
/**
|
|
* Get current scope type
|
|
*
|
|
* @return scope type
|
|
*/
|
|
scope_type_t
|
|
jsp_get_scope_type (jsp_ctx_t *ctx_p) /**< parser context */
|
|
{
|
|
return ctx_p->scope_type;
|
|
} /* jsp_get_scope_type */
|
|
|
|
/**
|
|
* Set current scope type
|
|
*/
|
|
void
|
|
jsp_set_scope_type (jsp_ctx_t *ctx_p, /**< parser context */
|
|
scope_type_t scope_type) /**< scope type */
|
|
{
|
|
ctx_p->scope_type = scope_type;
|
|
} /* jsp_set_scope_type */
|
|
|
|
/**
|
|
* Get number of current scope's child scopes, for which parse was already started
|
|
*
|
|
* @return the value
|
|
*/
|
|
uint16_t
|
|
jsp_get_processed_child_scopes_counter (jsp_ctx_t *ctx_p) /**< parser context */
|
|
{
|
|
if (jsp_is_dump_mode (ctx_p))
|
|
{
|
|
bytecode_data_header_t *bc_header_p = jsp_get_current_bytecode_header (ctx_p);
|
|
|
|
JERRY_ASSERT (ctx_p->processed_child_scopes_counter <= bc_header_p->func_scopes_count);
|
|
}
|
|
else
|
|
{
|
|
scopes_tree scope = jsp_get_current_scopes_tree_node (ctx_p);
|
|
|
|
JERRY_ASSERT (ctx_p->processed_child_scopes_counter <= scope->child_scopes_num);
|
|
}
|
|
|
|
return ctx_p->processed_child_scopes_counter;
|
|
} /* jsp_get_processed_child_scopes_counter */
|
|
|
|
/**
|
|
* Set number of current scope's child scopes, for which parse was already started
|
|
*/
|
|
void
|
|
jsp_set_processed_child_scopes_counter (jsp_ctx_t *ctx_p, /**< parser context */
|
|
uint16_t processed_child_scopes_counter) /**< the new value */
|
|
{
|
|
if (jsp_is_dump_mode (ctx_p))
|
|
{
|
|
bytecode_data_header_t *bc_header_p = jsp_get_current_bytecode_header (ctx_p);
|
|
|
|
if (bc_header_p == NULL)
|
|
{
|
|
JERRY_ASSERT (processed_child_scopes_counter == 0);
|
|
}
|
|
else
|
|
{
|
|
JERRY_ASSERT (processed_child_scopes_counter <= bc_header_p->func_scopes_count);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
scopes_tree scope = jsp_get_current_scopes_tree_node (ctx_p);
|
|
|
|
if (scope == NULL)
|
|
{
|
|
JERRY_ASSERT (processed_child_scopes_counter == 0);
|
|
}
|
|
else
|
|
{
|
|
JERRY_ASSERT (processed_child_scopes_counter <= scope->child_scopes_num);
|
|
}
|
|
}
|
|
|
|
ctx_p->processed_child_scopes_counter = processed_child_scopes_counter;
|
|
} /* jsp_set_processed_child_scopes_counter */
|
|
|
|
/**
|
|
* Get and increment number of current scope's child scopes, for which parse was already started
|
|
*
|
|
* @return the value before increment
|
|
*/
|
|
uint16_t
|
|
jsp_get_and_inc_processed_child_scopes_counter (jsp_ctx_t *ctx_p) /**< parser context */
|
|
{
|
|
uint16_t counter = jsp_get_processed_child_scopes_counter (ctx_p);
|
|
|
|
uint16_t ret = counter++;
|
|
|
|
jsp_set_processed_child_scopes_counter (ctx_p, counter);
|
|
|
|
return ret;
|
|
} /* jsp_get_and_inc_processed_child_scopes_counter */
|
|
|
|
/**
|
|
* Get next scope to dump
|
|
*
|
|
* Actually, it the function is iterator interface, used for traversing scopes tree during dump stage.
|
|
* It is assumed that node of scopes tree are linked into the list in pre-order traversal order.
|
|
*/
|
|
scopes_tree
|
|
jsp_get_next_scopes_tree_node_to_dump (jsp_ctx_t *ctx_p) /**< parser context */
|
|
{
|
|
JERRY_ASSERT (ctx_p->mode == DUMP);
|
|
|
|
scopes_tree node = ctx_p->u.dump_stage.next_scope_to_dump_p;
|
|
JERRY_ASSERT (node != NULL);
|
|
|
|
ctx_p->u.dump_stage.next_scope_to_dump_p = MEM_CP_GET_POINTER (scopes_tree_int,
|
|
node->next_scope_cp);
|
|
|
|
return node;
|
|
} /* jsp_get_next_scopes_tree_node_to_dump */
|
|
|
|
/**
|
|
* Get current scope (node of scopes tree)
|
|
*
|
|
* Note:
|
|
* Valid only during preparse stage
|
|
*
|
|
* @return the node
|
|
*/
|
|
scopes_tree
|
|
jsp_get_current_scopes_tree_node (jsp_ctx_t *ctx_p) /**< parser context */
|
|
{
|
|
JERRY_ASSERT (ctx_p->mode == PREPARSE);
|
|
|
|
return ctx_p->u.preparse_stage.current_scope_p;
|
|
} /* jsp_get_current_scopes_tree_node */
|
|
|
|
/**
|
|
* Set current scope (node of scopes tree)
|
|
*
|
|
* Note:
|
|
* Valid only during preparse stage
|
|
*/
|
|
void
|
|
jsp_set_current_scopes_tree_node (jsp_ctx_t *ctx_p, /**< parser context */
|
|
scopes_tree scope_p) /**< scopes tree node */
|
|
{
|
|
JERRY_ASSERT (ctx_p->mode == PREPARSE);
|
|
JERRY_ASSERT (scope_p != ctx_p->u.preparse_stage.current_scope_p);
|
|
|
|
ctx_p->u.preparse_stage.current_scope_p = scope_p;
|
|
ctx_p->u.preparse_stage.tmp_lit_set_num = 0;
|
|
jsp_set_processed_child_scopes_counter (ctx_p, 0);
|
|
} /* jsp_set_current_scopes_tree_node */
|
|
|
|
/**
|
|
* Get current scope's byte-code data header
|
|
*
|
|
* Note:
|
|
* Valid only during dump stage
|
|
*
|
|
* @return the byte-code data header
|
|
*/
|
|
bytecode_data_header_t *
|
|
jsp_get_current_bytecode_header (jsp_ctx_t *ctx_p) /**< parser context */
|
|
{
|
|
JERRY_ASSERT (ctx_p->mode == DUMP);
|
|
|
|
return ctx_p->u.dump_stage.current_bc_header_p;
|
|
} /* jsp_get_current_bytecode_header */
|
|
|
|
/**
|
|
* Set current scope's byte-code data header
|
|
*
|
|
* Note:
|
|
* Valid only during dump stage
|
|
*/
|
|
void
|
|
jsp_set_current_bytecode_header (jsp_ctx_t *ctx_p, /**< parser context */
|
|
bytecode_data_header_t *bc_header_p) /**< byte-code data header */
|
|
{
|
|
JERRY_ASSERT (ctx_p->mode == DUMP);
|
|
|
|
ctx_p->u.dump_stage.current_bc_header_p = bc_header_p;
|
|
jsp_set_processed_child_scopes_counter (ctx_p, 0);
|
|
} /* jsp_set_current_bytecode_header */
|
|
|
|
/**
|
|
* Empty temporary literal set
|
|
*
|
|
* Note:
|
|
* Valid only during preparse stage
|
|
*
|
|
* See also:
|
|
* Definition of tmp_lit_set in jsp_ctx_t
|
|
*/
|
|
void
|
|
jsp_empty_tmp_literal_set (jsp_ctx_t *ctx_p) /**< parser context */
|
|
{
|
|
JERRY_ASSERT (ctx_p->mode == PREPARSE);
|
|
|
|
ctx_p->u.preparse_stage.tmp_lit_set_num = 0;
|
|
} /* jsp_empty_tmp_literal_set */
|
|
|
|
/**
|
|
* Take into account the specified literal and adjust temporary literal set and unique literals counter accordingly
|
|
*
|
|
* Note:
|
|
* Valid only during preparse stage
|
|
*
|
|
* See also:
|
|
* Definition of tmp_lit_set in jsp_ctx_t
|
|
*/
|
|
void
|
|
jsp_account_next_bytecode_to_literal_reference (jsp_ctx_t *ctx_p, /**< parser context */
|
|
lit_cpointer_t lit_cp) /**< literal identifier */
|
|
{
|
|
JERRY_ASSERT (ctx_p->mode == PREPARSE);
|
|
|
|
scopes_tree scope_p = jsp_get_current_scopes_tree_node (ctx_p);
|
|
|
|
uint8_t tmp_hash_table_index;
|
|
for (tmp_hash_table_index = 0;
|
|
tmp_hash_table_index < ctx_p->u.preparse_stage.tmp_lit_set_num;
|
|
tmp_hash_table_index++)
|
|
{
|
|
if (ctx_p->u.preparse_stage.tmp_lit_set[tmp_hash_table_index].packed_value == lit_cp.packed_value)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (tmp_hash_table_index == ctx_p->u.preparse_stage.tmp_lit_set_num)
|
|
{
|
|
scope_p->max_uniq_literals_num++;
|
|
|
|
if (ctx_p->u.preparse_stage.tmp_lit_set_num < SCOPE_TMP_LIT_SET_SIZE)
|
|
{
|
|
ctx_p->u.preparse_stage.tmp_lit_set[ctx_p->u.preparse_stage.tmp_lit_set_num++] = lit_cp;
|
|
}
|
|
else
|
|
{
|
|
JERRY_ASSERT (ctx_p->u.preparse_stage.tmp_lit_set_num != 0);
|
|
|
|
ctx_p->u.preparse_stage.tmp_lit_set[ctx_p->u.preparse_stage.tmp_lit_set_num - 1u] = lit_cp;
|
|
}
|
|
}
|
|
} /* jsp_account_next_bytecode_to_literal_reference */
|
|
|
|
static bool
|
|
token_is (jsp_token_type_t tt)
|
|
{
|
|
return (lexer_get_token_type (tok) == tt);
|
|
}
|
|
|
|
static uint16_t
|
|
token_data (void)
|
|
{
|
|
return tok.uid;
|
|
}
|
|
|
|
/**
|
|
* Get token data as `lit_cpointer_t`
|
|
*
|
|
* @return compressed pointer to token data
|
|
*/
|
|
static lit_cpointer_t
|
|
token_data_as_lit_cp (void)
|
|
{
|
|
lit_cpointer_t cp;
|
|
cp.packed_value = tok.uid;
|
|
|
|
return cp;
|
|
} /* token_data_as_lit_cp */
|
|
|
|
static void
|
|
skip_token (jsp_ctx_t *ctx_p)
|
|
{
|
|
tok = lexer_next_token (false, jsp_is_strict_mode (ctx_p));
|
|
}
|
|
|
|
/**
|
|
* In case a regexp token is scanned as a division operator, rescan it
|
|
*/
|
|
static void
|
|
rescan_regexp_token (jsp_ctx_t *ctx_p)
|
|
{
|
|
lexer_seek (tok.loc);
|
|
tok = lexer_next_token (true, jsp_is_strict_mode (ctx_p));
|
|
} /* rescan_regexp_token */
|
|
|
|
static void
|
|
seek_token (jsp_ctx_t *ctx_p,
|
|
locus loc)
|
|
{
|
|
lexer_seek (loc);
|
|
|
|
skip_token (ctx_p);
|
|
}
|
|
|
|
static bool
|
|
is_keyword (jsp_token_type_t tt)
|
|
{
|
|
return (tt >= TOKEN_TYPE__KEYWORD_BEGIN && tt <= TOKEN_TYPE__KEYWORD_END);
|
|
}
|
|
|
|
static void
|
|
assert_keyword (jsp_token_type_t kw)
|
|
{
|
|
if (!token_is (kw))
|
|
{
|
|
EMIT_ERROR_VARG (JSP_EARLY_ERROR_SYNTAX, "Expected keyword '%s'", lexer_token_type_to_string (kw));
|
|
JERRY_UNREACHABLE ();
|
|
}
|
|
}
|
|
|
|
static void
|
|
current_token_must_be_check_and_skip_it (jsp_ctx_t *ctx_p,
|
|
jsp_token_type_t tt)
|
|
{
|
|
if (!token_is (tt))
|
|
{
|
|
EMIT_ERROR_VARG (JSP_EARLY_ERROR_SYNTAX, "Expected '%s' token", lexer_token_type_to_string (tt));
|
|
}
|
|
|
|
skip_token (ctx_p);
|
|
}
|
|
|
|
static void
|
|
current_token_must_be (jsp_token_type_t tt)
|
|
{
|
|
if (!token_is (tt))
|
|
{
|
|
EMIT_ERROR_VARG (JSP_EARLY_ERROR_SYNTAX, "Expected '%s' token", lexer_token_type_to_string (tt));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Skip block, defined with braces of specified type
|
|
*
|
|
* Note:
|
|
* Missing corresponding brace is considered a syntax error
|
|
*
|
|
* Note:
|
|
* Opening brace of the block to skip should be set as current
|
|
* token when the routine is called
|
|
*/
|
|
static void
|
|
jsp_skip_braces (jsp_ctx_t *ctx_p, /**< parser context */
|
|
jsp_token_type_t brace_type) /**< type of the opening brace */
|
|
{
|
|
current_token_must_be (brace_type);
|
|
|
|
jsp_token_type_t closing_bracket_type;
|
|
|
|
if (brace_type == TOK_OPEN_PAREN)
|
|
{
|
|
closing_bracket_type = TOK_CLOSE_PAREN;
|
|
}
|
|
else if (brace_type == TOK_OPEN_BRACE)
|
|
{
|
|
closing_bracket_type = TOK_CLOSE_BRACE;
|
|
}
|
|
else
|
|
{
|
|
JERRY_ASSERT (brace_type == TOK_OPEN_SQUARE);
|
|
closing_bracket_type = TOK_CLOSE_SQUARE;
|
|
}
|
|
|
|
skip_token (ctx_p);
|
|
|
|
while (!token_is (closing_bracket_type)
|
|
&& !token_is (TOK_EOF))
|
|
{
|
|
if (token_is (TOK_OPEN_PAREN)
|
|
|| token_is (TOK_OPEN_BRACE)
|
|
|| token_is (TOK_OPEN_SQUARE))
|
|
{
|
|
jsp_skip_braces (ctx_p, lexer_get_token_type (tok));
|
|
}
|
|
|
|
skip_token (ctx_p);
|
|
}
|
|
|
|
current_token_must_be (closing_bracket_type);
|
|
} /* jsp_skip_braces */
|
|
|
|
/**
|
|
* Find next token of specified type before the specified location
|
|
*
|
|
* Note:
|
|
* If skip_brace_blocks is true, every { should correspond to } brace before search end location,
|
|
* otherwise a syntax error is raised.
|
|
*
|
|
* @return true - if token was found (in the case, it is the current token,
|
|
* and lexer locus points to it),
|
|
* false - otherwise (in the case, lexer locus points to end_loc).
|
|
*/
|
|
static bool
|
|
jsp_find_next_token_before_the_locus (jsp_ctx_t *ctx_p, /**< parser context */
|
|
jsp_token_type_t token_to_find, /**< token to search for (except TOK_EOF) */
|
|
locus end_loc, /**< location to search before */
|
|
bool skip_brace_blocks) /**< skip blocks, surrounded with { and } braces */
|
|
{
|
|
JERRY_ASSERT (token_to_find != TOK_EOF);
|
|
|
|
while (lit_utf8_iterator_pos_cmp (tok.loc, end_loc) < 0)
|
|
{
|
|
if (skip_brace_blocks)
|
|
{
|
|
if (token_is (TOK_OPEN_BRACE))
|
|
{
|
|
jsp_skip_braces (ctx_p, TOK_OPEN_BRACE);
|
|
|
|
JERRY_ASSERT (token_is (TOK_CLOSE_BRACE));
|
|
skip_token (ctx_p);
|
|
|
|
if (lit_utf8_iterator_pos_cmp (tok.loc, end_loc) >= 0)
|
|
{
|
|
seek_token (ctx_p, end_loc);
|
|
|
|
return false;
|
|
}
|
|
}
|
|
else if (token_is (TOK_CLOSE_BRACE))
|
|
{
|
|
EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "Unmatched } brace");
|
|
}
|
|
}
|
|
|
|
if (token_is (token_to_find))
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
JERRY_ASSERT (!token_is (TOK_EOF));
|
|
}
|
|
|
|
skip_token (ctx_p);
|
|
}
|
|
|
|
JERRY_ASSERT (lit_utf8_iterator_pos_cmp (tok.loc, end_loc) == 0);
|
|
return false;
|
|
} /* jsp_find_next_token_before_the_locus */
|
|
|
|
/* property_name
|
|
: Identifier
|
|
| Keyword
|
|
| StringLiteral
|
|
| NumericLiteral
|
|
;
|
|
*/
|
|
static jsp_operand_t
|
|
parse_property_name (void)
|
|
{
|
|
jsp_token_type_t tt = lexer_get_token_type (tok);
|
|
|
|
if (is_keyword (tt))
|
|
{
|
|
const char *s = lexer_token_type_to_string (lexer_get_token_type (tok));
|
|
literal_t lit = lit_find_or_create_literal_from_utf8_string ((const lit_utf8_byte_t *) s,
|
|
(lit_utf8_size_t)strlen (s));
|
|
return jsp_operand_t::make_string_lit_operand (lit_cpointer_t::compress (lit));
|
|
}
|
|
else
|
|
{
|
|
switch (tt)
|
|
{
|
|
case TOK_NAME:
|
|
case TOK_STRING:
|
|
{
|
|
return jsp_operand_t::make_string_lit_operand (token_data_as_lit_cp ());
|
|
}
|
|
case TOK_NUMBER:
|
|
case TOK_SMALL_INT:
|
|
{
|
|
ecma_number_t num;
|
|
|
|
if (lexer_get_token_type (tok) == TOK_NUMBER)
|
|
{
|
|
literal_t num_lit = lit_get_literal_by_cp (token_data_as_lit_cp ());
|
|
JERRY_ASSERT (num_lit->get_type () == LIT_NUMBER_T);
|
|
num = lit_charset_literal_get_number (num_lit);
|
|
}
|
|
else
|
|
{
|
|
num = ((ecma_number_t) token_data ());
|
|
}
|
|
|
|
lit_utf8_byte_t buff[ECMA_MAX_CHARS_IN_STRINGIFIED_NUMBER];
|
|
lit_utf8_size_t sz = ecma_number_to_utf8_string (num, buff, sizeof (buff));
|
|
JERRY_ASSERT (sz <= ECMA_MAX_CHARS_IN_STRINGIFIED_NUMBER);
|
|
|
|
literal_t str_lit = lit_find_or_create_literal_from_utf8_string (buff, sz);
|
|
return jsp_operand_t::make_string_lit_operand (lit_cpointer_t::compress (str_lit));
|
|
}
|
|
case TOK_NULL:
|
|
case TOK_BOOL:
|
|
{
|
|
lit_magic_string_id_t id = (token_is (TOK_NULL)
|
|
? LIT_MAGIC_STRING_NULL
|
|
: (tok.uid ? LIT_MAGIC_STRING_TRUE : LIT_MAGIC_STRING_FALSE));
|
|
literal_t lit = lit_find_or_create_literal_from_utf8_string (lit_get_magic_string_utf8 (id),
|
|
lit_get_magic_string_size (id));
|
|
return jsp_operand_t::make_string_lit_operand (lit_cpointer_t::compress (lit));
|
|
}
|
|
default:
|
|
{
|
|
EMIT_ERROR_VARG (JSP_EARLY_ERROR_SYNTAX,
|
|
"Wrong property name type: %s",
|
|
lexer_token_type_to_string (lexer_get_token_type (tok)));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check for "use strict" in directive prologue
|
|
*/
|
|
static void
|
|
jsp_parse_directive_prologue (jsp_ctx_t *ctx_p)
|
|
{
|
|
const locus start_loc = tok.loc;
|
|
|
|
/*
|
|
* Check Directive Prologue for Use Strict directive (see ECMA-262 5.1 section 14.1)
|
|
*/
|
|
while (token_is (TOK_STRING))
|
|
{
|
|
if (lit_literal_equal_type_cstr (lit_get_literal_by_cp (token_data_as_lit_cp ()), "use strict")
|
|
&& lexer_is_no_escape_sequences_in_token_string (tok))
|
|
{
|
|
jsp_set_strict_mode (ctx_p);
|
|
break;
|
|
}
|
|
|
|
skip_token (ctx_p);
|
|
|
|
if (token_is (TOK_SEMICOLON))
|
|
{
|
|
skip_token (ctx_p);
|
|
}
|
|
}
|
|
|
|
seek_token (ctx_p, start_loc);
|
|
} /* jsp_parse_directive_prologue */
|
|
|
|
static void
|
|
jsp_state_push (jsp_ctx_t *ctx_p,
|
|
jsp_state_t state)
|
|
{
|
|
jsp_state_t *new_top_state_p = (jsp_state_t *) jsp_mm_alloc (sizeof (jsp_state_t));
|
|
|
|
*new_top_state_p = state;
|
|
|
|
new_top_state_p->next_state_cp = MEM_CP_NULL;
|
|
MEM_CP_SET_POINTER (new_top_state_p->prev_state_cp, ctx_p->state_stack_p);
|
|
|
|
if (ctx_p->state_stack_p != NULL)
|
|
{
|
|
MEM_CP_SET_NON_NULL_POINTER (ctx_p->state_stack_p->next_state_cp, new_top_state_p);
|
|
}
|
|
|
|
ctx_p->state_stack_p = new_top_state_p;
|
|
} /* jsp_state_push */
|
|
|
|
static jsp_state_t *
|
|
jsp_get_prev_state (jsp_state_t *state_p)
|
|
{
|
|
return MEM_CP_GET_POINTER (jsp_state_t, state_p->prev_state_cp);
|
|
} /* jsp_get_prev_state */
|
|
|
|
static jsp_state_t *
|
|
jsp_get_next_state (jsp_state_t *state_p)
|
|
{
|
|
return MEM_CP_GET_NON_NULL_POINTER (jsp_state_t, state_p->next_state_cp);
|
|
} /* jsp_get_next_state */
|
|
|
|
|
|
static jsp_state_t *
|
|
jsp_state_top (jsp_ctx_t *ctx_p)
|
|
{
|
|
JERRY_ASSERT (ctx_p->state_stack_p != NULL);
|
|
|
|
return ctx_p->state_stack_p;
|
|
} /* jsp_state_top */
|
|
|
|
static bool
|
|
jsp_is_stack_contains_exactly_one_element (jsp_ctx_t *ctx_p)
|
|
{
|
|
jsp_state_t *top_state_p = jsp_state_top (ctx_p);
|
|
|
|
return (top_state_p != NULL && jsp_get_prev_state (top_state_p) == NULL);
|
|
} /* jsp_is_stack_contains_exactly_one_element */
|
|
|
|
static void
|
|
jsp_state_pop (jsp_ctx_t *ctx_p)
|
|
{
|
|
jsp_state_t *top_state_p = ctx_p->state_stack_p;
|
|
JERRY_ASSERT (top_state_p != NULL);
|
|
|
|
jsp_state_t *prev_state_p = jsp_get_prev_state (top_state_p);
|
|
|
|
if (prev_state_p != NULL)
|
|
{
|
|
prev_state_p->next_state_cp = MEM_CP_NULL;
|
|
}
|
|
|
|
jsp_mm_free (top_state_p);
|
|
|
|
ctx_p->state_stack_p = prev_state_p;
|
|
} /* jsp_state_pop */
|
|
|
|
static void
|
|
jsp_stack_init (jsp_ctx_t *ctx_p)
|
|
{
|
|
JERRY_ASSERT (ctx_p->state_stack_p == NULL);
|
|
} /* jsp_stack_init */
|
|
|
|
static void
|
|
jsp_stack_finalize (jsp_ctx_t *ctx_p)
|
|
{
|
|
while (ctx_p->state_stack_p != NULL)
|
|
{
|
|
jsp_state_pop (ctx_p);
|
|
}
|
|
} /* jsp_stack_finalize */
|
|
|
|
static void
|
|
jsp_push_new_expr_state (jsp_ctx_t *ctx_p,
|
|
jsp_state_expr_t expr_type,
|
|
jsp_state_expr_t req_state,
|
|
bool in_allowed)
|
|
{
|
|
jsp_state_t new_state;
|
|
|
|
new_state.state = expr_type;
|
|
new_state.req_state = req_state;
|
|
new_state.u.expression.operand = empty_operand ();
|
|
new_state.u.expression.prop_name_operand = empty_operand ();
|
|
new_state.u.expression.token_type = TOK_EMPTY;
|
|
|
|
new_state.is_completed = false;
|
|
new_state.is_list_in_process = false;
|
|
new_state.is_fixed_ret_operand = false;
|
|
new_state.is_value_based_reference = false;
|
|
new_state.is_complex_production = false;
|
|
new_state.var_decl = false;
|
|
new_state.is_var_decl_no_in = false;
|
|
new_state.is_get_value_dumped_for_main_operand = false;
|
|
new_state.is_get_value_dumped_for_prop_operand = false;
|
|
new_state.is_need_retval = true;
|
|
new_state.prev_state_cp = MEM_CP_NULL;
|
|
new_state.next_state_cp = MEM_CP_NULL;
|
|
new_state.was_default = false;
|
|
new_state.is_default_branch = false;
|
|
new_state.is_simply_jumpable_border = false;
|
|
new_state.is_dump_eval_ret_store = false;
|
|
new_state.is_stmt_list_control_flow_exit_stmt_occured = false;
|
|
|
|
new_state.is_no_in_mode = !in_allowed;
|
|
|
|
jsp_state_push (ctx_p, new_state);
|
|
} /* jsp_push_new_expr_state */
|
|
|
|
static void
|
|
jsp_start_statement_parse (jsp_ctx_t *ctx_p,
|
|
jsp_state_expr_t stat)
|
|
{
|
|
jsp_state_t new_state;
|
|
|
|
new_state.state = stat;
|
|
new_state.req_state = JSP_STATE_STAT_STATEMENT;
|
|
|
|
new_state.u.statement.breaks_rewrite_chain = MAX_OPCODES;
|
|
|
|
new_state.is_completed = false;
|
|
new_state.is_list_in_process = false;
|
|
new_state.is_no_in_mode = false;
|
|
new_state.is_fixed_ret_operand = false;
|
|
new_state.is_complex_production = false;
|
|
new_state.var_decl = false;
|
|
new_state.was_default = false;
|
|
new_state.is_default_branch = false;
|
|
new_state.is_simply_jumpable_border = false;
|
|
new_state.is_stmt_list_control_flow_exit_stmt_occured = false;
|
|
new_state.prev_state_cp = MEM_CP_NULL;
|
|
new_state.next_state_cp = MEM_CP_NULL;
|
|
new_state.is_value_based_reference = false;
|
|
new_state.is_get_value_dumped_for_main_operand = false;
|
|
new_state.is_get_value_dumped_for_prop_operand = false;
|
|
new_state.is_need_retval = false;
|
|
new_state.is_var_decl_no_in = false;
|
|
new_state.is_dump_eval_ret_store = false;
|
|
|
|
jsp_state_push (ctx_p, new_state);
|
|
} /* jsp_start_statement_parse */
|
|
|
|
static void
|
|
jsp_start_parse_source_element_list (jsp_ctx_t *ctx_p,
|
|
mem_cpointer_t parent_scope_or_bc_header_cp,
|
|
uint16_t parent_scope_processed_scopes_counter,
|
|
scope_type_t scope_type)
|
|
{
|
|
const scope_type_t parent_scope_type = jsp_get_scope_type (ctx_p);
|
|
|
|
jsp_start_statement_parse (ctx_p, JSP_STATE_SOURCE_ELEMENTS);
|
|
|
|
jsp_set_scope_type (ctx_p, scope_type);
|
|
|
|
jsp_state_t *source_elements_state_p = jsp_state_top (ctx_p);
|
|
source_elements_state_p->req_state = JSP_STATE_SOURCE_ELEMENTS;
|
|
|
|
if (jsp_is_dump_mode (ctx_p))
|
|
{
|
|
source_elements_state_p->u.source_elements.u.parent_bc_header_cp = parent_scope_or_bc_header_cp;
|
|
}
|
|
else
|
|
{
|
|
source_elements_state_p->u.source_elements.u.parent_scopes_tree_node_cp = parent_scope_or_bc_header_cp;
|
|
}
|
|
|
|
dumper_save_reg_alloc_ctx (ctx_p,
|
|
&source_elements_state_p->u.source_elements.saved_reg_next,
|
|
&source_elements_state_p->u.source_elements.saved_reg_max_for_temps);
|
|
source_elements_state_p->u.source_elements.reg_var_decl_oc = dump_reg_var_decl_for_rewrite (ctx_p);
|
|
source_elements_state_p->u.source_elements.parent_scope_type = parent_scope_type;
|
|
|
|
source_elements_state_p->u.source_elements.parent_scope_child_scopes_counter = parent_scope_processed_scopes_counter;
|
|
|
|
if (scope_type == SCOPE_TYPE_EVAL)
|
|
{
|
|
dump_variable_assignment (ctx_p,
|
|
jsp_operand_t::make_reg_operand (VM_REG_SPECIAL_EVAL_RET),
|
|
jsp_operand_t::make_simple_value_operand (ECMA_SIMPLE_VALUE_UNDEFINED));
|
|
}
|
|
} /* jsp_start_parse_source_element_list */
|
|
|
|
static jsp_operand_t
|
|
jsp_start_parse_function_scope (jsp_ctx_t *ctx_p,
|
|
jsp_operand_t func_name,
|
|
bool is_function_expression,
|
|
size_t *out_formal_parameters_num_p)
|
|
{
|
|
jsp_operand_t func;
|
|
|
|
uint16_t index;
|
|
|
|
if (jsp_is_dump_mode (ctx_p))
|
|
{
|
|
index = jsp_get_and_inc_processed_child_scopes_counter (ctx_p);
|
|
}
|
|
else
|
|
{
|
|
index = 0u;
|
|
}
|
|
|
|
if (is_function_expression)
|
|
{
|
|
func = tmp_operand ();
|
|
|
|
vm_idx_t idx1 = (vm_idx_t) jrt_extract_bit_field (index, 0, JERRY_BITSINBYTE);
|
|
vm_idx_t idx2 = (vm_idx_t) jrt_extract_bit_field (index, JERRY_BITSINBYTE, JERRY_BITSINBYTE);
|
|
|
|
dump_binary_op (ctx_p,
|
|
VM_OP_FUNC_EXPR_REF,
|
|
func,
|
|
jsp_operand_t::make_idx_const_operand (idx1),
|
|
jsp_operand_t::make_idx_const_operand (idx2));
|
|
}
|
|
else
|
|
{
|
|
func = empty_operand ();
|
|
}
|
|
|
|
mem_cpointer_t parent_scope_or_bc_header_cp;
|
|
uint16_t parent_scope_processed_scopes_counter = jsp_get_processed_child_scopes_counter (ctx_p);
|
|
|
|
scopes_tree func_scope_p;
|
|
|
|
if (!jsp_is_dump_mode (ctx_p))
|
|
{
|
|
scopes_tree parent_scope_p = jsp_get_current_scopes_tree_node (ctx_p);
|
|
MEM_CP_SET_NON_NULL_POINTER (parent_scope_or_bc_header_cp, parent_scope_p);
|
|
|
|
scopes_tree_set_contains_functions (parent_scope_p);
|
|
|
|
func_scope_p = scopes_tree_new_scope (parent_scope_p, SCOPE_TYPE_FUNCTION);
|
|
|
|
jsp_set_current_scopes_tree_node (ctx_p, func_scope_p);
|
|
|
|
scopes_tree_set_strict_mode (func_scope_p, scopes_tree_strict_mode (parent_scope_p));
|
|
}
|
|
else
|
|
{
|
|
bytecode_data_header_t *parent_bc_header_p = jsp_get_current_bytecode_header (ctx_p);
|
|
MEM_CP_SET_NON_NULL_POINTER (parent_scope_or_bc_header_cp, parent_bc_header_p);
|
|
|
|
func_scope_p = jsp_get_next_scopes_tree_node_to_dump (ctx_p);
|
|
|
|
bc_dump_single_scope (func_scope_p);
|
|
|
|
mem_cpointer_t *parent_declarations_p = MEM_CP_GET_POINTER (mem_cpointer_t,
|
|
parent_bc_header_p->declarations_cp);
|
|
parent_declarations_p[index] = func_scope_p->bc_header_cp;
|
|
|
|
bytecode_data_header_t *func_bc_header_p = MEM_CP_GET_NON_NULL_POINTER (bytecode_data_header_t,
|
|
func_scope_p->bc_header_cp);
|
|
|
|
jsp_set_current_bytecode_header (ctx_p, func_bc_header_p);
|
|
|
|
scopes_tree_free_scope (func_scope_p);
|
|
}
|
|
|
|
/* parse formal parameters list */
|
|
size_t formal_parameters_num = 0;
|
|
|
|
current_token_must_be_check_and_skip_it (ctx_p, TOK_OPEN_PAREN);
|
|
|
|
JERRY_ASSERT (func_name.is_empty_operand ()
|
|
|| func_name.is_string_lit_operand ());
|
|
|
|
varg_list_type vlt = is_function_expression ? VARG_FUNC_EXPR : VARG_FUNC_DECL;
|
|
|
|
vm_instr_counter_t varg_header_pos = dump_varg_header_for_rewrite (ctx_p, vlt, func, func_name);
|
|
|
|
locus formal_parameters_list_loc = tok.loc;
|
|
|
|
while (!token_is (TOK_CLOSE_PAREN))
|
|
{
|
|
current_token_must_be (TOK_NAME);
|
|
jsp_operand_t formal_parameter_name = jsp_operand_t::make_string_lit_operand (token_data_as_lit_cp ());
|
|
skip_token (ctx_p);
|
|
|
|
dump_varg (ctx_p, formal_parameter_name);
|
|
|
|
formal_parameters_num++;
|
|
|
|
if (token_is (TOK_COMMA))
|
|
{
|
|
skip_token (ctx_p);
|
|
}
|
|
else
|
|
{
|
|
current_token_must_be (TOK_CLOSE_PAREN);
|
|
}
|
|
}
|
|
|
|
skip_token (ctx_p);
|
|
|
|
rewrite_varg_header_set_args_count (ctx_p, formal_parameters_num, varg_header_pos);
|
|
|
|
current_token_must_be_check_and_skip_it (ctx_p, TOK_OPEN_BRACE);
|
|
|
|
jsp_parse_directive_prologue (ctx_p);
|
|
|
|
if (!jsp_is_dump_mode (ctx_p))
|
|
{
|
|
jsp_early_error_check_for_eval_and_arguments_in_strict_mode (func_name, jsp_is_strict_mode (ctx_p), tok.loc);
|
|
}
|
|
|
|
if (jsp_is_strict_mode (ctx_p))
|
|
{
|
|
locus body_loc = tok.loc;
|
|
|
|
seek_token (ctx_p, formal_parameters_list_loc);
|
|
|
|
/* Check duplication of formal parameters names */
|
|
while (!token_is (TOK_CLOSE_PAREN))
|
|
{
|
|
current_token_must_be (TOK_NAME);
|
|
|
|
literal_t current_parameter_name_lit = lit_get_literal_by_cp (token_data_as_lit_cp ());
|
|
locus current_parameter_loc = tok.loc;
|
|
|
|
skip_token (ctx_p);
|
|
|
|
if (token_is (TOK_COMMA))
|
|
{
|
|
skip_token (ctx_p);
|
|
}
|
|
|
|
if (!jsp_is_dump_mode (ctx_p))
|
|
{
|
|
jsp_early_error_emit_error_on_eval_and_arguments (current_parameter_name_lit,
|
|
current_parameter_loc);
|
|
}
|
|
|
|
while (!token_is (TOK_CLOSE_PAREN))
|
|
{
|
|
current_token_must_be (TOK_NAME);
|
|
|
|
if (lit_utf8_iterator_pos_cmp (tok.loc, current_parameter_loc) != 0)
|
|
{
|
|
literal_t iter_parameter_name_lit = lit_get_literal_by_cp (token_data_as_lit_cp ());
|
|
|
|
if (lit_literal_equal_type (current_parameter_name_lit, iter_parameter_name_lit))
|
|
{
|
|
PARSE_ERROR_VARG (JSP_EARLY_ERROR_SYNTAX,
|
|
"Duplication of literal '%s' in FormalParameterList is not allowed in strict mode",
|
|
tok.loc, lit_literal_to_str_internal_buf (iter_parameter_name_lit));
|
|
}
|
|
}
|
|
|
|
skip_token (ctx_p);
|
|
|
|
if (token_is (TOK_COMMA))
|
|
{
|
|
skip_token (ctx_p);
|
|
}
|
|
else
|
|
{
|
|
current_token_must_be (TOK_CLOSE_PAREN);
|
|
}
|
|
}
|
|
|
|
seek_token (ctx_p, current_parameter_loc);
|
|
|
|
JERRY_ASSERT (lit_utf8_iterator_pos_cmp (tok.loc, current_parameter_loc) == 0);
|
|
skip_token (ctx_p);
|
|
|
|
if (token_is (TOK_COMMA))
|
|
{
|
|
skip_token (ctx_p);
|
|
}
|
|
else
|
|
{
|
|
current_token_must_be (TOK_CLOSE_PAREN);
|
|
}
|
|
}
|
|
|
|
seek_token (ctx_p, body_loc);
|
|
}
|
|
|
|
if (out_formal_parameters_num_p != NULL)
|
|
{
|
|
*out_formal_parameters_num_p = formal_parameters_num;
|
|
}
|
|
|
|
jsp_start_parse_source_element_list (ctx_p,
|
|
parent_scope_or_bc_header_cp,
|
|
parent_scope_processed_scopes_counter,
|
|
SCOPE_TYPE_FUNCTION);
|
|
|
|
return func;
|
|
}
|
|
|
|
static void
|
|
jsp_finish_parse_function_scope (jsp_ctx_t *ctx_p)
|
|
{
|
|
current_token_must_be_check_and_skip_it (ctx_p, TOK_CLOSE_BRACE);
|
|
}
|
|
|
|
static void
|
|
dump_get_value_for_state_if_const (jsp_ctx_t *ctx_p,
|
|
jsp_state_t *state_p)
|
|
{
|
|
// todo: pass to dumper
|
|
(void) ctx_p;
|
|
|
|
JERRY_ASSERT (state_p->state > JSP_STATE_EXPR__BEGIN
|
|
&& state_p->state < JSP_STATE_EXPR__END);
|
|
JERRY_ASSERT (!state_p->is_value_based_reference);
|
|
|
|
if (state_p->u.expression.operand.is_string_lit_operand ()
|
|
|| state_p->u.expression.operand.is_number_lit_operand ()
|
|
|| state_p->u.expression.operand.is_regexp_lit_operand ()
|
|
|| state_p->u.expression.operand.is_smallint_operand ()
|
|
|| state_p->u.expression.operand.is_simple_value_operand ())
|
|
{
|
|
jsp_operand_t reg = tmp_operand ();
|
|
|
|
dump_variable_assignment (ctx_p, reg, state_p->u.expression.operand);
|
|
|
|
state_p->u.expression.operand = reg;
|
|
}
|
|
}
|
|
|
|
static bool
|
|
dump_get_value_for_state_if_ref (jsp_ctx_t *ctx_p,
|
|
jsp_state_t *state_p,
|
|
bool is_dump_for_value_based_refs_only,
|
|
bool is_check_only)
|
|
{
|
|
// TODO: pass to dumper
|
|
(void) ctx_p;
|
|
|
|
jsp_operand_t obj = state_p->u.expression.operand;
|
|
jsp_operand_t prop_name = state_p->u.expression.prop_name_operand;
|
|
bool is_value_based_reference = state_p->is_value_based_reference;
|
|
|
|
jsp_operand_t val;
|
|
|
|
bool is_dump = false;
|
|
|
|
if (state_p->state == JSP_STATE_EXPR_LEFTHANDSIDE)
|
|
{
|
|
if (is_value_based_reference)
|
|
{
|
|
if (!state_p->is_get_value_dumped_for_main_operand)
|
|
{
|
|
JERRY_ASSERT (!state_p->is_get_value_dumped_for_prop_operand);
|
|
/* FIXME:
|
|
if (obj.is_identifier_operand ())
|
|
{
|
|
is_dump = true;
|
|
|
|
if (!is_check_only)
|
|
{
|
|
jsp_operand_t general_reg = tmp_operand ();
|
|
|
|
dump_variable_assignment (ctx_p, general_reg, obj);
|
|
|
|
state_p->u.expression.operand = general_reg;
|
|
state_p->is_get_value_dumped_for_main_operand = true;
|
|
}
|
|
}
|
|
|
|
if (prop_name.is_identifier_operand ())
|
|
{
|
|
is_dump = true;
|
|
|
|
if (!is_check_only)
|
|
{
|
|
jsp_operand_t general_reg = tmp_operand ();
|
|
|
|
dump_variable_assignment (ctx_p, general_reg, prop_name);
|
|
|
|
state_p->u.expression.prop_name_operand = general_reg;
|
|
state_p->is_get_value_dumped_for_prop_operand = true;
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (is_value_based_reference)
|
|
{
|
|
is_dump = true;
|
|
|
|
JERRY_ASSERT (!prop_name.is_empty_operand ());
|
|
|
|
if (!is_check_only)
|
|
{
|
|
jsp_operand_t reg = tmp_operand ();
|
|
|
|
dump_prop_getter (ctx_p, reg, obj, prop_name);
|
|
|
|
val = reg;
|
|
|
|
state_p->is_get_value_dumped_for_main_operand = true;
|
|
state_p->is_get_value_dumped_for_prop_operand = true;
|
|
}
|
|
}
|
|
else if (!is_dump_for_value_based_refs_only
|
|
&& (obj.is_identifier_operand () || obj.is_this_operand ()))
|
|
{
|
|
if (!state_p->is_get_value_dumped_for_main_operand)
|
|
{
|
|
is_dump = true;
|
|
|
|
if (!is_check_only)
|
|
{
|
|
jsp_operand_t general_reg = tmp_operand ();
|
|
|
|
dump_variable_assignment (ctx_p, general_reg, obj);
|
|
|
|
val = general_reg;
|
|
|
|
state_p->is_get_value_dumped_for_main_operand = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!is_check_only && is_dump)
|
|
{
|
|
JERRY_ASSERT (state_p->is_get_value_dumped_for_main_operand);
|
|
JERRY_ASSERT (!state_p->is_value_based_reference || state_p->is_get_value_dumped_for_prop_operand);
|
|
|
|
state_p->u.expression.operand = val;
|
|
state_p->u.expression.prop_name_operand = empty_operand ();
|
|
state_p->is_value_based_reference = false;
|
|
}
|
|
}
|
|
|
|
return is_dump;
|
|
}
|
|
|
|
static void
|
|
dump_get_value_for_prev_states (jsp_ctx_t *ctx_p,
|
|
jsp_state_t *state_p)
|
|
{
|
|
jsp_state_t *dump_state_border_for_p = jsp_get_prev_state (state_p);
|
|
jsp_state_t *first_state_to_dump_for_p = state_p;
|
|
|
|
while (dump_state_border_for_p != NULL)
|
|
{
|
|
if (dump_state_border_for_p->state > JSP_STATE_EXPR__BEGIN
|
|
&& dump_state_border_for_p->req_state < JSP_STATE_EXPR__END
|
|
&& (!dump_state_border_for_p->is_get_value_dumped_for_main_operand
|
|
|| !dump_state_border_for_p->is_get_value_dumped_for_prop_operand))
|
|
{
|
|
first_state_to_dump_for_p = dump_state_border_for_p;
|
|
dump_state_border_for_p = jsp_get_prev_state (dump_state_border_for_p);
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
JERRY_ASSERT (first_state_to_dump_for_p != NULL);
|
|
|
|
jsp_state_t *state_iter_p = first_state_to_dump_for_p;
|
|
|
|
while (state_iter_p != state_p)
|
|
{
|
|
dump_get_value_for_state_if_ref (ctx_p, state_iter_p, false, false);
|
|
|
|
state_iter_p = jsp_get_next_state (state_iter_p);
|
|
}
|
|
}
|
|
|
|
static void
|
|
dump_get_value_if_ref (jsp_ctx_t *ctx_p,
|
|
jsp_state_t *state_p,
|
|
bool is_dump_for_value_based_refs_only)
|
|
{
|
|
if (dump_get_value_for_state_if_ref (ctx_p, state_p, is_dump_for_value_based_refs_only, true))
|
|
{
|
|
dump_get_value_for_prev_states (ctx_p, state_p);
|
|
|
|
dump_get_value_for_state_if_ref (ctx_p, state_p, is_dump_for_value_based_refs_only, false);
|
|
}
|
|
}
|
|
|
|
static vm_instr_counter_t
|
|
jsp_start_call_dump (jsp_ctx_t *ctx_p,
|
|
jsp_state_t *state_p,
|
|
vm_idx_t *out_state_p)
|
|
{
|
|
bool is_value_based_reference = state_p->is_value_based_reference;
|
|
|
|
opcode_call_flags_t call_flags = OPCODE_CALL_FLAGS__EMPTY;
|
|
|
|
jsp_operand_t obj, val;
|
|
|
|
if (is_value_based_reference)
|
|
{
|
|
obj = state_p->u.expression.operand;
|
|
jsp_operand_t prop_name = state_p->u.expression.prop_name_operand;
|
|
|
|
if (obj.is_identifier_operand ()
|
|
&& !state_p->is_get_value_dumped_for_main_operand)
|
|
{
|
|
jsp_operand_t reg = tmp_operand ();
|
|
|
|
dump_variable_assignment (ctx_p, reg, obj);
|
|
|
|
obj = reg;
|
|
|
|
state_p->is_get_value_dumped_for_main_operand = true;
|
|
}
|
|
|
|
val = tmp_operand ();
|
|
dump_prop_getter (ctx_p, val, obj, prop_name);
|
|
|
|
state_p->is_get_value_dumped_for_prop_operand = true;
|
|
|
|
call_flags = (opcode_call_flags_t) (call_flags | OPCODE_CALL_FLAGS_HAVE_THIS_ARG);
|
|
|
|
/*
|
|
* Presence of explicit 'this' argument implies that this is not Direct call to Eval
|
|
*
|
|
* See also:
|
|
* ECMA-262 v5, 15.2.2.1
|
|
*/
|
|
}
|
|
else
|
|
{
|
|
dump_get_value_for_state_if_const (ctx_p, state_p);
|
|
|
|
obj = state_p->u.expression.operand;
|
|
|
|
if (dumper_is_eval_literal (obj))
|
|
{
|
|
call_flags = (opcode_call_flags_t) (call_flags | OPCODE_CALL_FLAGS_DIRECT_CALL_TO_EVAL_FORM);
|
|
}
|
|
|
|
/*
|
|
* Note:
|
|
* If function is called through Identifier, then the obj should be an Identifier reference,
|
|
* not register variable.
|
|
* Otherwise, if function is called immediately, without reference (for example, through anonymous
|
|
* function expression), the obj should be a register variable.
|
|
*
|
|
* See also:
|
|
* vm_helper_call_get_call_flags_and_this_arg
|
|
*/
|
|
val = obj;
|
|
|
|
state_p->is_get_value_dumped_for_main_operand = true;
|
|
}
|
|
|
|
|
|
vm_idx_t reg_alloc_saved_state = dumper_save_reg_alloc_counter (ctx_p);
|
|
|
|
jsp_operand_t ret = tmp_operand ();
|
|
vm_instr_counter_t varg_header_pos = dump_varg_header_for_rewrite (ctx_p, VARG_CALL_EXPR, ret, val);
|
|
|
|
*out_state_p = dumper_save_reg_alloc_counter (ctx_p);
|
|
|
|
dumper_restore_reg_alloc_counter (ctx_p, reg_alloc_saved_state);
|
|
|
|
if (call_flags != OPCODE_CALL_FLAGS__EMPTY)
|
|
{
|
|
if (call_flags & OPCODE_CALL_FLAGS_HAVE_THIS_ARG)
|
|
{
|
|
dump_call_additional_info (ctx_p, call_flags, obj);
|
|
}
|
|
else
|
|
{
|
|
dump_call_additional_info (ctx_p, call_flags, empty_operand ());
|
|
}
|
|
}
|
|
|
|
state_p->u.expression.operand = ret;
|
|
state_p->u.expression.prop_name_operand = empty_operand ();
|
|
state_p->is_value_based_reference = false;
|
|
|
|
return varg_header_pos;
|
|
} /* jsp_start_call_dump */
|
|
|
|
static void
|
|
jsp_finish_call_dump (jsp_ctx_t *ctx_p,
|
|
uint32_t args_num,
|
|
vm_instr_counter_t header_pos,
|
|
vm_idx_t reg_alloc_saved_state)
|
|
{
|
|
(void) ctx_p;
|
|
|
|
dumper_restore_reg_alloc_counter (ctx_p, reg_alloc_saved_state);
|
|
|
|
rewrite_varg_header_set_args_count (ctx_p, args_num, header_pos);
|
|
} /* jsp_finish_call_dump */
|
|
|
|
static vm_instr_counter_t __attr_unused___
|
|
jsp_start_construct_dump (jsp_ctx_t *ctx_p,
|
|
jsp_state_t *state_p,
|
|
vm_idx_t *out_state_p)
|
|
{
|
|
dump_get_value_if_ref (ctx_p, state_p, true);
|
|
dump_get_value_for_state_if_const (ctx_p, state_p);
|
|
|
|
state_p->is_get_value_dumped_for_main_operand = true;
|
|
|
|
vm_idx_t reg_alloc_saved_state = dumper_save_reg_alloc_counter (ctx_p);
|
|
|
|
jsp_operand_t ret = tmp_operand ();
|
|
vm_instr_counter_t pos = dump_varg_header_for_rewrite (ctx_p,
|
|
VARG_CONSTRUCT_EXPR,
|
|
ret,
|
|
state_p->u.expression.operand);
|
|
state_p->u.expression.operand = ret;
|
|
|
|
*out_state_p = dumper_save_reg_alloc_counter (ctx_p);
|
|
|
|
dumper_restore_reg_alloc_counter (ctx_p, reg_alloc_saved_state);
|
|
|
|
return pos;
|
|
} /* jsp_start_construct_dump */
|
|
|
|
static void
|
|
jsp_finish_construct_dump (jsp_ctx_t *ctx_p,
|
|
uint32_t args_num,
|
|
vm_instr_counter_t header_pos,
|
|
vm_idx_t reg_alloc_saved_state)
|
|
{
|
|
// TODO: pass to dumper
|
|
(void) ctx_p;
|
|
|
|
dumper_restore_reg_alloc_counter (ctx_p, reg_alloc_saved_state);
|
|
|
|
rewrite_varg_header_set_args_count (ctx_p, args_num, header_pos);
|
|
} /* jsp_finish_construct_dump */
|
|
|
|
static void
|
|
jsp_add_nested_jump_to_rewrite_chain (jsp_ctx_t *ctx_p,
|
|
vm_instr_counter_t *in_out_rewrite_chain_p)
|
|
{
|
|
// TODO: pass to dumper
|
|
(void) ctx_p;
|
|
|
|
*in_out_rewrite_chain_p = dump_simple_or_nested_jump_for_rewrite (ctx_p, true, false, false,
|
|
empty_operand (),
|
|
*in_out_rewrite_chain_p);
|
|
}
|
|
|
|
static void
|
|
jsp_add_simple_jump_to_rewrite_chain (jsp_ctx_t *ctx_p,
|
|
vm_instr_counter_t *in_out_rewrite_chain_p)
|
|
{
|
|
// TODO: pass to dumper
|
|
(void) ctx_p;
|
|
|
|
*in_out_rewrite_chain_p = dump_simple_or_nested_jump_for_rewrite (ctx_p, false, false, false,
|
|
empty_operand (),
|
|
*in_out_rewrite_chain_p);
|
|
}
|
|
|
|
static void
|
|
jsp_add_conditional_jump_to_rewrite_chain (jsp_ctx_t *ctx_p,
|
|
vm_instr_counter_t *in_out_rewrite_chain_p,
|
|
bool is_inverted_condition,
|
|
jsp_operand_t condition)
|
|
{
|
|
// TODO: pass to dumper
|
|
(void) ctx_p;
|
|
|
|
*in_out_rewrite_chain_p = dump_simple_or_nested_jump_for_rewrite (ctx_p, false, true, is_inverted_condition,
|
|
condition,
|
|
*in_out_rewrite_chain_p);
|
|
}
|
|
|
|
static uint32_t
|
|
jsp_rewrite_jumps_chain (jsp_ctx_t *ctx_p,
|
|
vm_instr_counter_t *rewrite_chain_p,
|
|
vm_instr_counter_t target_oc)
|
|
{
|
|
// TODO: pass to dumper
|
|
(void) ctx_p;
|
|
|
|
uint32_t count = 0;
|
|
|
|
while (*rewrite_chain_p != MAX_OPCODES)
|
|
{
|
|
count++;
|
|
|
|
*rewrite_chain_p = rewrite_simple_or_nested_jump_and_get_next (ctx_p,
|
|
*rewrite_chain_p,
|
|
target_oc);
|
|
}
|
|
|
|
return count;
|
|
} /* jsp_rewrite_jumps_chain */
|
|
|
|
static bool
|
|
jsp_is_assignment_expression_end (jsp_state_t *current_state_p)
|
|
{
|
|
jsp_token_type_t tt = lexer_get_token_type (tok);
|
|
|
|
JERRY_ASSERT (current_state_p->state == JSP_STATE_EXPR_UNARY
|
|
|| current_state_p->state == JSP_STATE_EXPR_MULTIPLICATIVE
|
|
|| current_state_p->state == JSP_STATE_EXPR_ADDITIVE
|
|
|| current_state_p->state == JSP_STATE_EXPR_SHIFT
|
|
|| current_state_p->state == JSP_STATE_EXPR_RELATIONAL
|
|
|| current_state_p->state == JSP_STATE_EXPR_EQUALITY
|
|
|| current_state_p->state == JSP_STATE_EXPR_BITWISE_AND
|
|
|| current_state_p->state == JSP_STATE_EXPR_BITWISE_XOR
|
|
|| current_state_p->state == JSP_STATE_EXPR_BITWISE_OR);
|
|
|
|
if ((tt >= TOKEN_TYPE__MULTIPLICATIVE_BEGIN
|
|
&& tt <= TOKEN_TYPE__MULTIPLICATIVE_END)
|
|
|| (tt >= TOKEN_TYPE__ADDITIVE_BEGIN
|
|
&& tt <= TOKEN_TYPE__ADDITIVE_END)
|
|
|| (tt >= TOKEN_TYPE__SHIFT_BEGIN
|
|
&& tt <= TOKEN_TYPE__SHIFT_END)
|
|
|| (tt >= TOKEN_TYPE__RELATIONAL_BEGIN
|
|
&& tt <= TOKEN_TYPE__RELATIONAL_END)
|
|
|| (tt >= TOKEN_TYPE__EQUALITY_BEGIN
|
|
&& tt <= TOKEN_TYPE__EQUALITY_END)
|
|
|| (tt == TOK_AND)
|
|
|| (tt == TOK_XOR)
|
|
|| (tt == TOK_OR)
|
|
|| (tt == TOK_DOUBLE_AND)
|
|
|| (tt == TOK_DOUBLE_OR)
|
|
|| (tt == TOK_QUERY))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
jsp_dump_unary_op (jsp_ctx_t *ctx_p,
|
|
jsp_state_t *state_p,
|
|
jsp_state_t *substate_p)
|
|
{
|
|
vm_op_t opcode;
|
|
|
|
bool is_combined_with_assignment = false;
|
|
|
|
jsp_state_t *prev_state_p = jsp_get_prev_state (state_p);
|
|
JERRY_ASSERT (prev_state_p != NULL);
|
|
|
|
bool is_try_combine_with_assignment = (prev_state_p->state == JSP_STATE_EXPR_LEFTHANDSIDE
|
|
&& prev_state_p->u.expression.token_type == TOK_EQ
|
|
&& !prev_state_p->is_value_based_reference
|
|
&& !prev_state_p->is_need_retval
|
|
&& jsp_is_assignment_expression_end (state_p));
|
|
|
|
jsp_token_type_t tt = state_p->u.expression.token_type;
|
|
state_p->u.expression.token_type = TOK_EMPTY;
|
|
|
|
bool is_dump_simple_assignment = false;
|
|
bool is_dump_undefined_or_boolean_true = true; /* the value is not valid until is_dump_simple_assignment is set */
|
|
|
|
switch (tt)
|
|
{
|
|
case TOK_DOUBLE_PLUS:
|
|
case TOK_DOUBLE_MINUS:
|
|
{
|
|
if (!substate_p->is_value_based_reference)
|
|
{
|
|
if (!jsp_is_dump_mode (ctx_p))
|
|
{
|
|
if (substate_p->u.expression.operand.is_identifier_operand ())
|
|
{
|
|
jsp_early_error_check_for_eval_and_arguments_in_strict_mode (substate_p->u.expression.operand,
|
|
jsp_is_strict_mode (ctx_p),
|
|
tok.loc);
|
|
}
|
|
else
|
|
{
|
|
PARSE_ERROR (JSP_EARLY_ERROR_REFERENCE, "Invalid left-hand-side expression", tok.loc);
|
|
}
|
|
}
|
|
}
|
|
|
|
opcode = (tt == TOK_DOUBLE_PLUS ? VM_OP_PRE_INCR : VM_OP_PRE_DECR);
|
|
break;
|
|
}
|
|
case TOK_PLUS:
|
|
{
|
|
dump_get_value_if_ref (ctx_p, substate_p, true);
|
|
|
|
opcode = VM_OP_UNARY_PLUS;
|
|
break;
|
|
}
|
|
case TOK_MINUS:
|
|
{
|
|
dump_get_value_if_ref (ctx_p, substate_p, true);
|
|
|
|
opcode = VM_OP_UNARY_MINUS;
|
|
break;
|
|
}
|
|
case TOK_COMPL:
|
|
{
|
|
dump_get_value_if_ref (ctx_p, substate_p, true);
|
|
|
|
opcode = VM_OP_B_NOT;
|
|
break;
|
|
}
|
|
case TOK_NOT:
|
|
{
|
|
dump_get_value_if_ref (ctx_p, substate_p, true);
|
|
|
|
opcode = VM_OP_LOGICAL_NOT;
|
|
break;
|
|
}
|
|
case TOK_KW_DELETE:
|
|
{
|
|
if (substate_p->is_value_based_reference)
|
|
{
|
|
opcode = VM_OP_DELETE_PROP;
|
|
}
|
|
else if (substate_p->u.expression.operand.is_identifier_operand ())
|
|
{
|
|
if (!jsp_is_dump_mode (ctx_p))
|
|
{
|
|
jsp_early_error_check_delete (jsp_is_strict_mode (ctx_p), tok.loc);
|
|
}
|
|
|
|
opcode = VM_OP_DELETE_VAR;
|
|
}
|
|
else
|
|
{
|
|
opcode = VM_OP__COUNT /* invalid opcode as it will not be used */;
|
|
|
|
is_dump_simple_assignment = true;
|
|
is_dump_undefined_or_boolean_true = false; /* dump boolean true value */
|
|
}
|
|
break;
|
|
}
|
|
case TOK_KW_VOID:
|
|
{
|
|
dump_get_value_if_ref (ctx_p, substate_p, false);
|
|
|
|
opcode = VM_OP__COUNT /* invalid opcode as it will not be used */;
|
|
|
|
is_dump_simple_assignment = true;
|
|
is_dump_undefined_or_boolean_true = true; /* dump undefined value */
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
dump_get_value_if_ref (ctx_p, substate_p, true);
|
|
|
|
JERRY_ASSERT (tt == TOK_KW_TYPEOF);
|
|
|
|
opcode = VM_OP_TYPEOF;
|
|
break;
|
|
}
|
|
}
|
|
|
|
jsp_operand_t dst;
|
|
|
|
if (!substate_p->is_value_based_reference)
|
|
{
|
|
dump_get_value_for_state_if_const (ctx_p, substate_p);
|
|
}
|
|
|
|
if (dump_get_value_for_state_if_ref (ctx_p, substate_p, false, true))
|
|
{
|
|
dump_get_value_for_prev_states (ctx_p, substate_p);
|
|
}
|
|
|
|
if (is_try_combine_with_assignment
|
|
&& (is_dump_simple_assignment
|
|
|| !substate_p->is_value_based_reference
|
|
|| opcode == VM_OP_DELETE_PROP))
|
|
{
|
|
dst = prev_state_p->u.expression.operand;
|
|
|
|
is_combined_with_assignment = true;
|
|
}
|
|
else
|
|
{
|
|
dst = tmp_operand ();
|
|
}
|
|
|
|
if (is_dump_simple_assignment)
|
|
{
|
|
if (is_dump_undefined_or_boolean_true)
|
|
{
|
|
dump_variable_assignment (ctx_p, dst, jsp_operand_t::make_simple_value_operand (ECMA_SIMPLE_VALUE_UNDEFINED));
|
|
}
|
|
else
|
|
{
|
|
dump_variable_assignment (ctx_p, dst, jsp_operand_t::make_simple_value_operand (ECMA_SIMPLE_VALUE_TRUE));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (substate_p->is_value_based_reference)
|
|
{
|
|
if (opcode == VM_OP_DELETE_PROP)
|
|
{
|
|
dump_binary_op (ctx_p,
|
|
VM_OP_DELETE_PROP,
|
|
dst,
|
|
substate_p->u.expression.operand,
|
|
substate_p->u.expression.prop_name_operand);
|
|
}
|
|
else
|
|
{
|
|
JERRY_ASSERT (opcode == VM_OP_PRE_INCR || opcode == VM_OP_PRE_DECR);
|
|
|
|
jsp_operand_t reg = tmp_operand ();
|
|
|
|
dump_prop_getter (ctx_p, reg, substate_p->u.expression.operand, substate_p->u.expression.prop_name_operand);
|
|
|
|
dump_unary_op (ctx_p, opcode, reg, reg);
|
|
|
|
dump_prop_setter (ctx_p, substate_p->u.expression.operand, substate_p->u.expression.prop_name_operand, reg);
|
|
|
|
dst = reg;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
JERRY_ASSERT (!substate_p->is_value_based_reference);
|
|
|
|
dump_unary_op (ctx_p, opcode, dst, substate_p->u.expression.operand);
|
|
}
|
|
}
|
|
|
|
JERRY_ASSERT (!state_p->is_value_based_reference);
|
|
state_p->u.expression.operand = dst;
|
|
|
|
return is_combined_with_assignment;
|
|
}
|
|
|
|
static bool
|
|
jsp_dump_binary_op (jsp_ctx_t *ctx_p,
|
|
jsp_state_t *state_p,
|
|
jsp_state_t *substate_p)
|
|
{
|
|
vm_op_t opcode;
|
|
|
|
bool is_combined_with_assignment = false;
|
|
|
|
jsp_state_t *prev_state_p = jsp_get_prev_state (state_p);
|
|
JERRY_ASSERT (prev_state_p != NULL);
|
|
|
|
bool is_try_combine_with_assignment = (prev_state_p->state == JSP_STATE_EXPR_LEFTHANDSIDE
|
|
&& prev_state_p->u.expression.token_type == TOK_EQ
|
|
&& !prev_state_p->is_value_based_reference
|
|
&& !prev_state_p->is_need_retval
|
|
&& jsp_is_assignment_expression_end (state_p));
|
|
|
|
jsp_token_type_t tt = state_p->u.expression.token_type;
|
|
state_p->u.expression.token_type = TOK_EMPTY;
|
|
|
|
switch (tt)
|
|
{
|
|
case TOK_MULT:
|
|
{
|
|
opcode = VM_OP_MULTIPLICATION;
|
|
break;
|
|
}
|
|
case TOK_DIV:
|
|
{
|
|
opcode = VM_OP_DIVISION;
|
|
break;
|
|
}
|
|
case TOK_MOD:
|
|
{
|
|
opcode = VM_OP_REMAINDER;
|
|
break;
|
|
}
|
|
case TOK_PLUS:
|
|
{
|
|
opcode = VM_OP_ADDITION;
|
|
break;
|
|
}
|
|
case TOK_MINUS:
|
|
{
|
|
opcode = VM_OP_SUBSTRACTION;
|
|
break;
|
|
}
|
|
case TOK_LSHIFT:
|
|
{
|
|
opcode = VM_OP_B_SHIFT_LEFT;
|
|
break;
|
|
}
|
|
case TOK_RSHIFT:
|
|
{
|
|
opcode = VM_OP_B_SHIFT_RIGHT;
|
|
break;
|
|
}
|
|
case TOK_RSHIFT_EX:
|
|
{
|
|
opcode = VM_OP_B_SHIFT_URIGHT;
|
|
break;
|
|
}
|
|
case TOK_LESS:
|
|
{
|
|
opcode = VM_OP_LESS_THAN;
|
|
break;
|
|
}
|
|
case TOK_GREATER:
|
|
{
|
|
opcode = VM_OP_GREATER_THAN;
|
|
break;
|
|
}
|
|
case TOK_LESS_EQ:
|
|
{
|
|
opcode = VM_OP_LESS_OR_EQUAL_THAN;
|
|
break;
|
|
}
|
|
case TOK_GREATER_EQ:
|
|
{
|
|
opcode = VM_OP_GREATER_OR_EQUAL_THAN;
|
|
break;
|
|
}
|
|
case TOK_KW_INSTANCEOF:
|
|
{
|
|
opcode = VM_OP_INSTANCEOF;
|
|
break;
|
|
}
|
|
case TOK_KW_IN:
|
|
{
|
|
opcode = VM_OP_IN;
|
|
break;
|
|
}
|
|
case TOK_DOUBLE_EQ:
|
|
{
|
|
opcode = VM_OP_EQUAL_VALUE;
|
|
break;
|
|
}
|
|
case TOK_NOT_EQ:
|
|
{
|
|
opcode = VM_OP_NOT_EQUAL_VALUE;
|
|
break;
|
|
}
|
|
case TOK_TRIPLE_EQ:
|
|
{
|
|
opcode = VM_OP_EQUAL_VALUE_TYPE;
|
|
break;
|
|
}
|
|
case TOK_NOT_DOUBLE_EQ:
|
|
{
|
|
opcode = VM_OP_NOT_EQUAL_VALUE_TYPE;
|
|
break;
|
|
}
|
|
case TOK_AND:
|
|
{
|
|
opcode = VM_OP_B_AND;
|
|
break;
|
|
}
|
|
case TOK_XOR:
|
|
{
|
|
opcode = VM_OP_B_XOR;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
JERRY_ASSERT (tt == TOK_OR);
|
|
|
|
opcode = VM_OP_B_OR;
|
|
break;
|
|
}
|
|
}
|
|
|
|
jsp_operand_t dst, op1, op2;
|
|
|
|
if (!state_p->is_value_based_reference)
|
|
{
|
|
dump_get_value_for_state_if_const (ctx_p, state_p);
|
|
}
|
|
|
|
if (!substate_p->is_value_based_reference)
|
|
{
|
|
dump_get_value_for_state_if_const (ctx_p, substate_p);
|
|
}
|
|
|
|
if (!state_p->is_value_based_reference
|
|
&& !substate_p->is_value_based_reference)
|
|
{
|
|
if (dump_get_value_for_state_if_ref (ctx_p, state_p, false, true)
|
|
|| dump_get_value_for_state_if_ref (ctx_p, substate_p, false, true))
|
|
{
|
|
dump_get_value_for_prev_states (ctx_p, state_p);
|
|
}
|
|
|
|
op1 = state_p->u.expression.operand;
|
|
op2 = substate_p->u.expression.operand;
|
|
|
|
if (is_try_combine_with_assignment)
|
|
{
|
|
dst = prev_state_p->u.expression.operand;
|
|
|
|
is_combined_with_assignment = true;
|
|
}
|
|
else if (op1.is_register_operand ())
|
|
{
|
|
dst = op1;
|
|
}
|
|
else
|
|
{
|
|
dst = tmp_operand ();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dump_get_value_if_ref (ctx_p, state_p, true);
|
|
dump_get_value_if_ref (ctx_p, substate_p, true);
|
|
|
|
if (is_try_combine_with_assignment)
|
|
{
|
|
dst = prev_state_p->u.expression.operand;
|
|
|
|
is_combined_with_assignment = true;
|
|
}
|
|
else
|
|
{
|
|
dst = state_p->u.expression.operand;
|
|
}
|
|
|
|
op1 = state_p->u.expression.operand;
|
|
op2 = substate_p->u.expression.operand;
|
|
}
|
|
|
|
JERRY_ASSERT (!state_p->is_value_based_reference);
|
|
state_p->u.expression.operand = dst;
|
|
|
|
dump_binary_op (ctx_p, opcode, dst, op1, op2);
|
|
|
|
return is_combined_with_assignment;
|
|
}
|
|
|
|
static lit_cpointer_t
|
|
jsp_get_prop_name_after_dot (void)
|
|
{
|
|
if (token_is (TOK_NAME))
|
|
{
|
|
return token_data_as_lit_cp ();
|
|
}
|
|
else if (is_keyword (lexer_get_token_type (tok)))
|
|
{
|
|
const char *s = lexer_token_type_to_string (lexer_get_token_type (tok));
|
|
literal_t lit = lit_find_or_create_literal_from_utf8_string ((lit_utf8_byte_t *) s,
|
|
(lit_utf8_size_t) strlen (s));
|
|
if (lit == NULL)
|
|
{
|
|
EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "Expected identifier");
|
|
}
|
|
|
|
return lit_cpointer_t::compress (lit);
|
|
}
|
|
else if (token_is (TOK_BOOL) || token_is (TOK_NULL))
|
|
{
|
|
lit_magic_string_id_t id = (token_is (TOK_NULL)
|
|
? LIT_MAGIC_STRING_NULL
|
|
: (tok.uid ? LIT_MAGIC_STRING_TRUE : LIT_MAGIC_STRING_FALSE));
|
|
literal_t lit = lit_find_or_create_literal_from_utf8_string (lit_get_magic_string_utf8 (id),
|
|
lit_get_magic_string_size (id));
|
|
|
|
return lit_cpointer_t::compress (lit);
|
|
}
|
|
else
|
|
{
|
|
EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "Expected identifier");
|
|
}
|
|
} /* jsp_get_prop_name_after_dot */
|
|
|
|
#ifdef CONFIG_PARSER_ENABLE_PARSE_TIME_BYTE_CODE_OPTIMIZER
|
|
/**
|
|
* Try to perform move (allocation) of variables' values on registers
|
|
*
|
|
* Note:
|
|
* The optimization is only applied to functions
|
|
*
|
|
* @return number of instructions removed from function's header
|
|
*/
|
|
static void
|
|
jsp_try_move_vars_to_regs (jsp_ctx_t *ctx_p,
|
|
jsp_state_t *state_p)
|
|
{
|
|
// TODO: pass to dumper
|
|
(void) ctx_p;
|
|
|
|
JERRY_ASSERT (state_p->state == JSP_STATE_SOURCE_ELEMENTS);
|
|
|
|
bytecode_data_header_t *bc_header_p = jsp_get_current_bytecode_header (ctx_p);
|
|
|
|
bool may_replace_vars_with_regs = bc_header_p->is_vars_and_args_to_regs_possible;
|
|
|
|
if (may_replace_vars_with_regs)
|
|
{
|
|
/* no subscopes, as no function declarations / eval etc. in the scope */
|
|
JERRY_ASSERT (bc_header_p->func_scopes_count == 0);
|
|
|
|
vm_instr_counter_t instr_pos = 0u;
|
|
|
|
const vm_instr_counter_t header_oc = instr_pos++;
|
|
op_meta header_opm = dumper_get_op_meta (ctx_p, header_oc);
|
|
JERRY_ASSERT (header_opm.op.op_idx == VM_OP_FUNC_EXPR_N || header_opm.op.op_idx == VM_OP_FUNC_DECL_N);
|
|
|
|
uint32_t args_num;
|
|
|
|
if (header_opm.op.op_idx == VM_OP_FUNC_EXPR_N)
|
|
{
|
|
args_num = header_opm.op.data.func_expr_n.arg_list;
|
|
}
|
|
else
|
|
{
|
|
args_num = header_opm.op.data.func_decl_n.arg_list;
|
|
}
|
|
|
|
dumper_start_move_of_vars_to_regs (ctx_p);
|
|
|
|
mem_cpointer_t *declarations_p = MEM_CP_GET_POINTER (mem_cpointer_t, bc_header_p->declarations_cp);
|
|
lit_cpointer_t *var_declarations_p = (lit_cpointer_t *) (declarations_p + bc_header_p->func_scopes_count);
|
|
|
|
/* remove declarations of variables with names equal to an argument's name */
|
|
uint32_t var_decl_index = 0;
|
|
while (var_decl_index < bc_header_p->var_decls_count)
|
|
{
|
|
lit_cpointer_t var_name_lit_cp = var_declarations_p[var_decl_index];
|
|
|
|
op_meta var_decl_opm;
|
|
var_decl_opm.op.op_idx = VM_OP_VAR_DECL;
|
|
var_decl_opm.lit_id[0] = var_name_lit_cp;
|
|
var_decl_opm.lit_id[1] = NOT_A_LITERAL;
|
|
var_decl_opm.lit_id[2] = NOT_A_LITERAL;
|
|
|
|
bool is_removed = false;
|
|
|
|
for (vm_instr_counter_t arg_index = instr_pos;
|
|
arg_index < instr_pos + args_num;
|
|
arg_index++)
|
|
{
|
|
op_meta meta_opm = dumper_get_op_meta (ctx_p, arg_index);
|
|
JERRY_ASSERT (meta_opm.op.op_idx == VM_OP_META);
|
|
|
|
if (meta_opm.lit_id[1].packed_value == var_name_lit_cp.packed_value)
|
|
{
|
|
var_declarations_p[var_decl_index] = NOT_A_LITERAL;
|
|
|
|
is_removed = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!is_removed)
|
|
{
|
|
if (dumper_try_replace_identifier_name_with_reg (ctx_p, bc_header_p, &var_decl_opm))
|
|
{
|
|
var_declarations_p[var_decl_index] = NOT_A_LITERAL;
|
|
}
|
|
}
|
|
|
|
var_decl_index++;
|
|
}
|
|
|
|
if (dumper_start_move_of_args_to_regs (ctx_p, args_num))
|
|
{
|
|
bc_header_p->is_args_moved_to_regs = true;
|
|
bc_header_p->is_no_lex_env = true;
|
|
|
|
dumper_rewrite_op_meta (ctx_p, header_oc, header_opm);
|
|
|
|
/*
|
|
* Mark duplicated arguments names as empty,
|
|
* leaving only last declaration for each duplicated
|
|
* argument name
|
|
*/
|
|
for (vm_instr_counter_t arg1_index = instr_pos;
|
|
arg1_index < instr_pos + args_num;
|
|
arg1_index++)
|
|
{
|
|
op_meta meta_opm1 = dumper_get_op_meta (ctx_p, arg1_index);
|
|
JERRY_ASSERT (meta_opm1.op.op_idx == VM_OP_META);
|
|
|
|
for (vm_instr_counter_t arg2_index = (vm_instr_counter_t) (arg1_index + 1u);
|
|
arg2_index < instr_pos + args_num;
|
|
arg2_index++)
|
|
{
|
|
op_meta meta_opm2 = dumper_get_op_meta (ctx_p, arg2_index);
|
|
JERRY_ASSERT (meta_opm2.op.op_idx == VM_OP_META);
|
|
|
|
if (meta_opm1.lit_id[1].packed_value == meta_opm2.lit_id[1].packed_value)
|
|
{
|
|
meta_opm1.op.data.meta.data_1 = VM_IDX_EMPTY;
|
|
meta_opm1.lit_id[1] = NOT_A_LITERAL;
|
|
|
|
dumper_rewrite_op_meta (ctx_p, arg1_index, meta_opm1);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (vm_instr_counter_t arg_instr_pos = instr_pos;
|
|
arg_instr_pos < instr_pos + args_num;
|
|
arg_instr_pos++)
|
|
{
|
|
op_meta meta_opm = dumper_get_op_meta (ctx_p, arg_instr_pos);
|
|
JERRY_ASSERT (meta_opm.op.op_idx == VM_OP_META);
|
|
|
|
opcode_meta_type meta_type = (opcode_meta_type) meta_opm.op.data.meta.type;
|
|
JERRY_ASSERT (meta_type == OPCODE_META_TYPE_VARG);
|
|
if (meta_opm.op.data.meta.data_1 == VM_IDX_EMPTY)
|
|
{
|
|
JERRY_ASSERT (meta_opm.lit_id[1].packed_value == NOT_A_LITERAL.packed_value);
|
|
|
|
dumper_alloc_reg_for_unused_arg (ctx_p);
|
|
}
|
|
else
|
|
{
|
|
/* the varg specifies argument name, and so should be a string literal */
|
|
JERRY_ASSERT (meta_opm.lit_id[1].packed_value != NOT_A_LITERAL.packed_value);
|
|
|
|
bool is_replaced = dumper_try_replace_identifier_name_with_reg (ctx_p, bc_header_p, &meta_opm);
|
|
JERRY_ASSERT (is_replaced);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif /* CONFIG_PARSER_ENABLE_PARSE_TIME_BYTE_CODE_OPTIMIZER */
|
|
|
|
static void
|
|
parse_expression_inside_parens_begin (jsp_ctx_t *ctx_p)
|
|
{
|
|
current_token_must_be_check_and_skip_it (ctx_p, TOK_OPEN_PAREN);
|
|
}
|
|
|
|
static void
|
|
parse_expression_inside_parens_end (jsp_ctx_t *ctx_p)
|
|
{
|
|
current_token_must_be_check_and_skip_it (ctx_p, TOK_CLOSE_PAREN);
|
|
}
|
|
|
|
static jsp_state_t *
|
|
jsp_find_unnamed_label (jsp_ctx_t *ctx_p,
|
|
bool is_label_for_break,
|
|
bool *out_is_simply_jumpable_p)
|
|
{
|
|
*out_is_simply_jumpable_p = true;
|
|
|
|
jsp_state_t *state_p = jsp_state_top (ctx_p);
|
|
while (state_p != NULL)
|
|
{
|
|
if (state_p->state == JSP_STATE_SOURCE_ELEMENTS)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (state_p->is_simply_jumpable_border)
|
|
{
|
|
*out_is_simply_jumpable_p = false;
|
|
}
|
|
|
|
bool is_iterational_stmt = (state_p->state == JSP_STATE_STAT_WHILE
|
|
|| state_p->state == JSP_STATE_STAT_DO_WHILE
|
|
|| state_p->state == JSP_STATE_STAT_FOR_IN
|
|
|| state_p->state == JSP_STATE_STAT_FOR_IN_EXPR
|
|
|| state_p->state == JSP_STATE_STAT_FOR_IN_FINISH
|
|
|| state_p->state == JSP_STATE_STAT_FOR_INCREMENT);
|
|
bool is_switch_stmt = (state_p->state == JSP_STATE_STAT_SWITCH_BRANCH_EXPR);
|
|
|
|
if ((is_switch_stmt && is_label_for_break) || is_iterational_stmt)
|
|
{
|
|
return state_p;
|
|
}
|
|
|
|
state_p = jsp_get_prev_state (state_p);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static jsp_state_t *
|
|
jsp_find_named_label (jsp_ctx_t *ctx_p,
|
|
lit_cpointer_t name_cp,
|
|
bool *out_is_simply_jumpable_p)
|
|
{
|
|
*out_is_simply_jumpable_p = true;
|
|
|
|
jsp_state_t *state_p = jsp_state_top (ctx_p);
|
|
jsp_state_t *last_non_named_label_state_p = NULL;
|
|
|
|
while (state_p != NULL)
|
|
{
|
|
if (state_p->state == JSP_STATE_SOURCE_ELEMENTS)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (state_p->is_simply_jumpable_border)
|
|
{
|
|
*out_is_simply_jumpable_p = false;
|
|
}
|
|
|
|
if (state_p->state != JSP_STATE_STAT_NAMED_LABEL)
|
|
{
|
|
last_non_named_label_state_p = state_p;
|
|
}
|
|
else if (state_p->u.named_label.name_cp.packed_value == name_cp.packed_value)
|
|
{
|
|
return last_non_named_label_state_p;
|
|
}
|
|
|
|
state_p = jsp_get_prev_state (state_p);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
insert_semicolon (jsp_ctx_t *ctx_p)
|
|
{
|
|
bool is_new_line_occured = lexer_is_preceded_by_newlines (tok);
|
|
bool is_close_brace_or_eof = (token_is (TOK_CLOSE_BRACE) || token_is (TOK_EOF));
|
|
|
|
if (!is_new_line_occured && !is_close_brace_or_eof)
|
|
{
|
|
if (token_is (TOK_SEMICOLON))
|
|
{
|
|
skip_token (ctx_p);
|
|
}
|
|
else if (!token_is (TOK_EOF))
|
|
{
|
|
EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "Expected either ';' or newline token");
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#define JSP_COMPLETE_STATEMENT_PARSE() \
|
|
do \
|
|
{ \
|
|
JERRY_ASSERT (substate_p == NULL); \
|
|
state_p->state = JSP_STATE_STAT_STATEMENT; \
|
|
JERRY_ASSERT (!state_p->is_completed); \
|
|
dumper_new_statement (ctx_p); \
|
|
} \
|
|
while (0)
|
|
|
|
#define JSP_PUSH_STATE_AND_STATEMENT_PARSE(s) \
|
|
do \
|
|
{ \
|
|
state_p->state = (s); \
|
|
jsp_start_statement_parse (ctx_p, JSP_STATE_STAT_EMPTY); \
|
|
dumper_new_statement (ctx_p); \
|
|
} \
|
|
while (0)
|
|
|
|
#define JSP_FINISH_SUBEXPR() \
|
|
JERRY_ASSERT (substate_p == jsp_state_top (ctx_p)); \
|
|
substate_p = NULL; \
|
|
jsp_state_pop (ctx_p); \
|
|
JERRY_ASSERT (state_p == jsp_state_top (ctx_p));
|
|
|
|
#define JSP_ASSIGNMENT_EXPR_COMBINE() \
|
|
jsp_state_pop (ctx_p); \
|
|
state_p = jsp_state_top (ctx_p); \
|
|
JERRY_ASSERT (state_p->state == JSP_STATE_EXPR_LEFTHANDSIDE); \
|
|
JERRY_ASSERT (!state_p->is_need_retval); \
|
|
state_p->state = JSP_STATE_EXPR_ASSIGNMENT; \
|
|
state_p->u.expression.operand = empty_operand (); \
|
|
state_p->u.expression.token_type = TOK_EMPTY; \
|
|
state_p->is_completed = true;
|
|
|
|
/**
|
|
* Parse source element list
|
|
*/
|
|
static void __attr_noinline___
|
|
jsp_parse_source_element_list (jsp_ctx_t *ctx_p,
|
|
scope_type_t scope_type)
|
|
{
|
|
jsp_start_parse_source_element_list (ctx_p, MEM_CP_NULL, 0, scope_type);
|
|
|
|
while (true)
|
|
{
|
|
bool is_source_elements_list_end = false;
|
|
|
|
bool is_stmt_list_end = false, is_stmt_list_control_flow_exit_stmt_occured = false;
|
|
|
|
bool is_subexpr_end = false;
|
|
|
|
jsp_state_t *state_p = jsp_state_top (ctx_p), *substate_p = NULL;
|
|
|
|
if (state_p->state == state_p->req_state && state_p->is_completed)
|
|
{
|
|
if (jsp_is_stack_contains_exactly_one_element (ctx_p))
|
|
{
|
|
JERRY_ASSERT (state_p->state == JSP_STATE_SOURCE_ELEMENTS);
|
|
|
|
jsp_state_pop (ctx_p);
|
|
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
is_subexpr_end = (state_p->state > JSP_STATE_EXPR__BEGIN && state_p->state < JSP_STATE_EXPR__END);
|
|
|
|
if (is_subexpr_end)
|
|
{
|
|
substate_p = state_p;
|
|
state_p = jsp_get_prev_state (state_p);
|
|
}
|
|
else
|
|
{
|
|
if (state_p->req_state == JSP_STATE_SOURCE_ELEMENTS)
|
|
{
|
|
is_source_elements_list_end = true;
|
|
}
|
|
else if (state_p->req_state == JSP_STATE_STAT_STATEMENT_LIST)
|
|
{
|
|
is_stmt_list_end = true;
|
|
|
|
is_stmt_list_control_flow_exit_stmt_occured = state_p->is_stmt_list_control_flow_exit_stmt_occured;
|
|
}
|
|
|
|
JERRY_ASSERT (state_p->is_completed);
|
|
jsp_state_pop (ctx_p);
|
|
|
|
state_p = jsp_state_top (ctx_p);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Any expression production, if parse of the production is not stopped because required
|
|
* production type was reached, eventually becomes Expression production.
|
|
*
|
|
* Because there are no any expression production higher than Expression,
|
|
* its invalid to reach Expression production type if required production is lower
|
|
* (i.e. is not Expression production type).
|
|
*/
|
|
JERRY_ASSERT (!(state_p->state == JSP_STATE_EXPR_EXPRESSION && state_p->req_state != JSP_STATE_EXPR_EXPRESSION));
|
|
}
|
|
|
|
const bool in_allowed = !state_p->is_no_in_mode;
|
|
|
|
if (state_p->state == JSP_STATE_SOURCE_ELEMENTS)
|
|
{
|
|
scope_type_t scope_type = jsp_get_scope_type (ctx_p);
|
|
|
|
jsp_token_type_t end_token_type = (scope_type == SCOPE_TYPE_FUNCTION) ? TOK_CLOSE_BRACE : TOK_EOF;
|
|
|
|
if (token_is (end_token_type))
|
|
{
|
|
#ifdef CONFIG_PARSER_ENABLE_PARSE_TIME_BYTE_CODE_OPTIMIZER
|
|
if (jsp_is_dump_mode (ctx_p))
|
|
{
|
|
jsp_try_move_vars_to_regs (ctx_p, state_p);
|
|
}
|
|
else
|
|
{
|
|
scopes_tree scope_p = jsp_get_current_scopes_tree_node (ctx_p);
|
|
|
|
JERRY_ASSERT (!scope_p->is_vars_and_args_to_regs_possible);
|
|
|
|
/*
|
|
* We don't try to perform replacement of local variables with registers for global code, eval code.
|
|
*
|
|
* For global and eval code the replacement can be connected with side effects,
|
|
* that currently can only be figured out in runtime. For example, a variable
|
|
* can be redefined as accessor property of the Global object.
|
|
*/
|
|
bool is_vars_and_args_to_regs_possible = (jsp_get_scope_type (ctx_p) == SCOPE_TYPE_FUNCTION
|
|
&& !scope_p->ref_eval /* 'eval' can reference variables in a way,
|
|
* that can't be figured out through static
|
|
* analysis */
|
|
&& !scope_p->ref_arguments /* 'arguments' variable, if declared,
|
|
* should not be moved to a register,
|
|
* as it is currently declared in
|
|
* function's lexical environment
|
|
* (generally, the problem is the same,
|
|
* as with function's arguments) */
|
|
&& !scope_p->contains_with /* 'with' create new lexical environment
|
|
* and so can change way identifier
|
|
* is evaluated */
|
|
&& !scope_p->contains_try /* same for 'catch' */
|
|
&& !scope_p->contains_delete /* 'delete' handles variable's names,
|
|
* not values */
|
|
&& !scope_p->contains_functions); /* nested functions
|
|
* can reference variables
|
|
* of current function */
|
|
|
|
scope_p->is_vars_and_args_to_regs_possible = is_vars_and_args_to_regs_possible;
|
|
}
|
|
#endif /* CONFIG_PARSER_ENABLE_PARSE_TIME_BYTE_CODE_OPTIMIZER */
|
|
|
|
rewrite_reg_var_decl (ctx_p, state_p->u.source_elements.reg_var_decl_oc);
|
|
|
|
if (scope_type == SCOPE_TYPE_FUNCTION)
|
|
{
|
|
dump_ret (ctx_p);
|
|
|
|
if (jsp_is_dump_mode (ctx_p))
|
|
{
|
|
bytecode_data_header_t *bc_header_p = jsp_get_current_bytecode_header (ctx_p);
|
|
|
|
if (parser_show_instrs)
|
|
{
|
|
bc_print_instrs (bc_header_p);
|
|
}
|
|
|
|
mem_cpointer_t parent_bc_header_cp = state_p->u.source_elements.u.parent_bc_header_cp;
|
|
bytecode_data_header_t *parent_bc_header_p = MEM_CP_GET_NON_NULL_POINTER (bytecode_data_header_t,
|
|
parent_bc_header_cp);
|
|
|
|
jsp_set_current_bytecode_header (ctx_p, parent_bc_header_p);
|
|
}
|
|
else
|
|
{
|
|
scopes_tree parent_scope_p = MEM_CP_GET_POINTER (scopes_tree_int,
|
|
state_p->u.source_elements.u.parent_scopes_tree_node_cp);
|
|
|
|
jsp_set_current_scopes_tree_node (ctx_p, parent_scope_p);
|
|
}
|
|
}
|
|
else if (scope_type == SCOPE_TYPE_GLOBAL)
|
|
{
|
|
dump_ret (ctx_p);
|
|
}
|
|
else
|
|
{
|
|
JERRY_ASSERT (scope_type == SCOPE_TYPE_EVAL);
|
|
|
|
dump_retval (ctx_p, jsp_operand_t::make_reg_operand (VM_REG_SPECIAL_EVAL_RET));
|
|
}
|
|
|
|
jsp_set_scope_type (ctx_p, state_p->u.source_elements.parent_scope_type);
|
|
jsp_set_processed_child_scopes_counter (ctx_p,
|
|
state_p->u.source_elements.parent_scope_child_scopes_counter);
|
|
|
|
dumper_restore_reg_alloc_ctx (ctx_p,
|
|
state_p->u.source_elements.saved_reg_next,
|
|
state_p->u.source_elements.saved_reg_max_for_temps,
|
|
true);
|
|
|
|
state_p->is_completed = true;
|
|
}
|
|
else
|
|
{
|
|
JSP_PUSH_STATE_AND_STATEMENT_PARSE (JSP_STATE_SOURCE_ELEMENTS);
|
|
}
|
|
}
|
|
else if (state_p->state == JSP_STATE_EXPR_EMPTY)
|
|
{
|
|
rescan_regexp_token (ctx_p);
|
|
|
|
/* no subexpressions can occur, as expression parse is just started */
|
|
JERRY_ASSERT (!is_subexpr_end);
|
|
JERRY_ASSERT (!state_p->is_completed);
|
|
|
|
jsp_token_type_t tt = lexer_get_token_type (tok);
|
|
if ((tt >= TOKEN_TYPE__UNARY_BEGIN
|
|
&& tt <= TOKEN_TYPE__UNARY_END)
|
|
|| tt == TOK_KW_DELETE
|
|
|| tt == TOK_KW_VOID
|
|
|| tt == TOK_KW_TYPEOF)
|
|
{
|
|
/* UnaryExpression */
|
|
state_p->state = JSP_STATE_EXPR_UNARY;
|
|
state_p->u.expression.token_type = tt;
|
|
|
|
if (!jsp_is_dump_mode (ctx_p))
|
|
{
|
|
if (tt == TOK_KW_DELETE)
|
|
{
|
|
scopes_tree_set_contains_delete (jsp_get_current_scopes_tree_node (ctx_p));
|
|
}
|
|
}
|
|
|
|
jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_UNARY, in_allowed);
|
|
}
|
|
else if (token_is (TOK_KW_FUNCTION))
|
|
{
|
|
/* FunctionExpression */
|
|
state_p->state = JSP_STATE_EXPR_FUNCTION;
|
|
}
|
|
else if (token_is (TOK_OPEN_SQUARE))
|
|
{
|
|
dump_get_value_for_prev_states (ctx_p, state_p);
|
|
|
|
/* ArrayLiteral */
|
|
vm_idx_t reg_alloc_saved_state = dumper_save_reg_alloc_counter (ctx_p);
|
|
|
|
state_p->u.expression.operand = tmp_operand ();
|
|
vm_instr_counter_t varg_header_pos = dump_varg_header_for_rewrite (ctx_p,
|
|
VARG_ARRAY_DECL,
|
|
state_p->u.expression.operand,
|
|
empty_operand ());
|
|
|
|
vm_idx_t reg_alloc_saved_state1 = dumper_save_reg_alloc_counter (ctx_p);
|
|
|
|
dumper_restore_reg_alloc_counter (ctx_p, reg_alloc_saved_state);
|
|
|
|
state_p->state = JSP_STATE_EXPR_ARRAY_LITERAL;
|
|
state_p->is_list_in_process = true;
|
|
state_p->u.expression.u.varg_sequence.list_length = 0;
|
|
state_p->u.expression.u.varg_sequence.reg_alloc_saved_state1 = reg_alloc_saved_state1;
|
|
state_p->u.expression.u.varg_sequence.header_pos = varg_header_pos;
|
|
}
|
|
else if (token_is (TOK_OPEN_BRACE))
|
|
{
|
|
dump_get_value_for_prev_states (ctx_p, state_p);
|
|
|
|
/* ObjectLiteral */
|
|
vm_idx_t reg_alloc_saved_state = dumper_save_reg_alloc_counter (ctx_p);
|
|
|
|
state_p->u.expression.operand = tmp_operand ();
|
|
vm_instr_counter_t varg_header_pos = dump_varg_header_for_rewrite (ctx_p,
|
|
VARG_OBJ_DECL,
|
|
state_p->u.expression.operand,
|
|
empty_operand ());
|
|
|
|
vm_idx_t reg_alloc_saved_state1 = dumper_save_reg_alloc_counter (ctx_p);
|
|
|
|
dumper_restore_reg_alloc_counter (ctx_p, reg_alloc_saved_state);
|
|
|
|
if (!jsp_is_dump_mode (ctx_p))
|
|
{
|
|
jsp_early_error_start_checking_of_prop_names ();
|
|
}
|
|
|
|
state_p->state = JSP_STATE_EXPR_OBJECT_LITERAL;
|
|
state_p->is_list_in_process = true;
|
|
state_p->u.expression.u.varg_sequence.list_length = 0;
|
|
state_p->u.expression.u.varg_sequence.reg_alloc_saved_state1 = reg_alloc_saved_state1;
|
|
state_p->u.expression.u.varg_sequence.header_pos = varg_header_pos;
|
|
}
|
|
else
|
|
{
|
|
/* MemberExpression (PrimaryExpression is immediately promoted to MemberExpression) */
|
|
state_p->state = JSP_STATE_EXPR_MEMBER;
|
|
|
|
switch (lexer_get_token_type (tok))
|
|
{
|
|
case TOK_OPEN_PAREN:
|
|
{
|
|
state_p->u.expression.token_type = TOK_OPEN_PAREN;
|
|
|
|
jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, true);
|
|
|
|
break;
|
|
}
|
|
case TOK_KW_THIS:
|
|
{
|
|
state_p->u.expression.operand = jsp_operand_t::make_this_operand ();
|
|
break;
|
|
}
|
|
case TOK_KW_NEW:
|
|
{
|
|
state_p->state = JSP_STATE_EXPR_MEMBER;
|
|
state_p->u.expression.token_type = TOK_KW_NEW;
|
|
|
|
jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_MEMBER, true);
|
|
break;
|
|
}
|
|
case TOK_NAME:
|
|
{
|
|
if (!jsp_is_dump_mode (ctx_p))
|
|
{
|
|
if (lit_literal_equal_type_cstr (lit_get_literal_by_cp (token_data_as_lit_cp ()), "arguments"))
|
|
{
|
|
scopes_tree_set_arguments_used (jsp_get_current_scopes_tree_node (ctx_p));
|
|
}
|
|
|
|
if (lit_literal_equal_type_cstr (lit_get_literal_by_cp (token_data_as_lit_cp ()), "eval"))
|
|
{
|
|
scopes_tree_set_eval_used (jsp_get_current_scopes_tree_node (ctx_p));
|
|
}
|
|
}
|
|
|
|
state_p->u.expression.operand = jsp_operand_t::make_identifier_operand (token_data_as_lit_cp ());
|
|
|
|
break;
|
|
}
|
|
case TOK_REGEXP:
|
|
{
|
|
state_p->u.expression.operand = jsp_operand_t::make_regexp_lit_operand (token_data_as_lit_cp ());
|
|
break;
|
|
}
|
|
case TOK_NULL:
|
|
{
|
|
state_p->u.expression.operand = jsp_operand_t::make_simple_value_operand (ECMA_SIMPLE_VALUE_NULL);
|
|
break;
|
|
}
|
|
case TOK_BOOL:
|
|
{
|
|
ecma_simple_value_t simple_value = (bool) token_data () ? ECMA_SIMPLE_VALUE_TRUE : ECMA_SIMPLE_VALUE_FALSE;
|
|
state_p->u.expression.operand = jsp_operand_t::make_simple_value_operand (simple_value);
|
|
break;
|
|
}
|
|
case TOK_SMALL_INT:
|
|
{
|
|
state_p->u.expression.operand = jsp_operand_t::make_smallint_operand ((uint8_t) token_data ());
|
|
break;
|
|
}
|
|
case TOK_NUMBER:
|
|
{
|
|
state_p->u.expression.operand = jsp_operand_t::make_number_lit_operand (token_data_as_lit_cp ());
|
|
break;
|
|
}
|
|
case TOK_STRING:
|
|
{
|
|
state_p->u.expression.operand = jsp_operand_t::make_string_lit_operand (token_data_as_lit_cp ());
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
EMIT_ERROR_VARG (JSP_EARLY_ERROR_SYNTAX,
|
|
"Unknown token %s",
|
|
lexer_token_type_to_string (lexer_get_token_type (tok)));
|
|
}
|
|
}
|
|
}
|
|
|
|
skip_token (ctx_p);
|
|
}
|
|
else if (state_p->state == JSP_STATE_EXPR_FUNCTION)
|
|
{
|
|
JERRY_ASSERT (!state_p->is_completed);
|
|
|
|
if (is_source_elements_list_end)
|
|
{
|
|
jsp_finish_parse_function_scope (ctx_p);
|
|
|
|
state_p->state = JSP_STATE_EXPR_MEMBER;
|
|
}
|
|
else
|
|
{
|
|
jsp_operand_t name;
|
|
if (token_is (TOK_NAME))
|
|
{
|
|
name = jsp_operand_t::make_string_lit_operand (token_data_as_lit_cp ());
|
|
skip_token (ctx_p);
|
|
}
|
|
else
|
|
{
|
|
name = empty_operand ();
|
|
}
|
|
|
|
state_p->u.expression.operand = jsp_start_parse_function_scope (ctx_p, name, true, NULL);
|
|
}
|
|
}
|
|
else if (state_p->state == JSP_STATE_EXPR_DATA_PROP_DECL)
|
|
{
|
|
JERRY_ASSERT (!state_p->is_completed);
|
|
|
|
if (is_subexpr_end)
|
|
{
|
|
JERRY_ASSERT (substate_p->state == JSP_STATE_EXPR_ASSIGNMENT);
|
|
|
|
dump_get_value_if_ref (ctx_p, substate_p, true);
|
|
dump_get_value_for_state_if_const (ctx_p, substate_p);
|
|
|
|
jsp_operand_t prop_name = state_p->u.expression.operand;
|
|
jsp_operand_t value = substate_p->u.expression.operand;
|
|
|
|
JERRY_ASSERT (prop_name.is_string_lit_operand ());
|
|
|
|
dump_prop_name_and_value (ctx_p, prop_name, value);
|
|
|
|
if (!jsp_is_dump_mode (ctx_p))
|
|
{
|
|
jsp_early_error_add_prop_name (prop_name, PROP_DATA);
|
|
}
|
|
|
|
state_p->is_completed = true;
|
|
|
|
JSP_FINISH_SUBEXPR ();
|
|
}
|
|
else
|
|
{
|
|
JERRY_ASSERT (state_p->u.expression.operand.is_empty_operand ());
|
|
state_p->u.expression.operand = parse_property_name ();
|
|
skip_token (ctx_p);
|
|
|
|
JERRY_ASSERT (token_is (TOK_COLON));
|
|
skip_token (ctx_p);
|
|
|
|
jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_ASSIGNMENT, true);
|
|
}
|
|
}
|
|
else if (state_p->state == JSP_STATE_EXPR_ACCESSOR_PROP_DECL)
|
|
{
|
|
JERRY_ASSERT (!state_p->is_completed);
|
|
|
|
if (is_source_elements_list_end)
|
|
{
|
|
jsp_finish_parse_function_scope (ctx_p);
|
|
|
|
jsp_operand_t prop_name = state_p->u.expression.u.accessor_prop_decl.prop_name;
|
|
jsp_operand_t func = state_p->u.expression.operand;
|
|
bool is_setter = state_p->u.expression.u.accessor_prop_decl.is_setter;
|
|
|
|
if (is_setter)
|
|
{
|
|
dump_prop_setter_decl (ctx_p, prop_name, func);
|
|
}
|
|
else
|
|
{
|
|
dump_prop_getter_decl (ctx_p, prop_name, func);
|
|
}
|
|
|
|
state_p->is_completed = true;
|
|
}
|
|
else
|
|
{
|
|
bool is_setter;
|
|
|
|
current_token_must_be (TOK_NAME);
|
|
|
|
lit_cpointer_t lit_cp = token_data_as_lit_cp ();
|
|
|
|
skip_token (ctx_p);
|
|
|
|
if (lit_literal_equal_type_cstr (lit_get_literal_by_cp (lit_cp), "get"))
|
|
{
|
|
is_setter = false;
|
|
}
|
|
else if (lit_literal_equal_type_cstr (lit_get_literal_by_cp (lit_cp), "set"))
|
|
{
|
|
is_setter = true;
|
|
}
|
|
else
|
|
{
|
|
EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "Invalid property declaration");
|
|
}
|
|
|
|
jsp_operand_t prop_name = parse_property_name ();
|
|
skip_token (ctx_p);
|
|
|
|
if (!jsp_is_dump_mode (ctx_p))
|
|
{
|
|
jsp_early_error_add_prop_name (prop_name, is_setter ? PROP_SET : PROP_GET);
|
|
}
|
|
|
|
size_t formal_parameters_num;
|
|
const jsp_operand_t func = jsp_start_parse_function_scope (ctx_p,
|
|
empty_operand (),
|
|
true,
|
|
&formal_parameters_num);
|
|
|
|
size_t req_num_of_formal_parameters = is_setter ? 1 : 0;
|
|
|
|
if (req_num_of_formal_parameters != formal_parameters_num)
|
|
{
|
|
EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "Invalid number of formal parameters");
|
|
}
|
|
|
|
JERRY_ASSERT (state_p->u.expression.operand.is_empty_operand ());
|
|
state_p->u.expression.operand = func;
|
|
|
|
state_p->u.expression.u.accessor_prop_decl.prop_name = prop_name;
|
|
state_p->u.expression.u.accessor_prop_decl.is_setter = is_setter;
|
|
}
|
|
}
|
|
else if (state_p->state == JSP_STATE_EXPR_OBJECT_LITERAL)
|
|
{
|
|
JERRY_ASSERT (!state_p->is_completed);
|
|
JERRY_ASSERT (state_p->is_list_in_process);
|
|
|
|
if (is_subexpr_end)
|
|
{
|
|
JERRY_ASSERT (substate_p->state == JSP_STATE_EXPR_DATA_PROP_DECL
|
|
|| substate_p->state == JSP_STATE_EXPR_ACCESSOR_PROP_DECL);
|
|
|
|
state_p->u.expression.u.varg_sequence.list_length++;
|
|
|
|
dumper_restore_reg_alloc_counter (ctx_p, state_p->u.expression.u.varg_sequence.reg_alloc_saved_state2);
|
|
|
|
if (token_is (TOK_COMMA))
|
|
{
|
|
skip_token (ctx_p);
|
|
}
|
|
else
|
|
{
|
|
current_token_must_be (TOK_CLOSE_BRACE);
|
|
}
|
|
|
|
JSP_FINISH_SUBEXPR ();
|
|
}
|
|
|
|
if (token_is (TOK_CLOSE_BRACE))
|
|
{
|
|
if (!jsp_is_dump_mode (ctx_p))
|
|
{
|
|
jsp_early_error_check_for_duplication_of_prop_names (jsp_is_strict_mode (ctx_p), tok.loc);
|
|
}
|
|
|
|
skip_token (ctx_p);
|
|
|
|
uint32_t list_len = state_p->u.expression.u.varg_sequence.list_length;
|
|
vm_instr_counter_t header_pos = state_p->u.expression.u.varg_sequence.header_pos;
|
|
|
|
rewrite_varg_header_set_args_count (ctx_p, list_len, header_pos);
|
|
|
|
dumper_restore_reg_alloc_counter (ctx_p, state_p->u.expression.u.varg_sequence.reg_alloc_saved_state1);
|
|
|
|
state_p->state = JSP_STATE_EXPR_MEMBER;
|
|
state_p->is_list_in_process = false;
|
|
}
|
|
else
|
|
{
|
|
state_p->u.expression.u.varg_sequence.reg_alloc_saved_state2 = dumper_save_reg_alloc_counter (ctx_p);
|
|
|
|
locus start_pos = tok.loc;
|
|
skip_token (ctx_p);
|
|
|
|
jsp_state_expr_t expr_type;
|
|
if (token_is (TOK_COLON))
|
|
{
|
|
expr_type = JSP_STATE_EXPR_DATA_PROP_DECL;
|
|
}
|
|
else
|
|
{
|
|
expr_type = JSP_STATE_EXPR_ACCESSOR_PROP_DECL;
|
|
}
|
|
|
|
seek_token (ctx_p, start_pos);
|
|
|
|
jsp_push_new_expr_state (ctx_p, expr_type, expr_type, true);
|
|
}
|
|
}
|
|
else if (state_p->state == JSP_STATE_EXPR_ARRAY_LITERAL)
|
|
{
|
|
JERRY_ASSERT (!state_p->is_completed);
|
|
JERRY_ASSERT (state_p->is_list_in_process);
|
|
|
|
if (is_subexpr_end)
|
|
{
|
|
dump_get_value_if_ref (ctx_p, substate_p, true);
|
|
dump_get_value_for_state_if_const (ctx_p, substate_p);
|
|
|
|
dump_varg (ctx_p, substate_p->u.expression.operand);
|
|
|
|
JSP_FINISH_SUBEXPR ();
|
|
|
|
state_p->u.expression.u.varg_sequence.list_length++;
|
|
|
|
dumper_restore_reg_alloc_counter (ctx_p, state_p->u.expression.u.varg_sequence.reg_alloc_saved_state2);
|
|
|
|
if (token_is (TOK_COMMA))
|
|
{
|
|
skip_token (ctx_p);
|
|
}
|
|
else
|
|
{
|
|
current_token_must_be (TOK_CLOSE_SQUARE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (token_is (TOK_CLOSE_SQUARE))
|
|
{
|
|
skip_token (ctx_p);
|
|
|
|
uint32_t list_len = state_p->u.expression.u.varg_sequence.list_length;
|
|
vm_instr_counter_t header_pos = state_p->u.expression.u.varg_sequence.header_pos;
|
|
|
|
rewrite_varg_header_set_args_count (ctx_p, list_len, header_pos);
|
|
|
|
dumper_restore_reg_alloc_counter (ctx_p, state_p->u.expression.u.varg_sequence.reg_alloc_saved_state1);
|
|
|
|
state_p->state = JSP_STATE_EXPR_MEMBER;
|
|
state_p->is_list_in_process = false;
|
|
}
|
|
else if (token_is (TOK_COMMA))
|
|
{
|
|
while (token_is (TOK_COMMA))
|
|
{
|
|
skip_token (ctx_p);
|
|
|
|
vm_idx_t reg_alloc_saved_state = dumper_save_reg_alloc_counter (ctx_p);
|
|
|
|
jsp_operand_t reg = tmp_operand ();
|
|
dump_variable_assignment (ctx_p,
|
|
reg,
|
|
jsp_operand_t::make_simple_value_operand (ECMA_SIMPLE_VALUE_ARRAY_HOLE));
|
|
dump_varg (ctx_p, reg);
|
|
|
|
state_p->u.expression.u.varg_sequence.list_length++;
|
|
|
|
dumper_restore_reg_alloc_counter (ctx_p, reg_alloc_saved_state);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
state_p->u.expression.u.varg_sequence.reg_alloc_saved_state2 = dumper_save_reg_alloc_counter (ctx_p);
|
|
|
|
jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_ASSIGNMENT, true);
|
|
}
|
|
}
|
|
}
|
|
else if (state_p->state == JSP_STATE_EXPR_MEMBER)
|
|
{
|
|
if (state_p->is_completed)
|
|
{
|
|
if (token_is (TOK_OPEN_PAREN))
|
|
{
|
|
state_p->state = JSP_STATE_EXPR_CALL;
|
|
state_p->is_completed = false;
|
|
|
|
/* propagate to CallExpression */
|
|
state_p->state = JSP_STATE_EXPR_CALL;
|
|
}
|
|
else
|
|
{
|
|
/* propagate to LeftHandSideExpression */
|
|
state_p->state = JSP_STATE_EXPR_LEFTHANDSIDE;
|
|
JERRY_ASSERT (state_p->is_completed);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (is_subexpr_end)
|
|
{
|
|
if (state_p->is_list_in_process)
|
|
{
|
|
JERRY_ASSERT (state_p->u.expression.token_type == TOK_KW_NEW);
|
|
JERRY_ASSERT (substate_p->state == JSP_STATE_EXPR_ASSIGNMENT);
|
|
|
|
dump_get_value_if_ref (ctx_p, substate_p, true);
|
|
dump_get_value_for_state_if_const (ctx_p, substate_p);
|
|
|
|
dump_varg (ctx_p, substate_p->u.expression.operand);
|
|
|
|
JSP_FINISH_SUBEXPR ();
|
|
|
|
dumper_restore_reg_alloc_counter (ctx_p, state_p->u.expression.u.varg_sequence.reg_alloc_saved_state2);
|
|
|
|
state_p->u.expression.u.varg_sequence.list_length++;
|
|
|
|
if (token_is (TOK_CLOSE_PAREN))
|
|
{
|
|
state_p->u.expression.token_type = TOK_EMPTY;
|
|
state_p->is_list_in_process = false;
|
|
|
|
uint32_t list_len = state_p->u.expression.u.varg_sequence.list_length;
|
|
vm_instr_counter_t header_pos = state_p->u.expression.u.varg_sequence.header_pos;
|
|
|
|
jsp_finish_construct_dump (ctx_p,
|
|
list_len,
|
|
header_pos,
|
|
state_p->u.expression.u.varg_sequence.reg_alloc_saved_state1);
|
|
}
|
|
else
|
|
{
|
|
current_token_must_be (TOK_COMMA);
|
|
|
|
jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_ASSIGNMENT, true);
|
|
|
|
state_p->u.expression.u.varg_sequence.reg_alloc_saved_state2 = dumper_save_reg_alloc_counter (ctx_p);
|
|
}
|
|
|
|
skip_token (ctx_p);
|
|
}
|
|
else if (state_p->u.expression.token_type == TOK_OPEN_PAREN)
|
|
{
|
|
JERRY_ASSERT (state_p->u.expression.operand.is_empty_operand ());
|
|
|
|
state_p->u.expression.operand = substate_p->u.expression.operand;
|
|
state_p->u.expression.prop_name_operand = substate_p->u.expression.prop_name_operand;
|
|
state_p->is_value_based_reference = substate_p->is_value_based_reference;
|
|
|
|
state_p->u.expression.token_type = TOK_EMPTY;
|
|
|
|
JSP_FINISH_SUBEXPR ();
|
|
|
|
current_token_must_be_check_and_skip_it (ctx_p, TOK_CLOSE_PAREN);
|
|
}
|
|
else if (state_p->u.expression.token_type == TOK_KW_NEW)
|
|
{
|
|
JERRY_ASSERT (substate_p->state == JSP_STATE_EXPR_MEMBER);
|
|
JERRY_ASSERT (state_p->u.expression.operand.is_empty_operand ());
|
|
JERRY_ASSERT (!substate_p->u.expression.operand.is_empty_operand ());
|
|
|
|
state_p->u.expression.operand = substate_p->u.expression.operand;
|
|
state_p->u.expression.prop_name_operand = substate_p->u.expression.prop_name_operand;
|
|
state_p->is_value_based_reference = substate_p->is_value_based_reference;
|
|
|
|
JSP_FINISH_SUBEXPR ();
|
|
|
|
bool is_arg_list_implicit = true;
|
|
bool is_arg_list_empty = true;
|
|
|
|
if (token_is (TOK_OPEN_PAREN))
|
|
{
|
|
skip_token (ctx_p);
|
|
|
|
is_arg_list_implicit = false;
|
|
|
|
if (token_is (TOK_CLOSE_PAREN))
|
|
{
|
|
skip_token (ctx_p);
|
|
}
|
|
else
|
|
{
|
|
is_arg_list_empty = false;
|
|
}
|
|
}
|
|
|
|
if (!is_arg_list_implicit && !is_arg_list_empty)
|
|
{
|
|
dump_get_value_for_prev_states (ctx_p, state_p);
|
|
|
|
vm_idx_t reg_alloc_saved_state1;
|
|
vm_instr_counter_t header_pos = jsp_start_construct_dump (ctx_p, state_p, ®_alloc_saved_state1);
|
|
|
|
state_p->is_list_in_process = true;
|
|
state_p->u.expression.u.varg_sequence.list_length = 0;
|
|
state_p->u.expression.u.varg_sequence.header_pos = header_pos;
|
|
state_p->u.expression.u.varg_sequence.reg_alloc_saved_state1 = reg_alloc_saved_state1;
|
|
state_p->u.expression.u.varg_sequence.reg_alloc_saved_state2 = dumper_save_reg_alloc_counter (ctx_p);
|
|
|
|
jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_ASSIGNMENT, true);
|
|
}
|
|
else
|
|
{
|
|
vm_idx_t reg_alloc_saved_state1;
|
|
vm_instr_counter_t header_pos = jsp_start_construct_dump (ctx_p, state_p, ®_alloc_saved_state1);
|
|
|
|
state_p->u.expression.token_type = TOK_EMPTY;
|
|
|
|
if (is_arg_list_implicit)
|
|
{
|
|
state_p->state = JSP_STATE_EXPR_MEMBER;
|
|
state_p->is_completed = true;
|
|
}
|
|
|
|
jsp_finish_construct_dump (ctx_p, 0, header_pos, reg_alloc_saved_state1);
|
|
state_p->u.expression.prop_name_operand = empty_operand ();
|
|
state_p->is_value_based_reference = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
JERRY_ASSERT (state_p->u.expression.token_type == TOK_OPEN_SQUARE);
|
|
state_p->u.expression.token_type = TOK_EMPTY;
|
|
|
|
current_token_must_be_check_and_skip_it (ctx_p, TOK_CLOSE_SQUARE);
|
|
|
|
dump_get_value_if_ref (ctx_p, substate_p, true);
|
|
dump_get_value_for_state_if_const (ctx_p, substate_p);
|
|
|
|
state_p->u.expression.prop_name_operand = substate_p->u.expression.operand;
|
|
state_p->is_value_based_reference = true;
|
|
|
|
JSP_FINISH_SUBEXPR ();
|
|
}
|
|
}
|
|
else if (token_is (TOK_OPEN_SQUARE))
|
|
{
|
|
skip_token (ctx_p);
|
|
|
|
state_p->u.expression.token_type = TOK_OPEN_SQUARE;
|
|
|
|
dump_get_value_if_ref (ctx_p, state_p, true);
|
|
dump_get_value_for_state_if_const (ctx_p, state_p);
|
|
|
|
jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, true);
|
|
}
|
|
else if (token_is (TOK_DOT))
|
|
{
|
|
skip_token (ctx_p);
|
|
|
|
lit_cpointer_t prop_name = jsp_get_prop_name_after_dot ();
|
|
skip_token (ctx_p);
|
|
|
|
dump_get_value_if_ref (ctx_p, state_p, true);
|
|
dump_get_value_for_state_if_const (ctx_p, state_p);
|
|
|
|
state_p->u.expression.prop_name_operand = tmp_operand ();
|
|
dump_variable_assignment (ctx_p,
|
|
state_p->u.expression.prop_name_operand,
|
|
jsp_operand_t::make_string_lit_operand (prop_name));
|
|
|
|
state_p->is_value_based_reference = true;
|
|
}
|
|
else
|
|
{
|
|
state_p->is_completed = true;
|
|
}
|
|
}
|
|
}
|
|
else if (state_p->state == JSP_STATE_EXPR_CALL)
|
|
{
|
|
JERRY_ASSERT (!state_p->is_completed);
|
|
|
|
if (is_subexpr_end)
|
|
{
|
|
if (state_p->is_list_in_process)
|
|
{
|
|
JERRY_ASSERT (substate_p->state == JSP_STATE_EXPR_ASSIGNMENT);
|
|
|
|
dump_get_value_if_ref (ctx_p, substate_p, true);
|
|
dump_get_value_for_state_if_const (ctx_p, substate_p);
|
|
|
|
dump_varg (ctx_p, substate_p->u.expression.operand);
|
|
|
|
JSP_FINISH_SUBEXPR ();
|
|
|
|
dumper_restore_reg_alloc_counter (ctx_p, state_p->u.expression.u.varg_sequence.reg_alloc_saved_state2);
|
|
|
|
state_p->u.expression.u.varg_sequence.list_length++;
|
|
|
|
if (token_is (TOK_CLOSE_PAREN))
|
|
{
|
|
state_p->is_list_in_process = false;
|
|
|
|
uint32_t list_len = state_p->u.expression.u.varg_sequence.list_length;
|
|
vm_instr_counter_t header_pos = state_p->u.expression.u.varg_sequence.header_pos;
|
|
|
|
jsp_finish_call_dump (ctx_p,
|
|
list_len,
|
|
header_pos,
|
|
state_p->u.expression.u.varg_sequence.reg_alloc_saved_state1);
|
|
state_p->u.expression.prop_name_operand = empty_operand ();
|
|
state_p->is_value_based_reference = false;
|
|
}
|
|
else
|
|
{
|
|
current_token_must_be (TOK_COMMA);
|
|
|
|
jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_ASSIGNMENT, true);
|
|
|
|
state_p->u.expression.u.varg_sequence.reg_alloc_saved_state2 = dumper_save_reg_alloc_counter (ctx_p);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
JERRY_ASSERT (state_p->u.expression.token_type == TOK_OPEN_SQUARE);
|
|
state_p->u.expression.token_type = TOK_EMPTY;
|
|
|
|
current_token_must_be (TOK_CLOSE_SQUARE);
|
|
|
|
dump_get_value_if_ref (ctx_p, substate_p, true);
|
|
dump_get_value_for_state_if_const (ctx_p, substate_p);
|
|
|
|
state_p->u.expression.prop_name_operand = substate_p->u.expression.operand;
|
|
state_p->is_value_based_reference = true;
|
|
|
|
JSP_FINISH_SUBEXPR ();
|
|
}
|
|
|
|
skip_token (ctx_p);
|
|
}
|
|
else
|
|
{
|
|
if (token_is (TOK_OPEN_PAREN))
|
|
{
|
|
skip_token (ctx_p);
|
|
|
|
dump_get_value_for_prev_states (ctx_p, state_p);
|
|
|
|
vm_idx_t reg_alloc_saved_state1;
|
|
|
|
vm_instr_counter_t header_pos = jsp_start_call_dump (ctx_p, state_p, ®_alloc_saved_state1);
|
|
|
|
if (token_is (TOK_CLOSE_PAREN))
|
|
{
|
|
skip_token (ctx_p);
|
|
|
|
jsp_finish_call_dump (ctx_p, 0, header_pos, reg_alloc_saved_state1);
|
|
state_p->u.expression.prop_name_operand = empty_operand ();
|
|
state_p->is_value_based_reference = false;
|
|
}
|
|
else
|
|
{
|
|
state_p->is_list_in_process = true;
|
|
state_p->u.expression.u.varg_sequence.list_length = 0;
|
|
state_p->u.expression.u.varg_sequence.header_pos = header_pos;
|
|
state_p->u.expression.u.varg_sequence.reg_alloc_saved_state1 = reg_alloc_saved_state1;
|
|
state_p->u.expression.u.varg_sequence.reg_alloc_saved_state2 = dumper_save_reg_alloc_counter (ctx_p);
|
|
|
|
jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_ASSIGNMENT, true);
|
|
}
|
|
}
|
|
else if (token_is (TOK_OPEN_SQUARE))
|
|
{
|
|
skip_token (ctx_p);
|
|
|
|
state_p->u.expression.token_type = TOK_OPEN_SQUARE;
|
|
dump_get_value_if_ref (ctx_p, state_p, true);
|
|
dump_get_value_for_state_if_const (ctx_p, state_p);
|
|
|
|
jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, true);
|
|
}
|
|
else if (token_is (TOK_DOT))
|
|
{
|
|
skip_token (ctx_p);
|
|
|
|
lit_cpointer_t prop_name = jsp_get_prop_name_after_dot ();
|
|
skip_token (ctx_p);
|
|
|
|
dump_get_value_if_ref (ctx_p, state_p, true);
|
|
dump_get_value_for_state_if_const (ctx_p, state_p);
|
|
|
|
state_p->u.expression.prop_name_operand = tmp_operand ();
|
|
dump_variable_assignment (ctx_p,
|
|
state_p->u.expression.prop_name_operand,
|
|
jsp_operand_t::make_string_lit_operand (prop_name));
|
|
|
|
state_p->is_value_based_reference = true;
|
|
}
|
|
else
|
|
{
|
|
state_p->state = JSP_STATE_EXPR_LEFTHANDSIDE;
|
|
state_p->is_completed = true;
|
|
}
|
|
}
|
|
}
|
|
else if (state_p->state == JSP_STATE_EXPR_LEFTHANDSIDE)
|
|
{
|
|
JERRY_ASSERT (state_p->is_completed);
|
|
|
|
if (is_subexpr_end)
|
|
{
|
|
jsp_token_type_t tt = state_p->u.expression.token_type;
|
|
JERRY_ASSERT (tt >= TOKEN_TYPE__ASSIGNMENTS_BEGIN && tt <= TOKEN_TYPE__ASSIGNMENTS_END);
|
|
|
|
dump_get_value_for_prev_states (ctx_p, state_p);
|
|
dump_get_value_if_ref (ctx_p, substate_p, true);
|
|
|
|
if (tt == TOK_EQ)
|
|
{
|
|
if (state_p->is_need_retval)
|
|
{
|
|
dump_get_value_if_ref (ctx_p, substate_p, false);
|
|
dump_get_value_for_state_if_const (ctx_p, substate_p);
|
|
}
|
|
|
|
if (state_p->is_value_based_reference)
|
|
{
|
|
dump_get_value_for_state_if_const (ctx_p, substate_p);
|
|
|
|
dump_prop_setter (ctx_p,
|
|
state_p->u.expression.operand,
|
|
state_p->u.expression.prop_name_operand,
|
|
substate_p->u.expression.operand);
|
|
|
|
state_p->u.expression.prop_name_operand = empty_operand ();
|
|
state_p->is_value_based_reference = false;
|
|
}
|
|
else
|
|
{
|
|
dump_variable_assignment (ctx_p, state_p->u.expression.operand, substate_p->u.expression.operand);
|
|
}
|
|
|
|
state_p->u.expression.operand = substate_p->u.expression.operand;
|
|
|
|
if (state_p->is_need_retval)
|
|
{
|
|
JERRY_ASSERT (state_p->u.expression.operand.is_register_operand ());
|
|
}
|
|
else
|
|
{
|
|
state_p->u.expression.operand = empty_operand ();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
vm_op_t opcode;
|
|
|
|
if (tt == TOK_MULT_EQ)
|
|
{
|
|
opcode = VM_OP_MULTIPLICATION;
|
|
}
|
|
else if (tt == TOK_DIV_EQ)
|
|
{
|
|
opcode = VM_OP_DIVISION;
|
|
}
|
|
else if (tt == TOK_MOD_EQ)
|
|
{
|
|
opcode = VM_OP_REMAINDER;
|
|
}
|
|
else if (tt == TOK_PLUS_EQ)
|
|
{
|
|
opcode = VM_OP_ADDITION;
|
|
}
|
|
else if (tt == TOK_MINUS_EQ)
|
|
{
|
|
opcode = VM_OP_SUBSTRACTION;
|
|
}
|
|
else if (tt == TOK_LSHIFT_EQ)
|
|
{
|
|
opcode = VM_OP_B_SHIFT_LEFT;
|
|
}
|
|
else if (tt == TOK_RSHIFT_EQ)
|
|
{
|
|
opcode = VM_OP_B_SHIFT_RIGHT;
|
|
}
|
|
else if (tt == TOK_RSHIFT_EX_EQ)
|
|
{
|
|
opcode = VM_OP_B_SHIFT_URIGHT;
|
|
}
|
|
else if (tt == TOK_AND_EQ)
|
|
{
|
|
opcode = VM_OP_B_AND;
|
|
}
|
|
else if (tt == TOK_XOR_EQ)
|
|
{
|
|
opcode = VM_OP_B_XOR;
|
|
}
|
|
else
|
|
{
|
|
JERRY_ASSERT (tt == TOK_OR_EQ);
|
|
|
|
opcode = VM_OP_B_OR;
|
|
}
|
|
|
|
dump_get_value_for_state_if_const (ctx_p, substate_p);
|
|
|
|
if (state_p->is_value_based_reference)
|
|
{
|
|
jsp_operand_t reg = tmp_operand ();
|
|
|
|
dump_prop_getter (ctx_p, reg, state_p->u.expression.operand, state_p->u.expression.prop_name_operand);
|
|
|
|
dump_binary_op (ctx_p, opcode, reg, reg, substate_p->u.expression.operand);
|
|
|
|
dump_prop_setter (ctx_p,
|
|
state_p->u.expression.operand,
|
|
state_p->u.expression.prop_name_operand,
|
|
reg);
|
|
|
|
state_p->u.expression.operand = reg;
|
|
state_p->u.expression.prop_name_operand = empty_operand ();
|
|
state_p->is_value_based_reference = false;
|
|
}
|
|
else if (state_p->is_need_retval)
|
|
{
|
|
jsp_operand_t reg = tmp_operand ();
|
|
|
|
dump_binary_op (ctx_p, opcode, reg, state_p->u.expression.operand, substate_p->u.expression.operand);
|
|
|
|
dump_variable_assignment (ctx_p, state_p->u.expression.operand, reg);
|
|
|
|
state_p->u.expression.operand = reg;
|
|
}
|
|
else
|
|
{
|
|
dump_binary_op (ctx_p, opcode, state_p->u.expression.operand,
|
|
state_p->u.expression.operand,
|
|
substate_p->u.expression.operand);
|
|
|
|
state_p->u.expression.operand = empty_operand ();
|
|
}
|
|
}
|
|
|
|
state_p->state = JSP_STATE_EXPR_ASSIGNMENT;
|
|
state_p->u.expression.token_type = TOK_EMPTY;
|
|
state_p->is_completed = true;
|
|
|
|
JSP_FINISH_SUBEXPR ();
|
|
}
|
|
else
|
|
{
|
|
JERRY_ASSERT (state_p->u.expression.token_type == TOK_EMPTY);
|
|
|
|
if (token_is (TOK_DOUBLE_PLUS)
|
|
&& !lexer_is_preceded_by_newlines (tok))
|
|
{
|
|
const jsp_operand_t reg = tmp_operand ();
|
|
|
|
if (state_p->is_value_based_reference)
|
|
{
|
|
const jsp_operand_t val = tmp_operand ();
|
|
|
|
dump_prop_getter (ctx_p, val, state_p->u.expression.operand, state_p->u.expression.prop_name_operand);
|
|
|
|
dump_unary_op (ctx_p, VM_OP_POST_INCR, reg, val);
|
|
|
|
dump_prop_setter (ctx_p, state_p->u.expression.operand, state_p->u.expression.prop_name_operand, val);
|
|
|
|
state_p->u.expression.operand = reg;
|
|
state_p->u.expression.prop_name_operand = empty_operand ();
|
|
state_p->is_value_based_reference = false;
|
|
}
|
|
else if (state_p->u.expression.operand.is_identifier_operand ())
|
|
{
|
|
if (!jsp_is_dump_mode (ctx_p))
|
|
{
|
|
jsp_early_error_check_for_eval_and_arguments_in_strict_mode (state_p->u.expression.operand,
|
|
jsp_is_strict_mode (ctx_p),
|
|
tok.loc);
|
|
}
|
|
|
|
dump_unary_op (ctx_p, VM_OP_POST_INCR, reg, state_p->u.expression.operand);
|
|
|
|
state_p->u.expression.operand = reg;
|
|
}
|
|
else
|
|
{
|
|
PARSE_ERROR (JSP_EARLY_ERROR_REFERENCE, "Invalid left-hand-side expression", tok.loc);
|
|
}
|
|
|
|
state_p->state = JSP_STATE_EXPR_UNARY;
|
|
JERRY_ASSERT (state_p->is_completed);
|
|
|
|
skip_token (ctx_p);
|
|
}
|
|
else if (token_is (TOK_DOUBLE_MINUS)
|
|
&& !lexer_is_preceded_by_newlines (tok))
|
|
{
|
|
const jsp_operand_t reg = tmp_operand ();
|
|
|
|
if (state_p->is_value_based_reference)
|
|
{
|
|
const jsp_operand_t val = tmp_operand ();
|
|
|
|
dump_prop_getter (ctx_p, val, state_p->u.expression.operand, state_p->u.expression.prop_name_operand);
|
|
|
|
dump_unary_op (ctx_p, VM_OP_POST_DECR, reg, val);
|
|
|
|
dump_prop_setter (ctx_p, state_p->u.expression.operand, state_p->u.expression.prop_name_operand, val);
|
|
|
|
state_p->u.expression.operand = reg;
|
|
state_p->u.expression.prop_name_operand = empty_operand ();
|
|
state_p->is_value_based_reference = false;
|
|
}
|
|
else if (state_p->u.expression.operand.is_identifier_operand ())
|
|
{
|
|
if (!jsp_is_dump_mode (ctx_p))
|
|
{
|
|
jsp_early_error_check_for_eval_and_arguments_in_strict_mode (state_p->u.expression.operand,
|
|
jsp_is_strict_mode (ctx_p),
|
|
tok.loc);
|
|
}
|
|
|
|
dump_unary_op (ctx_p, VM_OP_POST_DECR, reg, state_p->u.expression.operand);
|
|
|
|
state_p->u.expression.operand = reg;
|
|
}
|
|
else
|
|
{
|
|
PARSE_ERROR (JSP_EARLY_ERROR_REFERENCE, "Invalid left-hand-side expression", tok.loc);
|
|
}
|
|
|
|
state_p->state = JSP_STATE_EXPR_UNARY;
|
|
JERRY_ASSERT (state_p->is_completed);
|
|
|
|
skip_token (ctx_p);
|
|
}
|
|
else
|
|
{
|
|
jsp_token_type_t tt = lexer_get_token_type (tok);
|
|
|
|
if (tt >= TOKEN_TYPE__ASSIGNMENTS_BEGIN && tt <= TOKEN_TYPE__ASSIGNMENTS_END)
|
|
{
|
|
if (!state_p->is_value_based_reference)
|
|
{
|
|
if (!jsp_is_dump_mode (ctx_p))
|
|
{
|
|
if (!state_p->u.expression.operand.is_identifier_operand ())
|
|
{
|
|
PARSE_ERROR (JSP_EARLY_ERROR_REFERENCE, "Invalid left-hand-side expression", tok.loc);
|
|
}
|
|
else
|
|
{
|
|
jsp_early_error_check_for_eval_and_arguments_in_strict_mode (state_p->u.expression.operand,
|
|
jsp_is_strict_mode (ctx_p),
|
|
tok.loc);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* skip the assignment operator */
|
|
skip_token (ctx_p);
|
|
state_p->u.expression.token_type = tt;
|
|
|
|
jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_ASSIGNMENT, in_allowed);
|
|
}
|
|
else
|
|
{
|
|
state_p->state = JSP_STATE_EXPR_UNARY;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (state_p->state > JSP_STATE_EXPR__SIMPLE_BEGIN
|
|
&& state_p->state < JSP_STATE_EXPR__SIMPLE_END)
|
|
{
|
|
JERRY_STATIC_ASSERT (JSP_STATE_EXPR_MULTIPLICATIVE == JSP_STATE_EXPR_UNARY + 1u);
|
|
JERRY_STATIC_ASSERT (JSP_STATE_EXPR_ADDITIVE == JSP_STATE_EXPR_MULTIPLICATIVE + 1u);
|
|
JERRY_STATIC_ASSERT (JSP_STATE_EXPR_SHIFT == JSP_STATE_EXPR_ADDITIVE + 1u);
|
|
JERRY_STATIC_ASSERT (JSP_STATE_EXPR_RELATIONAL == JSP_STATE_EXPR_SHIFT + 1u);
|
|
JERRY_STATIC_ASSERT (JSP_STATE_EXPR_EQUALITY == JSP_STATE_EXPR_RELATIONAL + 1u);
|
|
JERRY_STATIC_ASSERT (JSP_STATE_EXPR_BITWISE_AND == JSP_STATE_EXPR_EQUALITY + 1u);
|
|
JERRY_STATIC_ASSERT (JSP_STATE_EXPR_BITWISE_XOR == JSP_STATE_EXPR_BITWISE_AND + 1u);
|
|
JERRY_STATIC_ASSERT (JSP_STATE_EXPR_BITWISE_OR == JSP_STATE_EXPR_BITWISE_XOR + 1u);
|
|
|
|
if (state_p->is_completed)
|
|
{
|
|
if (state_p->state == JSP_STATE_EXPR_BITWISE_OR)
|
|
{
|
|
state_p->state = JSP_STATE_EXPR_LOGICAL_AND;
|
|
|
|
state_p->u.expression.u.logical_and.rewrite_chain = MAX_OPCODES;
|
|
}
|
|
else
|
|
{
|
|
JERRY_ASSERT (state_p->state != state_p->req_state);
|
|
JERRY_ASSERT (state_p->state == JSP_STATE_EXPR_UNARY);
|
|
|
|
state_p->state = (jsp_state_expr_t) (state_p->state + 1u);
|
|
}
|
|
|
|
state_p->is_completed = false;
|
|
}
|
|
else if (is_subexpr_end)
|
|
{
|
|
bool is_combined_with_assignment;
|
|
|
|
if (state_p->state == JSP_STATE_EXPR_UNARY)
|
|
{
|
|
is_combined_with_assignment = jsp_dump_unary_op (ctx_p, state_p, substate_p);
|
|
}
|
|
else
|
|
{
|
|
is_combined_with_assignment = jsp_dump_binary_op (ctx_p, state_p, substate_p);
|
|
}
|
|
|
|
JSP_FINISH_SUBEXPR ();
|
|
|
|
if (is_combined_with_assignment)
|
|
{
|
|
JSP_ASSIGNMENT_EXPR_COMBINE ();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
JERRY_ASSERT (!state_p->is_completed);
|
|
|
|
|
|
jsp_state_expr_t new_state, subexpr_type;
|
|
|
|
bool is_simple = true;
|
|
|
|
jsp_token_type_t tt = lexer_get_token_type (tok);
|
|
|
|
if (tt >= TOKEN_TYPE__MULTIPLICATIVE_BEGIN && tt <= TOKEN_TYPE__MULTIPLICATIVE_END)
|
|
{
|
|
JERRY_ASSERT (state_p->state >= JSP_STATE_EXPR_UNARY && state_p->state <= JSP_STATE_EXPR_MULTIPLICATIVE);
|
|
|
|
new_state = JSP_STATE_EXPR_MULTIPLICATIVE;
|
|
subexpr_type = JSP_STATE_EXPR_UNARY;
|
|
}
|
|
else if (tt >= TOKEN_TYPE__ADDITIVE_BEGIN && tt <= TOKEN_TYPE__ADDITIVE_END)
|
|
{
|
|
JERRY_ASSERT (state_p->state >= JSP_STATE_EXPR_UNARY && state_p->state <= JSP_STATE_EXPR_ADDITIVE);
|
|
|
|
new_state = JSP_STATE_EXPR_ADDITIVE;
|
|
subexpr_type = JSP_STATE_EXPR_MULTIPLICATIVE;
|
|
}
|
|
else if (tt >= TOKEN_TYPE__SHIFT_BEGIN && tt <= TOKEN_TYPE__SHIFT_END)
|
|
{
|
|
JERRY_ASSERT (state_p->state >= JSP_STATE_EXPR_UNARY && state_p->state <= JSP_STATE_EXPR_SHIFT);
|
|
|
|
new_state = JSP_STATE_EXPR_SHIFT;
|
|
subexpr_type = JSP_STATE_EXPR_ADDITIVE;
|
|
}
|
|
else if ((tt >= TOKEN_TYPE__RELATIONAL_BEGIN && tt <= TOKEN_TYPE__RELATIONAL_END)
|
|
|| tt == TOK_KW_INSTANCEOF
|
|
|| (in_allowed && tt == TOK_KW_IN))
|
|
{
|
|
JERRY_ASSERT (state_p->state >= JSP_STATE_EXPR_UNARY && state_p->state <= JSP_STATE_EXPR_RELATIONAL);
|
|
|
|
new_state = JSP_STATE_EXPR_RELATIONAL;
|
|
subexpr_type = JSP_STATE_EXPR_SHIFT;
|
|
}
|
|
else if (tt >= TOKEN_TYPE__EQUALITY_BEGIN && tt <= TOKEN_TYPE__EQUALITY_END)
|
|
{
|
|
JERRY_ASSERT (state_p->state >= JSP_STATE_EXPR_UNARY && state_p->state <= JSP_STATE_EXPR_EQUALITY);
|
|
|
|
new_state = JSP_STATE_EXPR_EQUALITY;
|
|
subexpr_type = JSP_STATE_EXPR_RELATIONAL;
|
|
}
|
|
else if (tt == TOK_AND)
|
|
{
|
|
JERRY_ASSERT (state_p->state >= JSP_STATE_EXPR_UNARY && state_p->state <= JSP_STATE_EXPR_BITWISE_AND);
|
|
|
|
new_state = JSP_STATE_EXPR_BITWISE_AND;
|
|
subexpr_type = JSP_STATE_EXPR_EQUALITY;
|
|
}
|
|
else if (tt == TOK_XOR)
|
|
{
|
|
JERRY_ASSERT (state_p->state >= JSP_STATE_EXPR_UNARY && state_p->state <= JSP_STATE_EXPR_BITWISE_XOR);
|
|
|
|
new_state = JSP_STATE_EXPR_BITWISE_XOR;
|
|
subexpr_type = JSP_STATE_EXPR_BITWISE_AND;
|
|
}
|
|
else if (tt == TOK_OR)
|
|
{
|
|
JERRY_ASSERT (state_p->state >= JSP_STATE_EXPR_UNARY && state_p->state <= JSP_STATE_EXPR_BITWISE_OR);
|
|
|
|
new_state = JSP_STATE_EXPR_BITWISE_OR;
|
|
subexpr_type = JSP_STATE_EXPR_BITWISE_XOR;
|
|
}
|
|
else
|
|
{
|
|
is_simple = false;
|
|
}
|
|
|
|
if (!is_simple && state_p->req_state >= JSP_STATE_EXPR_LOGICAL_AND)
|
|
{
|
|
state_p->state = JSP_STATE_EXPR_LOGICAL_AND;
|
|
state_p->u.expression.u.logical_and.rewrite_chain = MAX_OPCODES;
|
|
}
|
|
else
|
|
{
|
|
JERRY_ASSERT (is_simple || state_p->req_state < JSP_STATE_EXPR__SIMPLE_END);
|
|
|
|
if (!is_simple || state_p->req_state < new_state)
|
|
{
|
|
state_p->state = state_p->req_state;
|
|
|
|
state_p->is_completed = true;
|
|
}
|
|
else
|
|
{
|
|
skip_token (ctx_p);
|
|
|
|
state_p->state = new_state;
|
|
state_p->u.expression.token_type = tt;
|
|
|
|
jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, subexpr_type, in_allowed);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (state_p->state == JSP_STATE_EXPR_LOGICAL_AND)
|
|
{
|
|
if (state_p->is_completed)
|
|
{
|
|
/* propagate to LogicalOrExpression */
|
|
state_p->state = JSP_STATE_EXPR_LOGICAL_OR;
|
|
|
|
state_p->u.expression.u.logical_or.rewrite_chain = MAX_OPCODES;
|
|
|
|
state_p->is_completed = false;
|
|
}
|
|
else
|
|
{
|
|
if (is_subexpr_end)
|
|
{
|
|
dump_get_value_if_ref (ctx_p, state_p, true);
|
|
dump_get_value_if_ref (ctx_p, substate_p, true);
|
|
|
|
JERRY_ASSERT (state_p->u.expression.token_type == TOK_DOUBLE_AND);
|
|
|
|
JERRY_ASSERT (state_p->u.expression.operand.is_register_operand ());
|
|
|
|
dump_variable_assignment (ctx_p, state_p->u.expression.operand, substate_p->u.expression.operand);
|
|
|
|
state_p->u.expression.token_type = TOK_EMPTY;
|
|
|
|
JSP_FINISH_SUBEXPR ();
|
|
}
|
|
else
|
|
{
|
|
JERRY_ASSERT (state_p->u.expression.token_type == TOK_EMPTY);
|
|
|
|
if (token_is (TOK_DOUBLE_AND))
|
|
{
|
|
/* ECMA-262 v5, 11.11 (complex LogicalAndExpression) */
|
|
skip_token (ctx_p);
|
|
|
|
/*
|
|
* FIXME:
|
|
* Consider eliminating COMPLEX_PRODUCTION flag through implementing establishing a general operand
|
|
* management for expressions
|
|
*/
|
|
if (!state_p->is_complex_production)
|
|
{
|
|
state_p->is_complex_production = true;
|
|
|
|
state_p->u.expression.u.logical_and.rewrite_chain = MAX_OPCODES;
|
|
|
|
JERRY_ASSERT (!state_p->is_fixed_ret_operand);
|
|
|
|
jsp_operand_t ret = tmp_operand ();
|
|
|
|
dump_get_value_if_ref (ctx_p, state_p, true);
|
|
|
|
dump_variable_assignment (ctx_p, ret, state_p->u.expression.operand);
|
|
|
|
state_p->is_fixed_ret_operand = true;
|
|
state_p->u.expression.operand = ret;
|
|
}
|
|
|
|
JERRY_ASSERT (state_p->is_complex_production);
|
|
|
|
jsp_add_conditional_jump_to_rewrite_chain (ctx_p, &state_p->u.expression.u.logical_and.rewrite_chain,
|
|
true, state_p->u.expression.operand);
|
|
|
|
state_p->u.expression.token_type = TOK_DOUBLE_AND;
|
|
|
|
jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_BITWISE_OR, in_allowed);
|
|
}
|
|
else
|
|
{
|
|
/* end of LogicalAndExpression */
|
|
JERRY_ASSERT (state_p->u.expression.token_type == TOK_EMPTY);
|
|
|
|
jsp_rewrite_jumps_chain (ctx_p, &state_p->u.expression.u.logical_and.rewrite_chain,
|
|
dumper_get_current_instr_counter (ctx_p));
|
|
|
|
state_p->is_complex_production = false;
|
|
|
|
state_p->is_completed = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (state_p->state == JSP_STATE_EXPR_LOGICAL_OR)
|
|
{
|
|
if (state_p->is_completed)
|
|
{
|
|
/* switching to ConditionalExpression */
|
|
if (token_is (TOK_QUERY))
|
|
{
|
|
state_p->state = JSP_STATE_EXPR_CONDITION;
|
|
state_p->is_completed = false;
|
|
|
|
/* ECMA-262 v5, 11.12 */
|
|
skip_token (ctx_p);
|
|
|
|
dump_get_value_if_ref (ctx_p, state_p, true);
|
|
dump_get_value_for_state_if_const (ctx_p, state_p);
|
|
|
|
vm_instr_counter_t conditional_check_pos = dump_conditional_check_for_rewrite (ctx_p,
|
|
state_p->u.expression.operand);
|
|
state_p->u.expression.u.conditional.conditional_check_pos = conditional_check_pos;
|
|
|
|
state_p->u.expression.token_type = TOK_QUERY;
|
|
|
|
if (!state_p->is_fixed_ret_operand)
|
|
{
|
|
state_p->is_fixed_ret_operand = true;
|
|
state_p->u.expression.operand = tmp_operand ();
|
|
}
|
|
|
|
jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_ASSIGNMENT, true);
|
|
}
|
|
else
|
|
{
|
|
/* just propagate */
|
|
state_p->state = JSP_STATE_EXPR_ASSIGNMENT;
|
|
JERRY_ASSERT (state_p->is_completed);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (is_subexpr_end)
|
|
{
|
|
dump_get_value_if_ref (ctx_p, substate_p, true);
|
|
|
|
JERRY_ASSERT (state_p->u.expression.token_type == TOK_DOUBLE_OR);
|
|
|
|
JERRY_ASSERT (state_p->u.expression.operand.is_register_operand ());
|
|
dump_variable_assignment (ctx_p, state_p->u.expression.operand, substate_p->u.expression.operand);
|
|
|
|
state_p->u.expression.token_type = TOK_EMPTY;
|
|
|
|
JSP_FINISH_SUBEXPR ();
|
|
}
|
|
else
|
|
{
|
|
JERRY_ASSERT (state_p->u.expression.token_type == TOK_EMPTY);
|
|
|
|
if (token_is (TOK_DOUBLE_OR))
|
|
{
|
|
/* ECMA-262 v5, 11.11 (complex LogicalOrExpression) */
|
|
skip_token (ctx_p);
|
|
|
|
/*
|
|
* FIXME:
|
|
* Consider eliminating COMPLEX_PRODUCTION flag through implementing establishing a general operand
|
|
* management for expressions
|
|
*/
|
|
if (!state_p->is_complex_production)
|
|
{
|
|
state_p->is_complex_production = true;
|
|
|
|
state_p->u.expression.u.logical_or.rewrite_chain = MAX_OPCODES;
|
|
|
|
jsp_operand_t ret;
|
|
|
|
if (state_p->is_fixed_ret_operand)
|
|
{
|
|
JERRY_ASSERT (state_p->u.expression.operand.is_register_operand ());
|
|
|
|
ret = state_p->u.expression.operand;
|
|
}
|
|
else
|
|
{
|
|
ret = tmp_operand ();
|
|
|
|
dump_get_value_if_ref (ctx_p, state_p, true);
|
|
|
|
dump_variable_assignment (ctx_p, ret, state_p->u.expression.operand);
|
|
|
|
state_p->is_fixed_ret_operand = true;
|
|
|
|
state_p->u.expression.operand = ret;
|
|
}
|
|
}
|
|
|
|
JERRY_ASSERT (state_p->is_complex_production);
|
|
|
|
jsp_add_conditional_jump_to_rewrite_chain (ctx_p, &state_p->u.expression.u.logical_or.rewrite_chain,
|
|
false, state_p->u.expression.operand);
|
|
|
|
state_p->u.expression.token_type = TOK_DOUBLE_OR;
|
|
|
|
jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_LOGICAL_AND, in_allowed);
|
|
}
|
|
else
|
|
{
|
|
/* end of LogicalOrExpression */
|
|
JERRY_ASSERT (state_p->u.expression.token_type == TOK_EMPTY);
|
|
|
|
jsp_rewrite_jumps_chain (ctx_p, &state_p->u.expression.u.logical_or.rewrite_chain,
|
|
dumper_get_current_instr_counter (ctx_p));
|
|
|
|
state_p->is_complex_production = false;
|
|
|
|
state_p->is_completed = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (state_p->state == JSP_STATE_EXPR_ASSIGNMENT)
|
|
{
|
|
/* assignment production can't be continued at the point */
|
|
JERRY_ASSERT (!is_subexpr_end);
|
|
|
|
JERRY_ASSERT (state_p->is_completed);
|
|
JERRY_ASSERT (state_p->u.expression.token_type == TOK_EMPTY);
|
|
|
|
/* 'assignment expression' production can't be continued with an operator,
|
|
* so just propagating it to 'expression' production */
|
|
state_p->is_completed = false;
|
|
state_p->state = JSP_STATE_EXPR_EXPRESSION;
|
|
}
|
|
else if (state_p->state == JSP_STATE_EXPR_CONDITION)
|
|
{
|
|
JERRY_ASSERT (!state_p->is_completed);
|
|
JERRY_ASSERT (is_subexpr_end);
|
|
|
|
/* ECMA-262 v5, 11.12 */
|
|
if (state_p->u.expression.token_type == TOK_QUERY)
|
|
{
|
|
current_token_must_be_check_and_skip_it (ctx_p, TOK_COLON);
|
|
|
|
JERRY_ASSERT (state_p->is_fixed_ret_operand);
|
|
JERRY_ASSERT (state_p->u.expression.operand.is_register_operand ());
|
|
JERRY_ASSERT (substate_p->state == JSP_STATE_EXPR_ASSIGNMENT);
|
|
|
|
dump_get_value_if_ref (ctx_p, substate_p, true);
|
|
|
|
dump_variable_assignment (ctx_p, state_p->u.expression.operand, substate_p->u.expression.operand);
|
|
|
|
JSP_FINISH_SUBEXPR ();
|
|
|
|
state_p->u.expression.u.conditional.jump_to_end_pos = dump_jump_to_end_for_rewrite (ctx_p);
|
|
|
|
rewrite_conditional_check (ctx_p, state_p->u.expression.u.conditional.conditional_check_pos);
|
|
|
|
state_p->u.expression.token_type = TOK_COLON;
|
|
|
|
jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_ASSIGNMENT, in_allowed);
|
|
}
|
|
else
|
|
{
|
|
JERRY_ASSERT (state_p->u.expression.token_type == TOK_COLON);
|
|
|
|
JERRY_ASSERT (state_p->is_fixed_ret_operand);
|
|
JERRY_ASSERT (state_p->u.expression.operand.is_register_operand ());
|
|
JERRY_ASSERT (substate_p->state == JSP_STATE_EXPR_ASSIGNMENT);
|
|
|
|
dump_get_value_if_ref (ctx_p, substate_p, true);
|
|
|
|
dump_variable_assignment (ctx_p, state_p->u.expression.operand, substate_p->u.expression.operand);
|
|
|
|
JSP_FINISH_SUBEXPR ();
|
|
|
|
rewrite_jump_to_end (ctx_p, state_p->u.expression.u.conditional.jump_to_end_pos);
|
|
|
|
state_p->u.expression.token_type = TOK_EMPTY;
|
|
state_p->is_fixed_ret_operand = false;
|
|
|
|
state_p->state = JSP_STATE_EXPR_ASSIGNMENT;
|
|
state_p->is_completed = true;
|
|
}
|
|
}
|
|
else if (state_p->state == JSP_STATE_EXPR_EXPRESSION)
|
|
{
|
|
/* ECMA-262 v5, 11.14 */
|
|
|
|
if (is_subexpr_end)
|
|
{
|
|
JERRY_ASSERT (state_p->u.expression.token_type == TOK_COMMA);
|
|
|
|
dump_get_value_if_ref (ctx_p, substate_p, false);
|
|
|
|
if (state_p->is_need_retval)
|
|
{
|
|
/*
|
|
* The operand should be already evaluated
|
|
*
|
|
* See also:
|
|
* 11.14, step 2
|
|
*/
|
|
JERRY_ASSERT (!state_p->is_value_based_reference
|
|
&& !state_p->u.expression.operand.is_identifier_operand ());
|
|
|
|
state_p->u.expression.operand = substate_p->u.expression.operand;
|
|
}
|
|
|
|
JSP_FINISH_SUBEXPR ();
|
|
}
|
|
else
|
|
{
|
|
JERRY_ASSERT (!state_p->is_completed);
|
|
|
|
if (token_is (TOK_COMMA))
|
|
{
|
|
skip_token (ctx_p);
|
|
|
|
JERRY_ASSERT (!token_is (TOK_COMMA));
|
|
|
|
state_p->u.expression.token_type = TOK_COMMA;
|
|
|
|
/* ECMA-262 v5, 11.14, step 2 */
|
|
dump_get_value_if_ref (ctx_p, state_p, false);
|
|
|
|
jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_ASSIGNMENT, in_allowed);
|
|
}
|
|
else
|
|
{
|
|
if (state_p->is_need_retval)
|
|
{
|
|
if (!state_p->is_value_based_reference)
|
|
{
|
|
dump_get_value_for_state_if_const (ctx_p, state_p);
|
|
}
|
|
}
|
|
|
|
state_p->is_completed = true;
|
|
}
|
|
}
|
|
}
|
|
else if (state_p->state == JSP_STATE_STAT_EMPTY)
|
|
{
|
|
dumper_new_statement (ctx_p);
|
|
|
|
if (token_is (TOK_KW_IF)) /* IfStatement */
|
|
{
|
|
skip_token (ctx_p);
|
|
|
|
parse_expression_inside_parens_begin (ctx_p);
|
|
|
|
state_p->state = JSP_STATE_STAT_IF_BRANCH_START;
|
|
|
|
jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, in_allowed);
|
|
}
|
|
else if (token_is (TOK_OPEN_BRACE))
|
|
{
|
|
skip_token (ctx_p);
|
|
|
|
state_p->state = JSP_STATE_STAT_BLOCK;
|
|
jsp_start_statement_parse (ctx_p, JSP_STATE_STAT_STATEMENT_LIST);
|
|
jsp_state_top (ctx_p)->req_state = JSP_STATE_STAT_STATEMENT_LIST;
|
|
}
|
|
else if (token_is (TOK_KW_VAR))
|
|
{
|
|
state_p->state = JSP_STATE_STAT_VAR_DECL;
|
|
state_p->var_decl = true;
|
|
}
|
|
else if (token_is (TOK_KW_DO)
|
|
|| token_is (TOK_KW_WHILE)
|
|
|| token_is (TOK_KW_FOR))
|
|
{
|
|
state_p->u.statement.u.iterational.continues_rewrite_chain = MAX_OPCODES;
|
|
state_p->u.statement.u.iterational.continue_tgt_oc = MAX_OPCODES;
|
|
|
|
if (token_is (TOK_KW_DO))
|
|
{
|
|
vm_instr_counter_t next_iter_tgt_pos = dumper_set_next_iteration_target (ctx_p);
|
|
state_p->u.statement.u.iterational.u.loop_do_while.next_iter_tgt_pos = next_iter_tgt_pos;
|
|
skip_token (ctx_p);
|
|
|
|
JSP_PUSH_STATE_AND_STATEMENT_PARSE (JSP_STATE_STAT_DO_WHILE);
|
|
}
|
|
else if (token_is (TOK_KW_WHILE))
|
|
{
|
|
skip_token (ctx_p);
|
|
|
|
state_p->u.statement.u.iterational.u.loop_while.u.cond_expr_start_loc = tok.loc;
|
|
jsp_skip_braces (ctx_p, TOK_OPEN_PAREN);
|
|
|
|
state_p->u.statement.u.iterational.u.loop_while.jump_to_end_pos = dump_jump_to_end_for_rewrite (ctx_p);
|
|
|
|
state_p->u.statement.u.iterational.u.loop_while.next_iter_tgt_pos = dumper_set_next_iteration_target (ctx_p);
|
|
|
|
skip_token (ctx_p);
|
|
|
|
JSP_PUSH_STATE_AND_STATEMENT_PARSE (JSP_STATE_STAT_WHILE);
|
|
}
|
|
else
|
|
{
|
|
current_token_must_be_check_and_skip_it (ctx_p, TOK_KW_FOR);
|
|
|
|
current_token_must_be (TOK_OPEN_PAREN);
|
|
|
|
locus for_open_paren_loc, for_body_statement_loc;
|
|
|
|
for_open_paren_loc = tok.loc;
|
|
|
|
jsp_skip_braces (ctx_p, TOK_OPEN_PAREN);
|
|
skip_token (ctx_p);
|
|
|
|
for_body_statement_loc = tok.loc;
|
|
|
|
seek_token (ctx_p, for_open_paren_loc);
|
|
|
|
bool is_plain_for = jsp_find_next_token_before_the_locus (ctx_p,
|
|
TOK_SEMICOLON,
|
|
for_body_statement_loc,
|
|
true);
|
|
seek_token (ctx_p, for_open_paren_loc);
|
|
|
|
if (is_plain_for)
|
|
{
|
|
state_p->u.statement.u.iterational.u.loop_for.u1.body_loc = for_body_statement_loc;
|
|
|
|
current_token_must_be_check_and_skip_it (ctx_p, TOK_OPEN_PAREN);
|
|
|
|
// Initializer
|
|
if (token_is (TOK_KW_VAR))
|
|
{
|
|
state_p->state = JSP_STATE_STAT_FOR_INIT_END;
|
|
jsp_start_statement_parse (ctx_p, JSP_STATE_STAT_VAR_DECL);
|
|
}
|
|
else
|
|
{
|
|
if (!token_is (TOK_SEMICOLON))
|
|
{
|
|
jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, false);
|
|
|
|
jsp_state_top (ctx_p)->is_need_retval = false;
|
|
}
|
|
else
|
|
{
|
|
// Initializer is empty
|
|
}
|
|
state_p->state = JSP_STATE_STAT_FOR_INIT_END;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
current_token_must_be_check_and_skip_it (ctx_p, TOK_OPEN_PAREN);
|
|
|
|
// Save Iterator location
|
|
state_p->u.statement.u.iterational.u.loop_for_in.u.iterator_expr_loc = tok.loc;
|
|
|
|
while (lit_utf8_iterator_pos_cmp (tok.loc, for_body_statement_loc) < 0)
|
|
{
|
|
if (jsp_find_next_token_before_the_locus (ctx_p,
|
|
TOK_KW_IN,
|
|
for_body_statement_loc,
|
|
true))
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "Invalid for statement");
|
|
}
|
|
}
|
|
|
|
JERRY_ASSERT (token_is (TOK_KW_IN));
|
|
skip_token (ctx_p);
|
|
|
|
// Collection
|
|
jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, true);
|
|
state_p->state = JSP_STATE_STAT_FOR_IN;
|
|
}
|
|
}
|
|
}
|
|
else if (token_is (TOK_KW_SWITCH))
|
|
{
|
|
skip_token (ctx_p);
|
|
|
|
parse_expression_inside_parens_begin (ctx_p);
|
|
jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, true);
|
|
state_p->state = JSP_STATE_STAT_SWITCH;
|
|
}
|
|
else if (token_is (TOK_SEMICOLON))
|
|
{
|
|
skip_token (ctx_p);
|
|
|
|
JSP_COMPLETE_STATEMENT_PARSE ();
|
|
}
|
|
else if (token_is (TOK_KW_CONTINUE)
|
|
|| token_is (TOK_KW_BREAK))
|
|
{
|
|
bool is_break = token_is (TOK_KW_BREAK);
|
|
|
|
skip_token (ctx_p);
|
|
|
|
jsp_state_t *prev_state_p = jsp_get_prev_state (state_p);
|
|
|
|
if (prev_state_p->state == JSP_STATE_STAT_STATEMENT_LIST)
|
|
{
|
|
prev_state_p->is_stmt_list_control_flow_exit_stmt_occured = true;
|
|
}
|
|
|
|
jsp_state_t *labelled_stmt_p;
|
|
bool is_simply_jumpable = true;
|
|
|
|
if (!lexer_is_preceded_by_newlines (tok) && token_is (TOK_NAME))
|
|
{
|
|
/* break or continue on a label */
|
|
labelled_stmt_p = jsp_find_named_label (ctx_p, token_data_as_lit_cp (), &is_simply_jumpable);
|
|
|
|
if (labelled_stmt_p == NULL)
|
|
{
|
|
EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "Label not found");
|
|
}
|
|
|
|
skip_token (ctx_p);
|
|
}
|
|
else
|
|
{
|
|
labelled_stmt_p = jsp_find_unnamed_label (ctx_p, is_break, &is_simply_jumpable);
|
|
|
|
if (labelled_stmt_p == NULL)
|
|
{
|
|
if (is_break)
|
|
{
|
|
EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "No corresponding statement for the break");
|
|
}
|
|
else
|
|
{
|
|
EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "No corresponding statement for the continue");
|
|
}
|
|
}
|
|
}
|
|
|
|
insert_semicolon (ctx_p);
|
|
|
|
JERRY_ASSERT (labelled_stmt_p != NULL);
|
|
|
|
vm_instr_counter_t *rewrite_chain_p;
|
|
if (is_break)
|
|
{
|
|
rewrite_chain_p = &labelled_stmt_p->u.statement.breaks_rewrite_chain;
|
|
}
|
|
else
|
|
{
|
|
rewrite_chain_p = &labelled_stmt_p->u.statement.u.iterational.continues_rewrite_chain;
|
|
}
|
|
|
|
if (is_simply_jumpable)
|
|
{
|
|
jsp_add_simple_jump_to_rewrite_chain (ctx_p, rewrite_chain_p);
|
|
}
|
|
else
|
|
{
|
|
jsp_add_nested_jump_to_rewrite_chain (ctx_p, rewrite_chain_p);
|
|
}
|
|
|
|
JSP_COMPLETE_STATEMENT_PARSE ();
|
|
}
|
|
else if (token_is (TOK_KW_RETURN))
|
|
{
|
|
if (jsp_get_scope_type (ctx_p) != SCOPE_TYPE_FUNCTION)
|
|
{
|
|
EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "Return is illegal");
|
|
}
|
|
|
|
jsp_state_t *prev_state_p = jsp_get_prev_state (state_p);
|
|
|
|
if (prev_state_p->state == JSP_STATE_STAT_STATEMENT_LIST)
|
|
{
|
|
prev_state_p->is_stmt_list_control_flow_exit_stmt_occured = true;
|
|
}
|
|
|
|
skip_token (ctx_p);
|
|
|
|
if (!token_is (TOK_SEMICOLON)
|
|
&& !lexer_is_preceded_by_newlines (tok)
|
|
&& !token_is (TOK_CLOSE_BRACE))
|
|
{
|
|
jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, true);
|
|
state_p->state = JSP_STATE_STAT_RETURN;
|
|
}
|
|
else
|
|
{
|
|
dump_ret (ctx_p);
|
|
JSP_COMPLETE_STATEMENT_PARSE ();
|
|
}
|
|
}
|
|
else if (token_is (TOK_KW_TRY))
|
|
{
|
|
skip_token (ctx_p);
|
|
|
|
if (!jsp_is_dump_mode (ctx_p))
|
|
{
|
|
scopes_tree_set_contains_try (jsp_get_current_scopes_tree_node (ctx_p));
|
|
}
|
|
|
|
state_p->u.statement.u.try_statement.try_pos = dump_try_for_rewrite (ctx_p);
|
|
|
|
current_token_must_be_check_and_skip_it (ctx_p, TOK_OPEN_BRACE);
|
|
|
|
state_p->is_simply_jumpable_border = true;
|
|
|
|
state_p->state = JSP_STATE_STAT_TRY;
|
|
jsp_start_statement_parse (ctx_p, JSP_STATE_STAT_BLOCK);
|
|
jsp_start_statement_parse (ctx_p, JSP_STATE_STAT_STATEMENT_LIST);
|
|
jsp_state_top (ctx_p)->req_state = JSP_STATE_STAT_STATEMENT_LIST;
|
|
}
|
|
else if (token_is (TOK_KW_WITH))
|
|
{
|
|
skip_token (ctx_p);
|
|
|
|
if (jsp_is_strict_mode (ctx_p))
|
|
{
|
|
EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "'with' expression is not allowed in strict mode.");
|
|
}
|
|
|
|
parse_expression_inside_parens_begin (ctx_p);
|
|
jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, true);
|
|
state_p->state = JSP_STATE_STAT_WITH;
|
|
}
|
|
else if (token_is (TOK_KW_THROW))
|
|
{
|
|
skip_token (ctx_p);
|
|
|
|
jsp_state_t *prev_state_p = jsp_get_prev_state (state_p);
|
|
|
|
if (prev_state_p->state == JSP_STATE_STAT_STATEMENT_LIST)
|
|
{
|
|
prev_state_p->is_stmt_list_control_flow_exit_stmt_occured = true;
|
|
}
|
|
|
|
jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, true);
|
|
state_p->state = JSP_STATE_STAT_THROW;
|
|
}
|
|
else if (token_is (TOK_KW_FUNCTION))
|
|
{
|
|
skip_token (ctx_p);
|
|
|
|
current_token_must_be (TOK_NAME);
|
|
|
|
const jsp_operand_t func_name = jsp_operand_t::make_string_lit_operand (token_data_as_lit_cp ());
|
|
skip_token (ctx_p);
|
|
|
|
state_p->state = JSP_STATE_FUNC_DECL_FINISH;
|
|
|
|
jsp_start_parse_function_scope (ctx_p, func_name, false, NULL);
|
|
}
|
|
else
|
|
{
|
|
bool is_expression = true;
|
|
|
|
if (token_is (TOK_NAME)) // LabelledStatement or ExpressionStatement
|
|
{
|
|
const token temp = tok;
|
|
skip_token (ctx_p);
|
|
if (token_is (TOK_COLON))
|
|
{
|
|
skip_token (ctx_p);
|
|
|
|
lit_cpointer_t name_cp;
|
|
name_cp.packed_value = temp.uid;
|
|
|
|
bool is_simply_jumpable;
|
|
jsp_state_t *named_label_state_p = jsp_find_named_label (ctx_p, name_cp, &is_simply_jumpable);
|
|
|
|
if (named_label_state_p != NULL)
|
|
{
|
|
EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "Label is duplicated");
|
|
}
|
|
|
|
state_p->state = JSP_STATE_STAT_NAMED_LABEL;
|
|
state_p->u.named_label.name_cp = name_cp;
|
|
|
|
jsp_start_statement_parse (ctx_p, JSP_STATE_STAT_EMPTY);
|
|
|
|
is_expression = false;
|
|
}
|
|
else
|
|
{
|
|
seek_token (ctx_p, temp.loc);
|
|
}
|
|
}
|
|
|
|
if (is_expression)
|
|
{
|
|
jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, true);
|
|
|
|
if (jsp_get_scope_type (ctx_p) != SCOPE_TYPE_EVAL)
|
|
{
|
|
jsp_state_top (ctx_p)->is_need_retval = false;
|
|
}
|
|
|
|
state_p->state = JSP_STATE_STAT_EXPRESSION;
|
|
}
|
|
}
|
|
}
|
|
else if (state_p->state == JSP_STATE_STAT_BLOCK)
|
|
{
|
|
JERRY_ASSERT (is_stmt_list_end);
|
|
|
|
jsp_state_t *prev_state_p = jsp_get_prev_state (state_p);
|
|
|
|
if (prev_state_p->state == JSP_STATE_STAT_STATEMENT_LIST)
|
|
{
|
|
prev_state_p->is_stmt_list_control_flow_exit_stmt_occured = is_stmt_list_control_flow_exit_stmt_occured;
|
|
}
|
|
|
|
current_token_must_be_check_and_skip_it (ctx_p, TOK_CLOSE_BRACE);
|
|
|
|
JSP_COMPLETE_STATEMENT_PARSE ();
|
|
}
|
|
else if (state_p->state == JSP_STATE_STAT_IF_BRANCH_START)
|
|
{
|
|
if (is_subexpr_end) // Finished parsing condition
|
|
{
|
|
parse_expression_inside_parens_end (ctx_p);
|
|
|
|
dump_get_value_if_ref (ctx_p, substate_p, true);
|
|
dump_get_value_for_state_if_const (ctx_p, substate_p);
|
|
|
|
jsp_operand_t cond = substate_p->u.expression.operand;
|
|
|
|
state_p->u.statement.u.if_statement.conditional_check_pos = dump_conditional_check_for_rewrite (ctx_p, cond);
|
|
|
|
JSP_FINISH_SUBEXPR ();
|
|
|
|
JSP_PUSH_STATE_AND_STATEMENT_PARSE (JSP_STATE_STAT_IF_BRANCH_START);
|
|
}
|
|
else
|
|
{
|
|
if (token_is (TOK_KW_ELSE))
|
|
{
|
|
skip_token (ctx_p);
|
|
|
|
state_p->u.statement.u.if_statement.jump_to_end_pos = dump_jump_to_end_for_rewrite (ctx_p);
|
|
rewrite_conditional_check (ctx_p, state_p->u.statement.u.if_statement.conditional_check_pos);
|
|
|
|
JSP_PUSH_STATE_AND_STATEMENT_PARSE (JSP_STATE_STAT_IF_BRANCH_END);
|
|
}
|
|
else
|
|
{
|
|
rewrite_conditional_check (ctx_p, state_p->u.statement.u.if_statement.conditional_check_pos);
|
|
|
|
JSP_COMPLETE_STATEMENT_PARSE ();
|
|
}
|
|
}
|
|
}
|
|
else if (state_p->state == JSP_STATE_STAT_IF_BRANCH_END)
|
|
{
|
|
rewrite_jump_to_end (ctx_p, state_p->u.statement.u.if_statement.jump_to_end_pos);
|
|
|
|
JSP_COMPLETE_STATEMENT_PARSE ();
|
|
}
|
|
else if (state_p->state == JSP_STATE_STAT_STATEMENT_LIST)
|
|
{
|
|
if (is_subexpr_end)
|
|
{
|
|
JSP_FINISH_SUBEXPR ();
|
|
}
|
|
|
|
while (token_is (TOK_SEMICOLON))
|
|
{
|
|
skip_token (ctx_p);
|
|
}
|
|
|
|
if (token_is (TOK_CLOSE_BRACE)
|
|
|| (token_is (TOK_KW_CASE) || token_is (TOK_KW_DEFAULT)))
|
|
{
|
|
state_p->is_completed = true;
|
|
}
|
|
else
|
|
{
|
|
jsp_start_statement_parse (ctx_p, JSP_STATE_STAT_EMPTY);
|
|
}
|
|
}
|
|
else if (state_p->state == JSP_STATE_STAT_VAR_DECL)
|
|
{
|
|
skip_token (ctx_p);
|
|
|
|
locus name_loc = tok.loc;
|
|
|
|
current_token_must_be (TOK_NAME);
|
|
|
|
const lit_cpointer_t lit_cp = token_data_as_lit_cp ();
|
|
const jsp_operand_t name = jsp_operand_t::make_string_lit_operand (lit_cp);
|
|
|
|
skip_token (ctx_p);
|
|
|
|
if (!jsp_is_dump_mode (ctx_p))
|
|
{
|
|
jsp_early_error_check_for_eval_and_arguments_in_strict_mode (name, jsp_is_strict_mode (ctx_p), name_loc);
|
|
}
|
|
|
|
if (!jsp_is_dump_mode (ctx_p))
|
|
{
|
|
if (!scopes_tree_variable_declaration_exists (jsp_get_current_scopes_tree_node (ctx_p), lit_cp))
|
|
{
|
|
dump_variable_declaration (ctx_p, lit_cp);
|
|
}
|
|
}
|
|
|
|
if (token_is (TOK_EQ))
|
|
{
|
|
seek_token (ctx_p, name_loc);
|
|
|
|
jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_ASSIGNMENT, true);
|
|
|
|
jsp_state_top (ctx_p)->is_need_retval = false;
|
|
}
|
|
|
|
state_p->state = JSP_STATE_STAT_VAR_DECL_FINISH;
|
|
}
|
|
else if (state_p->state == JSP_STATE_STAT_VAR_DECL_FINISH)
|
|
{
|
|
if (is_subexpr_end)
|
|
{
|
|
JSP_FINISH_SUBEXPR ();
|
|
}
|
|
|
|
if (!token_is (TOK_COMMA))
|
|
{
|
|
JSP_COMPLETE_STATEMENT_PARSE ();
|
|
|
|
if (state_p->var_decl)
|
|
{
|
|
insert_semicolon (ctx_p);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
state_p->state = JSP_STATE_STAT_VAR_DECL;
|
|
}
|
|
}
|
|
else if (state_p->state == JSP_STATE_STAT_DO_WHILE)
|
|
{
|
|
if (is_subexpr_end)
|
|
{
|
|
parse_expression_inside_parens_end (ctx_p);
|
|
|
|
const jsp_operand_t cond = substate_p->u.expression.operand;
|
|
|
|
JSP_FINISH_SUBEXPR ();
|
|
|
|
dump_continue_iterations_check (ctx_p,
|
|
state_p->u.statement.u.iterational.u.loop_do_while.next_iter_tgt_pos,
|
|
cond);
|
|
|
|
state_p->state = JSP_STATE_STAT_ITER_FINISH;
|
|
}
|
|
else
|
|
{
|
|
assert_keyword (TOK_KW_WHILE);
|
|
skip_token (ctx_p);
|
|
|
|
JERRY_ASSERT (state_p->u.statement.u.iterational.continue_tgt_oc == MAX_OPCODES);
|
|
state_p->u.statement.u.iterational.continue_tgt_oc = dumper_get_current_instr_counter (ctx_p);
|
|
|
|
parse_expression_inside_parens_begin (ctx_p);
|
|
jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, true);
|
|
}
|
|
}
|
|
else if (state_p->state == JSP_STATE_STAT_WHILE)
|
|
{
|
|
if (is_subexpr_end)
|
|
{
|
|
parse_expression_inside_parens_end (ctx_p);
|
|
|
|
const jsp_operand_t cond = substate_p->u.expression.operand;
|
|
|
|
JSP_FINISH_SUBEXPR ();
|
|
|
|
dump_continue_iterations_check (ctx_p, state_p->u.statement.u.iterational.u.loop_while.next_iter_tgt_pos, cond);
|
|
|
|
seek_token (ctx_p, state_p->u.statement.u.iterational.u.loop_while.u.end_loc);
|
|
|
|
state_p->state = JSP_STATE_STAT_ITER_FINISH;
|
|
}
|
|
else
|
|
{
|
|
JERRY_ASSERT (state_p->u.statement.u.iterational.continue_tgt_oc == MAX_OPCODES);
|
|
state_p->u.statement.u.iterational.continue_tgt_oc = dumper_get_current_instr_counter (ctx_p);
|
|
|
|
rewrite_jump_to_end (ctx_p, state_p->u.statement.u.iterational.u.loop_while.jump_to_end_pos);
|
|
|
|
const locus end_loc = tok.loc;
|
|
|
|
seek_token (ctx_p, state_p->u.statement.u.iterational.u.loop_while.u.cond_expr_start_loc);
|
|
|
|
state_p->u.statement.u.iterational.u.loop_while.u.end_loc = end_loc;
|
|
|
|
parse_expression_inside_parens_begin (ctx_p);
|
|
jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, true);
|
|
}
|
|
}
|
|
else if (state_p->state == JSP_STATE_STAT_FOR_INIT_END)
|
|
{
|
|
if (is_subexpr_end)
|
|
{
|
|
JSP_FINISH_SUBEXPR ();
|
|
}
|
|
|
|
// Jump -> ConditionCheck
|
|
state_p->u.statement.u.iterational.u.loop_for.jump_to_end_pos = dump_jump_to_end_for_rewrite (ctx_p);
|
|
|
|
state_p->u.statement.u.iterational.u.loop_for.next_iter_tgt_pos = dumper_set_next_iteration_target (ctx_p);
|
|
|
|
current_token_must_be_check_and_skip_it (ctx_p, TOK_SEMICOLON);
|
|
|
|
locus for_body_statement_loc = state_p->u.statement.u.iterational.u.loop_for.u1.body_loc;
|
|
|
|
// Save Condition locus
|
|
state_p->u.statement.u.iterational.u.loop_for.u1.condition_expr_loc = tok.loc;
|
|
|
|
if (!jsp_find_next_token_before_the_locus (ctx_p,
|
|
TOK_SEMICOLON,
|
|
for_body_statement_loc,
|
|
true))
|
|
{
|
|
EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "Invalid for statement");
|
|
}
|
|
|
|
current_token_must_be_check_and_skip_it (ctx_p, TOK_SEMICOLON);
|
|
|
|
// Save Increment locus
|
|
state_p->u.statement.u.iterational.u.loop_for.u2.increment_expr_loc = tok.loc;
|
|
|
|
// Body
|
|
seek_token (ctx_p, for_body_statement_loc);
|
|
|
|
JSP_PUSH_STATE_AND_STATEMENT_PARSE (JSP_STATE_STAT_FOR_INCREMENT);
|
|
}
|
|
else if (state_p->state == JSP_STATE_STAT_FOR_INCREMENT)
|
|
{
|
|
if (is_subexpr_end)
|
|
{
|
|
JSP_FINISH_SUBEXPR ();
|
|
|
|
state_p->state = JSP_STATE_STAT_FOR_COND;
|
|
}
|
|
else
|
|
{
|
|
// Save LoopEnd locus
|
|
const locus loop_end_loc = tok.loc;
|
|
|
|
// Setup ContinueTarget
|
|
JERRY_ASSERT (state_p->u.statement.u.iterational.continue_tgt_oc == MAX_OPCODES);
|
|
state_p->u.statement.u.iterational.continue_tgt_oc = dumper_get_current_instr_counter (ctx_p);
|
|
|
|
// Increment
|
|
seek_token (ctx_p, state_p->u.statement.u.iterational.u.loop_for.u2.increment_expr_loc);
|
|
|
|
state_p->u.statement.u.iterational.u.loop_for.u2.end_loc = loop_end_loc;
|
|
|
|
if (!token_is (TOK_CLOSE_PAREN))
|
|
{
|
|
jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, true);
|
|
|
|
jsp_state_top (ctx_p)->is_need_retval = false;
|
|
}
|
|
else
|
|
{
|
|
state_p->state = JSP_STATE_STAT_FOR_COND;
|
|
}
|
|
}
|
|
}
|
|
else if (state_p->state == JSP_STATE_STAT_FOR_COND)
|
|
{
|
|
if (is_subexpr_end)
|
|
{
|
|
jsp_operand_t cond = substate_p->u.expression.operand;
|
|
JSP_FINISH_SUBEXPR ();
|
|
|
|
dump_continue_iterations_check (ctx_p, state_p->u.statement.u.iterational.u.loop_for.next_iter_tgt_pos, cond);
|
|
|
|
state_p->state = JSP_STATE_STAT_FOR_FINISH;
|
|
}
|
|
else
|
|
{
|
|
current_token_must_be (TOK_CLOSE_PAREN);
|
|
|
|
// Setup ConditionCheck
|
|
rewrite_jump_to_end (ctx_p, state_p->u.statement.u.iterational.u.loop_for.jump_to_end_pos);
|
|
|
|
// Condition
|
|
seek_token (ctx_p, state_p->u.statement.u.iterational.u.loop_for.u1.condition_expr_loc);
|
|
|
|
if (token_is (TOK_SEMICOLON))
|
|
{
|
|
dump_continue_iterations_check (ctx_p,
|
|
state_p->u.statement.u.iterational.u.loop_for.next_iter_tgt_pos,
|
|
empty_operand ());
|
|
state_p->state = JSP_STATE_STAT_FOR_FINISH;
|
|
}
|
|
else
|
|
{
|
|
jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, true);
|
|
}
|
|
}
|
|
}
|
|
else if (state_p->state == JSP_STATE_STAT_FOR_FINISH)
|
|
{
|
|
seek_token (ctx_p, state_p->u.statement.u.iterational.u.loop_for.u2.end_loc);
|
|
|
|
state_p->state = JSP_STATE_STAT_ITER_FINISH;
|
|
}
|
|
else if (state_p->state == JSP_STATE_STAT_FOR_IN)
|
|
{
|
|
current_token_must_be_check_and_skip_it (ctx_p, TOK_CLOSE_PAREN);
|
|
|
|
JERRY_ASSERT (is_subexpr_end);
|
|
|
|
locus body_loc = tok.loc;
|
|
|
|
// Dump for-in instruction
|
|
dump_get_value_if_ref (ctx_p, substate_p, true);
|
|
dump_get_value_for_state_if_const (ctx_p, substate_p);
|
|
|
|
jsp_operand_t collection = substate_p->u.expression.operand;
|
|
|
|
JSP_FINISH_SUBEXPR ();
|
|
|
|
state_p->u.statement.u.iterational.u.loop_for_in.header_pos = dump_for_in_for_rewrite (ctx_p, collection);
|
|
|
|
// Dump assignment VariableDeclarationNoIn / LeftHandSideExpression <- VM_REG_SPECIAL_FOR_IN_PROPERTY_NAME
|
|
seek_token (ctx_p, state_p->u.statement.u.iterational.u.loop_for_in.u.iterator_expr_loc);
|
|
|
|
if (token_is (TOK_KW_VAR))
|
|
{
|
|
skip_token (ctx_p);
|
|
|
|
locus name_loc = tok.loc;
|
|
|
|
current_token_must_be (TOK_NAME);
|
|
|
|
const lit_cpointer_t lit_cp = token_data_as_lit_cp ();
|
|
const jsp_operand_t name = jsp_operand_t::make_string_lit_operand (lit_cp);
|
|
|
|
skip_token (ctx_p);
|
|
|
|
if (!jsp_is_dump_mode (ctx_p))
|
|
{
|
|
jsp_early_error_check_for_eval_and_arguments_in_strict_mode (name, jsp_is_strict_mode (ctx_p), name_loc);
|
|
|
|
if (!scopes_tree_variable_declaration_exists (jsp_get_current_scopes_tree_node (ctx_p), lit_cp))
|
|
{
|
|
dump_variable_declaration (ctx_p, lit_cp);
|
|
}
|
|
}
|
|
|
|
if (token_is (TOK_EQ))
|
|
{
|
|
seek_token (ctx_p, name_loc);
|
|
|
|
jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_ASSIGNMENT, false);
|
|
}
|
|
state_p->is_var_decl_no_in = true;
|
|
state_p->u.statement.u.iterational.u.loop_for_in.var_name_lit_cp = lit_cp;
|
|
}
|
|
else
|
|
{
|
|
state_p->is_var_decl_no_in = false;
|
|
state_p->u.statement.u.iterational.u.loop_for_in.var_name_lit_cp = NOT_A_LITERAL;
|
|
jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_LEFTHANDSIDE, true);
|
|
}
|
|
|
|
// Body
|
|
state_p->u.statement.u.iterational.u.loop_for_in.u.body_loc = body_loc;
|
|
|
|
state_p->state = JSP_STATE_STAT_FOR_IN_EXPR;
|
|
}
|
|
else if (state_p->state == JSP_STATE_STAT_FOR_IN_EXPR)
|
|
{
|
|
current_token_must_be (TOK_KW_IN);
|
|
|
|
seek_token (ctx_p, state_p->u.statement.u.iterational.u.loop_for_in.u.body_loc);
|
|
|
|
jsp_operand_t for_in_special_reg = jsp_operand_t::make_reg_operand (VM_REG_SPECIAL_FOR_IN_PROPERTY_NAME);
|
|
|
|
if (!state_p->is_var_decl_no_in)
|
|
{
|
|
JERRY_ASSERT (is_subexpr_end);
|
|
|
|
if (substate_p->is_value_based_reference)
|
|
{
|
|
dump_prop_setter (ctx_p,
|
|
substate_p->u.expression.operand,
|
|
substate_p->u.expression.prop_name_operand,
|
|
for_in_special_reg);
|
|
}
|
|
else
|
|
{
|
|
dump_variable_assignment (ctx_p, substate_p->u.expression.operand, for_in_special_reg);
|
|
}
|
|
|
|
JSP_FINISH_SUBEXPR ();
|
|
}
|
|
else
|
|
{
|
|
JERRY_ASSERT (!is_subexpr_end);
|
|
|
|
lit_cpointer_t var_name_lit_cp = state_p->u.statement.u.iterational.u.loop_for_in.var_name_lit_cp;
|
|
|
|
dump_variable_assignment (ctx_p, jsp_operand_t::make_identifier_operand (var_name_lit_cp), for_in_special_reg);
|
|
}
|
|
|
|
state_p->is_simply_jumpable_border = true;
|
|
|
|
JSP_PUSH_STATE_AND_STATEMENT_PARSE (JSP_STATE_STAT_FOR_IN_FINISH);
|
|
}
|
|
else if (state_p->state == JSP_STATE_STAT_FOR_IN_FINISH)
|
|
{
|
|
// Save LoopEnd locus
|
|
const locus loop_end_loc = tok.loc;
|
|
|
|
// Setup ContinueTarget
|
|
JERRY_ASSERT (state_p->u.statement.u.iterational.continue_tgt_oc == MAX_OPCODES);
|
|
state_p->u.statement.u.iterational.continue_tgt_oc = dumper_get_current_instr_counter (ctx_p);
|
|
|
|
// Write position of for-in end to for_in instruction
|
|
rewrite_for_in (ctx_p, state_p->u.statement.u.iterational.u.loop_for_in.header_pos);
|
|
|
|
// Dump meta (OPCODE_META_TYPE_END_FOR_IN)
|
|
dump_for_in_end (ctx_p);
|
|
|
|
seek_token (ctx_p, loop_end_loc);
|
|
|
|
state_p->state = JSP_STATE_STAT_ITER_FINISH;
|
|
}
|
|
else if (state_p->state == JSP_STATE_STAT_ITER_FINISH)
|
|
{
|
|
JSP_COMPLETE_STATEMENT_PARSE ();
|
|
|
|
jsp_rewrite_jumps_chain (ctx_p,
|
|
&state_p->u.statement.u.iterational.continues_rewrite_chain,
|
|
state_p->u.statement.u.iterational.continue_tgt_oc);
|
|
}
|
|
else if (state_p->state == JSP_STATE_STAT_SWITCH)
|
|
{
|
|
JERRY_ASSERT (is_subexpr_end);
|
|
|
|
parse_expression_inside_parens_end (ctx_p);
|
|
|
|
dump_get_value_if_ref (ctx_p, substate_p, false);
|
|
dump_get_value_for_state_if_const (ctx_p, substate_p);
|
|
|
|
jsp_operand_t switch_expr = substate_p->u.expression.operand;
|
|
|
|
JSP_FINISH_SUBEXPR ();
|
|
|
|
current_token_must_be_check_and_skip_it (ctx_p, TOK_OPEN_BRACE);
|
|
|
|
state_p->state = JSP_STATE_STAT_SWITCH_BRANCH_EXPR;
|
|
|
|
state_p->u.statement.u.switch_statement.expr = switch_expr;
|
|
state_p->u.statement.u.switch_statement.default_label_oc = MAX_OPCODES;
|
|
state_p->u.statement.u.switch_statement.last_cond_check_jmp_oc = MAX_OPCODES;
|
|
state_p->u.statement.u.switch_statement.skip_check_jmp_oc = MAX_OPCODES;
|
|
|
|
dumper_save_reg_alloc_ctx (ctx_p,
|
|
&state_p->u.statement.u.switch_statement.saved_reg_next,
|
|
&state_p->u.statement.u.switch_statement.saved_reg_max_for_temps);
|
|
}
|
|
else if (state_p->state == JSP_STATE_STAT_SWITCH_BRANCH_EXPR)
|
|
{
|
|
if (is_subexpr_end)
|
|
{
|
|
/* finished parse of an Expression in CaseClause */
|
|
dump_get_value_if_ref (ctx_p, substate_p, true);
|
|
dump_get_value_for_state_if_const (ctx_p, substate_p);
|
|
|
|
jsp_operand_t case_expr = substate_p->u.expression.operand;
|
|
|
|
JSP_FINISH_SUBEXPR ();
|
|
|
|
current_token_must_be_check_and_skip_it (ctx_p, TOK_COLON);
|
|
|
|
jsp_operand_t switch_expr = state_p->u.statement.u.switch_statement.expr;
|
|
|
|
jsp_operand_t condition_reg;
|
|
if (case_expr.is_register_operand ())
|
|
{
|
|
/* reuse the register */
|
|
condition_reg = case_expr;
|
|
}
|
|
else
|
|
{
|
|
condition_reg = tmp_operand ();
|
|
}
|
|
|
|
dump_binary_op (ctx_p, VM_OP_EQUAL_VALUE_TYPE, condition_reg, switch_expr, case_expr);
|
|
|
|
jsp_add_conditional_jump_to_rewrite_chain (ctx_p,
|
|
&state_p->u.statement.u.switch_statement.last_cond_check_jmp_oc,
|
|
true,
|
|
condition_reg);
|
|
|
|
uint32_t num = jsp_rewrite_jumps_chain (ctx_p,
|
|
&state_p->u.statement.u.switch_statement.skip_check_jmp_oc,
|
|
dumper_get_current_instr_counter (ctx_p));
|
|
JERRY_ASSERT (num <= 1);
|
|
|
|
jsp_start_statement_parse (ctx_p, JSP_STATE_STAT_STATEMENT_LIST);
|
|
jsp_state_top (ctx_p)->req_state = JSP_STATE_STAT_STATEMENT_LIST;
|
|
}
|
|
else if (token_is (TOK_CLOSE_BRACE))
|
|
{
|
|
skip_token (ctx_p);
|
|
|
|
vm_instr_counter_t last_cond_check_tgt_oc;
|
|
|
|
if (state_p->u.statement.u.switch_statement.default_label_oc != MAX_OPCODES)
|
|
{
|
|
last_cond_check_tgt_oc = state_p->u.statement.u.switch_statement.default_label_oc;
|
|
}
|
|
else
|
|
{
|
|
last_cond_check_tgt_oc = dumper_get_current_instr_counter (ctx_p);
|
|
}
|
|
|
|
uint32_t num = jsp_rewrite_jumps_chain (ctx_p,
|
|
&state_p->u.statement.u.switch_statement.last_cond_check_jmp_oc,
|
|
last_cond_check_tgt_oc);
|
|
JERRY_ASSERT (num <= 1);
|
|
|
|
JSP_COMPLETE_STATEMENT_PARSE ();
|
|
}
|
|
else
|
|
{
|
|
if (is_stmt_list_end
|
|
&& !is_stmt_list_control_flow_exit_stmt_occured
|
|
&& token_is (TOK_KW_CASE))
|
|
{
|
|
jsp_add_simple_jump_to_rewrite_chain (ctx_p,
|
|
&state_p->u.statement.u.switch_statement.skip_check_jmp_oc);
|
|
}
|
|
|
|
if (token_is (TOK_KW_CASE) || token_is (TOK_KW_DEFAULT))
|
|
{
|
|
if (!is_stmt_list_end /* no StatementList[opt] occured in the SwitchStatement yet,
|
|
* so no conditions were checked for now and the DefaultClause
|
|
* should be jumped over */
|
|
&& token_is (TOK_KW_DEFAULT))
|
|
{
|
|
/* first clause is DefaultClause */
|
|
JERRY_ASSERT (state_p->u.statement.u.switch_statement.default_label_oc == MAX_OPCODES);
|
|
|
|
/* the check is unconditional as it is jump over DefaultClause */
|
|
jsp_add_simple_jump_to_rewrite_chain (ctx_p,
|
|
&state_p->u.statement.u.switch_statement.last_cond_check_jmp_oc);
|
|
}
|
|
|
|
if (token_is (TOK_KW_CASE))
|
|
{
|
|
skip_token (ctx_p);
|
|
|
|
uint32_t num = jsp_rewrite_jumps_chain (ctx_p,
|
|
&state_p->u.statement.u.switch_statement.last_cond_check_jmp_oc,
|
|
dumper_get_current_instr_counter (ctx_p));
|
|
JERRY_ASSERT (num <= 1);
|
|
|
|
dumper_restore_reg_alloc_ctx (ctx_p,
|
|
state_p->u.statement.u.switch_statement.saved_reg_next,
|
|
state_p->u.statement.u.switch_statement.saved_reg_max_for_temps,
|
|
false);
|
|
|
|
jsp_push_new_expr_state (ctx_p, JSP_STATE_EXPR_EMPTY, JSP_STATE_EXPR_EXPRESSION, true);
|
|
}
|
|
else
|
|
{
|
|
JERRY_ASSERT (token_is (TOK_KW_DEFAULT));
|
|
skip_token (ctx_p);
|
|
|
|
if (state_p->u.statement.u.switch_statement.default_label_oc != MAX_OPCODES)
|
|
{
|
|
EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "Duplication of 'default' clause");
|
|
}
|
|
|
|
state_p->u.statement.u.switch_statement.default_label_oc = dumper_get_current_instr_counter (ctx_p);
|
|
|
|
uint32_t num = jsp_rewrite_jumps_chain (ctx_p,
|
|
&state_p->u.statement.u.switch_statement.skip_check_jmp_oc,
|
|
dumper_get_current_instr_counter (ctx_p));
|
|
JERRY_ASSERT (num <= 1);
|
|
|
|
current_token_must_be_check_and_skip_it (ctx_p, TOK_COLON);
|
|
|
|
jsp_start_statement_parse (ctx_p, JSP_STATE_STAT_STATEMENT_LIST);
|
|
jsp_state_top (ctx_p)->req_state = JSP_STATE_STAT_STATEMENT_LIST;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "Expected 'case' or' 'default' clause");
|
|
}
|
|
}
|
|
}
|
|
else if (state_p->state == JSP_STATE_STAT_TRY)
|
|
{
|
|
rewrite_try (ctx_p, state_p->u.statement.u.try_statement.try_pos);
|
|
|
|
if (!token_is (TOK_KW_CATCH)
|
|
&& !token_is (TOK_KW_FINALLY))
|
|
{
|
|
EMIT_ERROR (JSP_EARLY_ERROR_SYNTAX, "Expected either 'catch' or 'finally' token");
|
|
}
|
|
|
|
if (token_is (TOK_KW_CATCH))
|
|
{
|
|
skip_token (ctx_p);
|
|
|
|
current_token_must_be_check_and_skip_it (ctx_p, TOK_OPEN_PAREN);
|
|
|
|
current_token_must_be (TOK_NAME);
|
|
|
|
const jsp_operand_t name = jsp_operand_t::make_string_lit_operand (token_data_as_lit_cp ());
|
|
|
|
if (!jsp_is_dump_mode (ctx_p))
|
|
{
|
|
jsp_early_error_check_for_eval_and_arguments_in_strict_mode (name, jsp_is_strict_mode (ctx_p), tok.loc);
|
|
}
|
|
|
|
skip_token (ctx_p);
|
|
|
|
current_token_must_be_check_and_skip_it (ctx_p, TOK_CLOSE_PAREN);
|
|
|
|
state_p->u.statement.u.try_statement.catch_pos = dump_catch_for_rewrite (ctx_p, name);
|
|
|
|
current_token_must_be_check_and_skip_it (ctx_p, TOK_OPEN_BRACE);
|
|
|
|
state_p->state = JSP_STATE_STAT_CATCH_FINISH;
|
|
|
|
JERRY_ASSERT (state_p->is_simply_jumpable_border);
|
|
|
|
jsp_start_statement_parse (ctx_p, JSP_STATE_STAT_BLOCK);
|
|
jsp_start_statement_parse (ctx_p, JSP_STATE_STAT_STATEMENT_LIST);
|
|
jsp_state_top (ctx_p)->req_state = JSP_STATE_STAT_STATEMENT_LIST;
|
|
}
|
|
else
|
|
{
|
|
JERRY_ASSERT (token_is (TOK_KW_FINALLY));
|
|
skip_token (ctx_p);
|
|
|
|
state_p->u.statement.u.try_statement.finally_pos = dump_finally_for_rewrite (ctx_p);
|
|
|
|
current_token_must_be_check_and_skip_it (ctx_p, TOK_OPEN_BRACE);
|
|
|
|
state_p->state = JSP_STATE_STAT_FINALLY_FINISH;
|
|
|
|
JERRY_ASSERT (state_p->is_simply_jumpable_border);
|
|
|
|
jsp_start_statement_parse (ctx_p, JSP_STATE_STAT_BLOCK);
|
|
jsp_start_statement_parse (ctx_p, JSP_STATE_STAT_STATEMENT_LIST);
|
|
jsp_state_top (ctx_p)->req_state = JSP_STATE_STAT_STATEMENT_LIST;
|
|
}
|
|
}
|
|
else if (state_p->state == JSP_STATE_STAT_CATCH_FINISH)
|
|
{
|
|
rewrite_catch (ctx_p, state_p->u.statement.u.try_statement.catch_pos);
|
|
|
|
if (token_is (TOK_KW_FINALLY))
|
|
{
|
|
skip_token (ctx_p);
|
|
state_p->u.statement.u.try_statement.finally_pos = dump_finally_for_rewrite (ctx_p);
|
|
|
|
current_token_must_be_check_and_skip_it (ctx_p, TOK_OPEN_BRACE);
|
|
|
|
state_p->state = JSP_STATE_STAT_FINALLY_FINISH;
|
|
|
|
JERRY_ASSERT (state_p->is_simply_jumpable_border);
|
|
|
|
jsp_start_statement_parse (ctx_p, JSP_STATE_STAT_BLOCK);
|
|
jsp_start_statement_parse (ctx_p, JSP_STATE_STAT_STATEMENT_LIST);
|
|
jsp_state_top (ctx_p)->req_state = JSP_STATE_STAT_STATEMENT_LIST;
|
|
}
|
|
else
|
|
{
|
|
state_p->state = JSP_STATE_STAT_TRY_FINISH;
|
|
}
|
|
}
|
|
else if (state_p->state == JSP_STATE_STAT_FINALLY_FINISH)
|
|
{
|
|
rewrite_finally (ctx_p, state_p->u.statement.u.try_statement.finally_pos);
|
|
|
|
state_p->state = JSP_STATE_STAT_TRY_FINISH;
|
|
}
|
|
else if (state_p->state == JSP_STATE_STAT_TRY_FINISH)
|
|
{
|
|
dump_end_try_catch_finally (ctx_p);
|
|
|
|
JSP_COMPLETE_STATEMENT_PARSE ();
|
|
}
|
|
else if (state_p->state == JSP_STATE_STAT_WITH)
|
|
{
|
|
if (is_subexpr_end)
|
|
{
|
|
parse_expression_inside_parens_end (ctx_p);
|
|
const jsp_operand_t expr = substate_p->u.expression.operand;
|
|
|
|
JSP_FINISH_SUBEXPR ();
|
|
|
|
if (!jsp_is_dump_mode (ctx_p))
|
|
{
|
|
scopes_tree_set_contains_with (jsp_get_current_scopes_tree_node (ctx_p));
|
|
}
|
|
|
|
state_p->is_simply_jumpable_border = true;
|
|
|
|
state_p->u.statement.u.with_statement.header_pos = dump_with_for_rewrite (ctx_p, expr);
|
|
|
|
JSP_PUSH_STATE_AND_STATEMENT_PARSE (JSP_STATE_STAT_WITH);
|
|
}
|
|
else
|
|
{
|
|
rewrite_with (ctx_p, state_p->u.statement.u.with_statement.header_pos);
|
|
dump_with_end (ctx_p);
|
|
|
|
JSP_COMPLETE_STATEMENT_PARSE ();
|
|
}
|
|
}
|
|
else if (state_p->state == JSP_STATE_FUNC_DECL_FINISH)
|
|
{
|
|
JERRY_ASSERT (is_source_elements_list_end);
|
|
jsp_finish_parse_function_scope (ctx_p);
|
|
|
|
JSP_COMPLETE_STATEMENT_PARSE ();
|
|
}
|
|
else if (state_p->state == JSP_STATE_STAT_NAMED_LABEL)
|
|
{
|
|
jsp_state_pop (ctx_p);
|
|
}
|
|
else if (state_p->state == JSP_STATE_STAT_EXPRESSION)
|
|
{
|
|
JERRY_ASSERT (is_subexpr_end);
|
|
insert_semicolon (ctx_p);
|
|
|
|
dump_get_value_if_ref (ctx_p, substate_p, false);
|
|
|
|
if (jsp_get_scope_type (ctx_p) == SCOPE_TYPE_EVAL)
|
|
{
|
|
JERRY_ASSERT (substate_p->is_need_retval);
|
|
|
|
dump_variable_assignment (ctx_p,
|
|
jsp_operand_t::make_reg_operand (VM_REG_SPECIAL_EVAL_RET),
|
|
substate_p->u.expression.operand);
|
|
}
|
|
|
|
JSP_FINISH_SUBEXPR ();
|
|
|
|
JSP_COMPLETE_STATEMENT_PARSE ();
|
|
}
|
|
else if (state_p->state == JSP_STATE_STAT_RETURN)
|
|
{
|
|
JERRY_ASSERT (is_subexpr_end);
|
|
|
|
dump_get_value_if_ref (ctx_p, substate_p, true);
|
|
dump_get_value_for_state_if_const (ctx_p, substate_p);
|
|
|
|
const jsp_operand_t op = substate_p->u.expression.operand;
|
|
|
|
JSP_FINISH_SUBEXPR ();
|
|
|
|
dump_retval (ctx_p, op);
|
|
|
|
insert_semicolon (ctx_p);
|
|
|
|
JSP_COMPLETE_STATEMENT_PARSE ();
|
|
}
|
|
else if (state_p->state == JSP_STATE_STAT_THROW)
|
|
{
|
|
JERRY_ASSERT (is_subexpr_end);
|
|
|
|
dump_get_value_if_ref (ctx_p, substate_p, true);
|
|
dump_get_value_for_state_if_const (ctx_p, substate_p);
|
|
|
|
const jsp_operand_t op = substate_p->u.expression.operand;
|
|
|
|
JSP_FINISH_SUBEXPR ();
|
|
|
|
dump_throw (ctx_p, op);
|
|
|
|
insert_semicolon (ctx_p);
|
|
|
|
JSP_COMPLETE_STATEMENT_PARSE ();
|
|
}
|
|
else
|
|
{
|
|
JERRY_ASSERT (state_p->state == JSP_STATE_STAT_STATEMENT);
|
|
JERRY_ASSERT (!state_p->is_completed);
|
|
|
|
vm_instr_counter_t break_tgt_oc = dumper_get_current_instr_counter (ctx_p);
|
|
jsp_rewrite_jumps_chain (ctx_p,
|
|
&state_p->u.statement.breaks_rewrite_chain,
|
|
break_tgt_oc);
|
|
|
|
state_p->is_completed = true;
|
|
}
|
|
|
|
JERRY_ASSERT (substate_p == NULL);
|
|
}
|
|
} /* jsp_parse_source_element_list */
|
|
|
|
/**
|
|
* Parse program
|
|
*
|
|
* program
|
|
* : LT!* source_element_list LT!* EOF!
|
|
* ;
|
|
*
|
|
* @return true - if parse finished successfully (no SyntaxError was raised);
|
|
* false - otherwise.
|
|
*/
|
|
static jsp_status_t
|
|
parser_parse_program (const jerry_api_char_t *source_p, /**< source code buffer */
|
|
size_t source_size, /**< source code size in bytes */
|
|
bool in_eval, /**< flag indicating if we are parsing body of eval code */
|
|
bool is_strict, /**< flag, indicating whether current code
|
|
* inherited strict mode from code of an outer scope */
|
|
const bytecode_data_header_t **out_bytecode_data_p, /**< out: generated byte-code array
|
|
* (in case there were no syntax errors) */
|
|
bool *out_contains_functions_p) /**< out: optional (can be NULL, if the output is not needed)
|
|
* flag, indicating whether the compiled byte-code
|
|
* contains a function declaration / expression */
|
|
{
|
|
JERRY_ASSERT (out_bytecode_data_p != NULL);
|
|
|
|
const scope_type_t scope_type = (in_eval ? SCOPE_TYPE_EVAL : SCOPE_TYPE_GLOBAL);
|
|
|
|
#ifndef JERRY_NDEBUG
|
|
volatile bool is_pre_parse_finished = false;
|
|
#endif /* !JERRY_NDEBUG */
|
|
|
|
jsp_status_t status;
|
|
|
|
jsp_mm_init ();
|
|
|
|
jsp_ctx_t ctx;
|
|
jsp_init_ctx (&ctx, scope_type);
|
|
|
|
jsp_stack_init (&ctx);
|
|
|
|
dumper_init (&ctx, parser_show_instrs);
|
|
jsp_early_error_init ();
|
|
|
|
scopes_tree_init ();
|
|
|
|
scopes_tree scope = scopes_tree_new_scope (NULL, scope_type);
|
|
scopes_tree_set_strict_mode (scope, is_strict);
|
|
|
|
jsp_set_current_scopes_tree_node (&ctx, scope);
|
|
|
|
jmp_buf *jsp_early_error_label_p = jsp_early_error_get_early_error_longjmp_label ();
|
|
int r = setjmp (*jsp_early_error_label_p);
|
|
|
|
if (r == 0)
|
|
{
|
|
/*
|
|
* Note:
|
|
* Operations that could raise an early error can be performed only during execution of the block.
|
|
*/
|
|
lexer_init (source_p, source_size, parser_show_instrs);
|
|
skip_token (&ctx);
|
|
|
|
locus start_loc = tok.loc;
|
|
|
|
jsp_parse_directive_prologue (&ctx);
|
|
|
|
jsp_parse_source_element_list (&ctx, scope_type);
|
|
JERRY_ASSERT (token_is (TOK_EOF));
|
|
|
|
jsp_set_current_scopes_tree_node (&ctx, NULL);
|
|
|
|
scopes_tree_finish_build ();
|
|
|
|
#ifndef JERRY_NDEBUG
|
|
is_pre_parse_finished = true;
|
|
#endif /* !JERRY_NDEBUG */
|
|
|
|
jsp_early_error_free ();
|
|
|
|
if (out_contains_functions_p != NULL)
|
|
{
|
|
*out_contains_functions_p = scope->contains_functions;
|
|
}
|
|
|
|
jsp_switch_to_dump_mode (&ctx, scope);
|
|
|
|
scopes_tree scope_to_dump_p = jsp_get_next_scopes_tree_node_to_dump (&ctx);
|
|
JERRY_ASSERT (scope_to_dump_p == scope);
|
|
|
|
bytecode_data_header_t *root_bc_header_p = bc_dump_single_scope (scope);
|
|
|
|
scopes_tree_free_scope (scope);
|
|
|
|
bc_register_root_bytecode_header (root_bc_header_p);
|
|
|
|
*out_bytecode_data_p = root_bc_header_p;
|
|
|
|
jsp_set_current_bytecode_header (&ctx, root_bc_header_p);
|
|
|
|
seek_token (&ctx, start_loc);
|
|
|
|
jsp_parse_source_element_list (&ctx, scope_type);
|
|
|
|
jsp_set_current_bytecode_header (&ctx, NULL);
|
|
|
|
jsp_stack_finalize (&ctx);
|
|
|
|
JERRY_ASSERT (jsp_get_scope_type (&ctx) == scope_type);
|
|
|
|
if (parser_show_instrs)
|
|
{
|
|
lit_dump_literals ();
|
|
bc_print_instrs (root_bc_header_p);
|
|
}
|
|
|
|
scopes_tree_finalize ();
|
|
|
|
status = JSP_STATUS_OK;
|
|
}
|
|
else
|
|
{
|
|
/* SyntaxError handling */
|
|
|
|
#ifndef JERRY_NDEBUG
|
|
JERRY_ASSERT (!is_pre_parse_finished);
|
|
#endif /* !JERRY_NDEBUG */
|
|
|
|
*out_bytecode_data_p = NULL;
|
|
|
|
jsp_mm_free_all ();
|
|
|
|
jsp_early_error_t type = jsp_early_error_get_type ();
|
|
|
|
if (type == JSP_EARLY_ERROR_SYNTAX)
|
|
{
|
|
status = JSP_STATUS_SYNTAX_ERROR;
|
|
}
|
|
else
|
|
{
|
|
JERRY_ASSERT (type == JSP_EARLY_ERROR_REFERENCE);
|
|
|
|
status = JSP_STATUS_REFERENCE_ERROR;
|
|
}
|
|
}
|
|
|
|
jsp_mm_finalize ();
|
|
|
|
return status;
|
|
} /* parser_parse_program */
|
|
|
|
/**
|
|
* Parse source script
|
|
*
|
|
* @return true - if parse finished successfully (no SyntaxError were raised);
|
|
* false - otherwise.
|
|
*/
|
|
jsp_status_t
|
|
parser_parse_script (const jerry_api_char_t *source, /**< source script */
|
|
size_t source_size, /**< source script size it bytes */
|
|
const bytecode_data_header_t **out_bytecode_data_p) /**< out: generated byte-code array
|
|
* (in case there were no syntax errors) */
|
|
{
|
|
return parser_parse_program (source, source_size, false, false, out_bytecode_data_p, NULL);
|
|
} /* parser_parse_script */
|
|
|
|
/**
|
|
* Parse string passed to eval() call
|
|
*
|
|
* @return true - if parse finished successfully (no SyntaxError were raised);
|
|
* false - otherwise.
|
|
*/
|
|
jsp_status_t
|
|
parser_parse_eval (const jerry_api_char_t *source, /**< string passed to eval() */
|
|
size_t source_size, /**< string size in bytes */
|
|
bool is_strict, /**< flag, indicating whether eval is called
|
|
* from strict code in direct mode */
|
|
const bytecode_data_header_t **out_bytecode_data_p, /**< out: generated byte-code array
|
|
* (in case there were no syntax errors) */
|
|
bool *out_contains_functions_p) /**< out: flag, indicating whether the compiled byte-code
|
|
* contains a function declaration / expression */
|
|
{
|
|
JERRY_ASSERT (out_contains_functions_p != NULL);
|
|
|
|
return parser_parse_program (source,
|
|
source_size,
|
|
true,
|
|
is_strict,
|
|
out_bytecode_data_p,
|
|
out_contains_functions_p);
|
|
} /* parser_parse_eval */
|
|
|
|
/**
|
|
* Tell parser whether to dump bytecode
|
|
*/
|
|
void
|
|
parser_set_show_instrs (bool show_instrs) /**< flag indicating whether to dump bytecode */
|
|
{
|
|
parser_show_instrs = show_instrs;
|
|
} /* parser_set_show_instrs */
|