Support labelled statements.

Related issue: #52

JerryScript-DCO-1.0-Signed-off-by: Ruben Ayrapetyan r.ayrapetyan@samsung.com
This commit is contained in:
Ruben Ayrapetyan
2015-06-03 20:57:15 +03:00
parent 2de49cdba7
commit d99d779486
11 changed files with 1014 additions and 297 deletions
+295
View File
@@ -0,0 +1,295 @@
/* Copyright 2015 Samsung Electronics Co., Ltd.
*
* 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 "jsp-label.h"
#include "lexer.h"
#include "opcodes-dumper.h"
/** \addtogroup jsparser ECMAScript parser
* @{
*
* \addtogroup labels Jump labels
* @{
*/
/**
* Stack, containing current label set
*/
jsp_label_t *label_set_p = NULL;
/**
* Initialize jumps labels mechanism
*/
void
jsp_label_init (void)
{
JERRY_ASSERT (label_set_p == NULL);
} /* jsp_label_init */
/**
* Finalize jumps labels mechanism
*/
void
jsp_label_finalize (void)
{
JERRY_ASSERT (label_set_p == NULL);
} /* jsp_label_finalize */
/**
* Add label to the current label set
*/
void
jsp_label_push (jsp_label_t *out_label_p, /**< out: place where label structure
* should be initialized and
* linked into label set stack */
jsp_label_type_flag_t type_mask, /**< label's type mask */
token id) /**< identifier of the label (TOK_NAME)
* if mask includes JSP_LABEL_TYPE_NAMED,
* or empty token - otherwise */
{
JERRY_ASSERT (out_label_p != NULL);
if (type_mask & JSP_LABEL_TYPE_NAMED)
{
JERRY_ASSERT (id.type == TOK_NAME);
JERRY_ASSERT (jsp_label_find (JSP_LABEL_TYPE_NAMED, id, NULL) == NULL);
}
else
{
JERRY_ASSERT (id.type == TOK_EMPTY);
}
out_label_p->type_mask = type_mask;
out_label_p->id = id;
out_label_p->continue_tgt_oc = MAX_OPCODES;
out_label_p->breaks_list_oc = MAX_OPCODES;
out_label_p->breaks_number = 0;
out_label_p->continues_list_oc = MAX_OPCODES;
out_label_p->continues_number = 0;
out_label_p->next_label_p = label_set_p;
out_label_p->is_nested_jumpable_border = false;
label_set_p = out_label_p;
} /* jsp_label_push */
/**
* Rewrite jumps to the label, if there any,
* and remove it from the current label set
*
* @return the label should be on top of label set stack
*/
void
jsp_label_rewrite_jumps_and_pop (jsp_label_t *label_p, /**< label to remove (should be on top of stack) */
opcode_counter_t break_tgt_oc) /**< target opcode counter
* for breaks on the label */
{
JERRY_ASSERT (label_p != NULL);
JERRY_ASSERT (break_tgt_oc != MAX_OPCODES);
JERRY_ASSERT (label_set_p == label_p);
/* Iterating jumps list, rewriting them */
while (label_p->breaks_number--)
{
JERRY_ASSERT (label_p->breaks_list_oc != MAX_OPCODES);
label_p->breaks_list_oc = rewrite_simple_or_nested_jump_and_get_next (label_p->breaks_list_oc,
break_tgt_oc);
}
while (label_p->continues_number--)
{
JERRY_ASSERT (label_p->continue_tgt_oc != MAX_OPCODES);
JERRY_ASSERT (label_p->continues_list_oc != MAX_OPCODES);
label_p->continues_list_oc = rewrite_simple_or_nested_jump_and_get_next (label_p->continues_list_oc,
label_p->continue_tgt_oc);
}
label_set_p = label_set_p->next_label_p;
} /* jsp_label_rewrite_jumps_and_pop */
/**
* Find label with specified identifier
*
* @return if found, pointer to label descriptor,
* otherwise - NULL.
*/
jsp_label_t*
jsp_label_find (jsp_label_type_flag_t type_mask, /**< types to search for */
token id, /**< identifier of the label (TOK_NAME)
* if mask equals to JSP_LABEL_TYPE_NAMED,
* or empty token - otherwise
* (if so, mask should not include JSP_LABEL_TYPE_NAMED) */
bool *out_is_simply_jumpable_p) /**< out: is the label currently
* accessible with a simple jump */
{
bool is_search_named = (type_mask == JSP_LABEL_TYPE_NAMED);
if (is_search_named)
{
JERRY_ASSERT (id.type == TOK_NAME);
}
else
{
JERRY_ASSERT (!(type_mask & JSP_LABEL_TYPE_NAMED));
JERRY_ASSERT (id.type == TOK_EMPTY);
}
bool is_simply_jumpable = true;
jsp_label_t *ret_label_p = NULL;
for (jsp_label_t *label_iter_p = label_set_p;
label_iter_p != NULL;
label_iter_p = label_iter_p->next_label_p)
{
if (label_iter_p->is_nested_jumpable_border)
{
is_simply_jumpable = false;
}
bool is_named_label = (label_iter_p->type_mask & JSP_LABEL_TYPE_NAMED);
if ((is_search_named
&& is_named_label
&& lexer_are_tokens_with_same_identifier (label_iter_p->id, id))
|| (!is_search_named
&& (type_mask & label_iter_p->type_mask)))
{
ret_label_p = label_iter_p;
break;
}
}
if (out_is_simply_jumpable_p != NULL)
{
*out_is_simply_jumpable_p = is_simply_jumpable;
}
return ret_label_p;
} /* jsp_label_find */
/**
* Dump jump and register it in the specified label to be rewritten later (see also: jsp_label_rewrite_jumps_and_pop)
*
* Warning:
* The dumped instruction should not be modified before it is rewritten, as its idx fields are used
* to link jump instructions related to the label into singly linked list.
*/
void
jsp_label_add_jump (jsp_label_t *label_p, /**< label to register jump for */
bool is_simply_jumpable, /**< is the label currently
* accessible with a simple jump */
bool is_break) /**< type of jump - 'break' (true) or 'continue' (false) */
{
JERRY_ASSERT (label_p != NULL);
if (is_break)
{
label_p->breaks_list_oc = dump_simple_or_nested_jump_for_rewrite (is_simply_jumpable,
label_p->breaks_list_oc);
label_p->breaks_number++;
}
else
{
label_p->continues_list_oc = dump_simple_or_nested_jump_for_rewrite (is_simply_jumpable,
label_p->continues_list_oc);
label_p->continues_number++;
}
} /* jsp_label_add_jump */
/**
* Setup target for 'continue' jumps,
* associated with the labels, from innermost
* to the specified label.
*/
void
jsp_label_setup_continue_target (jsp_label_t *outermost_label_p, /**< the outermost label to setup target for */
opcode_counter_t tgt_oc) /**< target */
{
/* There are no labels that could not be targeted with 'break' jumps */
JERRY_ASSERT (tgt_oc != MAX_OPCODES);
JERRY_ASSERT (outermost_label_p != NULL);
for (jsp_label_t *label_iter_p = label_set_p;
label_iter_p != outermost_label_p->next_label_p;
label_iter_p = label_iter_p->next_label_p)
{
JERRY_ASSERT (label_iter_p != NULL);
JERRY_ASSERT (label_iter_p->continue_tgt_oc == MAX_OPCODES);
label_iter_p->continue_tgt_oc = tgt_oc;
}
} /* jsp_label_setup_continue_target */
/**
* Mark current label, if any, as nested jumpable border
*/
void
jsp_label_raise_nested_jumpable_border (void)
{
if (label_set_p != NULL)
{
JERRY_ASSERT (!label_set_p->is_nested_jumpable_border);
label_set_p->is_nested_jumpable_border = true;
}
} /* jsp_label_raise_nested_jumpable_border */
/**
* Unmark current label, if any, as nested jumpable border
*/
void
jsp_label_remove_nested_jumpable_border (void)
{
if (label_set_p != NULL)
{
JERRY_ASSERT (label_set_p->is_nested_jumpable_border);
label_set_p->is_nested_jumpable_border = false;
}
} /* jsp_label_remove_nested_jumpable_border */
/**
* Mask current label set to restore it later, and start new label set
*
* @return pointer to masked label set's list of labels
*/
jsp_label_t*
jsp_label_mask_set (void)
{
jsp_label_t *ret_p = label_set_p;
label_set_p = NULL;
return ret_p;
} /* jsp_label_mask_set */
/**
* Restore previously masked label set
*
* Note:
* current label set should be empty
*/
void
jsp_label_restore_set (jsp_label_t *masked_label_set_list_p) /**< list of labels of
* a masked label set */
{
JERRY_ASSERT (label_set_p == NULL);
label_set_p = masked_label_set_list_p;
} /* jsp_label_restore_set */
/**
* @}
* @}
*/
+89
View File
@@ -0,0 +1,89 @@
/* Copyright 2015 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef JSP_LABEL_H
#define JSP_LABEL_H
#include "lexer.h"
#include "opcodes.h"
/** \addtogroup jsparser ECMAScript parser
* @{
*
* \addtogroup labels Jump labels
* @{
*/
/**
* Label types
*/
typedef enum
{
JSP_LABEL_TYPE_NAMED = (1 << 0), /**< label for breaks and continues with identifiers */
JSP_LABEL_TYPE_UNNAMED_BREAKS = (1 << 1), /**< label for breaks without identifiers */
JSP_LABEL_TYPE_UNNAMED_CONTINUES = (1 << 2) /**< label for continues without identifiers */
} jsp_label_type_flag_t;
/**
* Descriptor of a jump label (See also: ECMA-262 v5, 12.12, Labelled statements)
*
* Note:
* Jump instructions with target identified by some specific label,
* are linked into singly-linked list.
*
* Pointer to a next element of the list is represented with opcode counter.
* stored in instructions linked into the list.
*
*/
typedef struct jsp_label_t
{
jsp_label_type_flag_t type_mask; /**< label type mask */
token id; /**< label name (TOK_NAME), if type is LABEL_NAMED */
opcode_counter_t continue_tgt_oc; /**< target opcode counter for continues on the label */
opcode_counter_t breaks_list_oc; /**< opcode counter of first 'break' instruction in the list
* of instructions with the target identified by the label */
opcode_counter_t breaks_number; /**< number of 'break' instructions in the list */
opcode_counter_t continues_list_oc; /**< opcode counter of first 'continue' instruction in the list
* of instructions with the target identified by the label */
opcode_counter_t continues_number; /**< number of 'continue' instructions in the list */
jsp_label_t *next_label_p; /**< next label in current label set stack */
bool is_nested_jumpable_border : 1; /**< flag, indicating that this and outer labels
* are not currently accessible with simple jumps,
* and so should be targetted with nested jumps only */
} jsp_label_t;
extern void jsp_label_init (void);
extern void jsp_label_finalize (void);
extern void jsp_label_push (jsp_label_t *out_label_p, jsp_label_type_flag_t type_mask, token id);
extern void jsp_label_rewrite_jumps_and_pop (jsp_label_t *label_p, opcode_counter_t break_tgt_oc);
extern jsp_label_t *jsp_label_find (jsp_label_type_flag_t type_mask, token id, bool *out_is_simply_jumpable_p);
extern void jsp_label_add_jump (jsp_label_t *label_p, bool is_simply_jumpable, bool is_break);
extern void jsp_label_setup_continue_target (jsp_label_t *outermost_label_p, opcode_counter_t tgt_oc);
extern void jsp_label_raise_nested_jumpable_border (void);
extern void jsp_label_remove_nested_jumpable_border (void);
extern jsp_label_t *jsp_label_mask_set (void);
extern void jsp_label_restore_set (jsp_label_t *masked_label_set_list_p);
/**
* @}
* @}
*/
#endif /* !JSP_LABEL_H */
+20
View File
@@ -1596,6 +1596,26 @@ lexer_set_strict_mode (bool is_strict)
strict_mode = is_strict;
}
/**
* Check whether the identifier tokens represent the same identifiers
*
* Note:
* As all literals represent unique strings,
* it is sufficient to just check if literal indices
* in the tokens are equal.
*
* @return true / false
*/
bool
lexer_are_tokens_with_same_identifier (token id1, /**< identifier token (TOK_NAME) */
token id2) /**< identifier token (TOK_NAME) */
{
JERRY_ASSERT (id1.type == TOK_NAME);
JERRY_ASSERT (id2.type == TOK_NAME);
return (id1.uid == id2.uid);
} /* lexer_are_tokens_with_same_identifier */
void
lexer_init (const char *source, size_t source_size, bool show_opcodes)
{
+7
View File
@@ -163,6 +163,11 @@ typedef struct
literal_index_t uid;
} token;
/**
* Initializer for empty token
*/
#define TOKEN_EMPTY_INITIALIZER {0, TOK_EMPTY, 0}
void lexer_init (const char *, size_t, bool);
void lexer_free (void);
@@ -185,4 +190,6 @@ const char *lexer_token_type_to_string (token_type);
void lexer_set_strict_mode (bool);
extern bool lexer_are_tokens_with_same_identifier (token id1, token id2);
#endif
+47 -146
View File
@@ -76,30 +76,6 @@ enum
};
STATIC_STACK (prop_getters, op_meta)
enum
{
breaks_global_size
};
STATIC_STACK (breaks, opcode_counter_t)
enum
{
break_targets_global_size
};
STATIC_STACK (break_targets, opcode_counter_t)
enum
{
continues_global_size
};
STATIC_STACK (continues, opcode_counter_t)
enum
{
continue_targets_global_size
};
STATIC_STACK (continue_targets, opcode_counter_t)
enum
{
next_iterations_global_size
@@ -2079,30 +2055,6 @@ dump_prop_setter_or_bitwise_or_res (operand res, operand op)
return dump_prop_setter_or_triple_address_res (dump_bitwise_or, res, op);
}
void
start_collecting_breaks (void)
{
STACK_PUSH (U8, (uint8_t) STACK_SIZE (breaks));
}
void
start_collecting_continues (void)
{
STACK_PUSH (U8, (uint8_t) STACK_SIZE (continues));
}
void
dumper_set_break_target (void)
{
STACK_PUSH (break_targets, serializer_get_current_opcode_counter ());
}
void
dumper_set_continue_target (void)
{
STACK_PUSH (continue_targets, serializer_get_current_opcode_counter ());
}
void
dumper_set_next_interation_target (void)
{
@@ -2143,126 +2095,83 @@ dump_continue_iterations_check (operand op)
}
/**
* Dump template of 'jmp_break_continue' instruction.
* Dump template of 'jmp_break_continue' or 'jmp_down' instruction (depending on is_simple_jump argument).
*
* Note:
* the instruction's flags field is written later (see also: rewrite_breaks, rewrite_continues).
* the instruction's flags field is written later (see also: rewrite_simple_or_nested_jump_get_next).
*
* @return position of dumped instruction
*/
void
dump_break_continue_for_rewrite (bool is_break, /**< flag indicating whether 'break' should be dumped (true)
* or 'continue' (false) */
bool is_simple_jump) /**< flag indicating, whether simple jump
* or 'jmp_break_continue' template should be dumped */
opcode_counter_t
dump_simple_or_nested_jump_for_rewrite (bool is_simple_jump, /**< flag indicating, whether simple jump
* or 'jmp_break_continue' template should be dumped */
opcode_counter_t next_jump_for_tgt_oc) /**< opcode counter of next
* template targetted to
* the same target - if any,
* or MAX_OPCODES - otherwise */
{
if (is_break)
{
STACK_PUSH (breaks, serializer_get_current_opcode_counter ());
}
else
{
STACK_PUSH (continues, serializer_get_current_opcode_counter ());
}
idx_t id1, id2;
split_opcode_counter (next_jump_for_tgt_oc, &id1, &id2);
opcode_t opcode;
if (is_simple_jump)
{
opcode = getop_jmp_down (INVALID_VALUE, INVALID_VALUE);
opcode = getop_jmp_down (id1, id2);
}
else
{
opcode = getop_jmp_break_continue (INVALID_VALUE, INVALID_VALUE);
opcode = getop_jmp_break_continue (id1, id2);
}
opcode_counter_t ret = serializer_get_current_opcode_counter ();
serializer_dump_op_meta (create_op_meta_000 (opcode));
} /* dump_break_continue_for_rewrite */
return ret;
} /* dump_simple_or_nested_jump_for_rewrite */
/**
* Write jump target positions into previously dumped templates of instructions
* for currently processed set of 'break' statements.
* Write jump target position into previously dumped template of jump (simple or nested) instruction
*
* See also:
* dump_break_continue_for_rewrite
* start_collecting_breaks
* @return opcode counter value that was encoded in the jump before rewrite
*/
void
rewrite_breaks (void)
opcode_counter_t
rewrite_simple_or_nested_jump_and_get_next (opcode_counter_t jump_oc, /**< position of jump to rewrite */
opcode_counter_t target_oc) /**< the jump's target */
{
const opcode_counter_t break_target = STACK_TOP (break_targets);
op_meta jump_op_meta = serializer_get_op_meta (jump_oc);
STACK_ITERATE (breaks, break_oc, STACK_TOP (U8))
bool is_simple_jump = (jump_op_meta.op.op_idx == OPCODE (jmp_down));
JERRY_ASSERT (is_simple_jump
|| (jump_op_meta.op.op_idx == OPCODE (jmp_break_continue)));
idx_t id1, id2, id1_prev, id2_prev;
split_opcode_counter ((opcode_counter_t) (target_oc - jump_oc), &id1, &id2);
if (is_simple_jump)
{
idx_t id1, id2;
split_opcode_counter ((opcode_counter_t) (break_target - break_oc), &id1, &id2);
op_meta break_op_meta = serializer_get_op_meta (break_oc);
id1_prev = jump_op_meta.op.data.jmp_down.opcode_1;
id2_prev = jump_op_meta.op.data.jmp_down.opcode_2;
bool is_simple_jump = (break_op_meta.op.op_idx == OPCODE (jmp_down));
if (is_simple_jump)
{
break_op_meta.op.data.jmp_down.opcode_1 = id1;
break_op_meta.op.data.jmp_down.opcode_2 = id2;
}
else
{
JERRY_ASSERT (break_op_meta.op.op_idx == OPCODE (jmp_break_continue));
break_op_meta.op.data.jmp_break_continue.opcode_1 = id1;
break_op_meta.op.data.jmp_break_continue.opcode_2 = id2;
}
serializer_rewrite_op_meta (break_oc, break_op_meta);
jump_op_meta.op.data.jmp_down.opcode_1 = id1;
jump_op_meta.op.data.jmp_down.opcode_2 = id2;
}
STACK_ITERATE_END ();
STACK_DROP (break_targets, 1);
STACK_DROP (breaks, STACK_SIZE (breaks) - STACK_TOP (U8));
STACK_DROP (U8, 1);
} /* rewrite_breaks */
/**
* Write jump target positions into previously dumped templates of instructions
* for currently processed set of 'continue' statements.
*
* See also:
* dump_break_continue_for_rewrite
* start_collecting_continues
*/
void
rewrite_continues (void)
{
const opcode_counter_t continue_target = STACK_TOP (continue_targets);
STACK_ITERATE (continues, continue_oc, STACK_TOP (U8))
else
{
idx_t id1, id2;
split_opcode_counter ((opcode_counter_t) (continue_target - continue_oc), &id1, &id2);
op_meta continue_op_meta = serializer_get_op_meta (continue_oc);
JERRY_ASSERT (jump_op_meta.op.op_idx == OPCODE (jmp_break_continue));
bool is_simple_jump = (continue_op_meta.op.op_idx == OPCODE (jmp_down));
id1_prev = jump_op_meta.op.data.jmp_break_continue.opcode_1;
id2_prev = jump_op_meta.op.data.jmp_break_continue.opcode_2;
if (is_simple_jump)
{
continue_op_meta.op.data.jmp_down.opcode_1 = id1;
continue_op_meta.op.data.jmp_down.opcode_2 = id2;
}
else
{
JERRY_ASSERT (continue_op_meta.op.op_idx == OPCODE (jmp_break_continue));
continue_op_meta.op.data.jmp_break_continue.opcode_1 = id1;
continue_op_meta.op.data.jmp_break_continue.opcode_2 = id2;
}
serializer_rewrite_op_meta (continue_oc, continue_op_meta);
jump_op_meta.op.data.jmp_break_continue.opcode_1 = id1;
jump_op_meta.op.data.jmp_break_continue.opcode_2 = id2;
}
STACK_ITERATE_END ();
STACK_DROP (continue_targets, 1);
STACK_DROP (continues, STACK_SIZE (continues) - STACK_TOP (U8));
STACK_DROP (U8, 1);
} /* rewrite_continues */
serializer_rewrite_op_meta (jump_oc, jump_op_meta);
return calc_opcode_counter_from_idx_idx (id1_prev, id2_prev);
} /* rewrite_simple_or_nested_jump_get_next */
void
start_dumping_case_clauses (void)
@@ -2576,10 +2485,6 @@ dumper_init (void)
STACK_INIT (conditional_checks);
STACK_INIT (jumps_to_end);
STACK_INIT (prop_getters);
STACK_INIT (breaks);
STACK_INIT (continues);
STACK_INIT (break_targets);
STACK_INIT (continue_targets);
STACK_INIT (next_iterations);
STACK_INIT (case_clauses);
STACK_INIT (catches);
@@ -2600,10 +2505,6 @@ dumper_free (void)
STACK_FREE (conditional_checks);
STACK_FREE (jumps_to_end);
STACK_FREE (prop_getters);
STACK_FREE (breaks);
STACK_FREE (continues);
STACK_FREE (break_targets);
STACK_FREE (continue_targets);
STACK_FREE (next_iterations);
STACK_FREE (case_clauses);
STACK_FREE (catches);
+6 -5
View File
@@ -178,14 +178,15 @@ operand dump_prop_setter_or_bitwise_and_res (operand, operand);
operand dump_prop_setter_or_bitwise_xor_res (operand, operand);
operand dump_prop_setter_or_bitwise_or_res (operand, operand);
void start_collecting_breaks (void);
void start_collecting_continues (void);
void dumper_set_break_target (void);
void dumper_set_continue_target (void);
void dumper_set_next_interation_target (void);
void dump_break_continue_for_rewrite (bool is_break, bool is_simple_jump);
void rewrite_continues (void);
void rewrite_breaks (void);
opcode_counter_t
dump_simple_or_nested_jump_for_rewrite (bool is_simple_jump,
opcode_counter_t next_jump_for_tg_oc);
opcode_counter_t
rewrite_simple_or_nested_jump_and_get_next (opcode_counter_t jump_oc,
opcode_counter_t target_oc);
void dump_continue_iterations_check (operand);
void start_dumping_case_clauses (void);
+168 -146
View File
@@ -16,6 +16,7 @@
#include <stdarg.h>
#include "jrt-libc-includes.h"
#include "jsp-label.h"
#include "parser.h"
#include "opcodes.h"
#include "serializer.h"
@@ -82,7 +83,7 @@ STATIC_STACK (scopes, scopes_tree)
#define OPCODE_IS(OP, ID) (OP.op_idx == __op__idx_##ID)
static operand parse_expression (bool);
static void parse_statement (void);
static void parse_statement (jsp_label_t *outermost_stmt_label_p);
static operand parse_assignment_expression (bool);
static void parse_source_element_list (bool);
static operand parse_argument_list (varg_list_type, operand, uint8_t *, operand *);
@@ -109,45 +110,6 @@ pop_nesting (nesting_t nesting_type) /**< type of current nesting */
STACK_DROP (nestings, 1);
} /* pop_nesting */
/**
* Check that current nesting chain contains one of the specified 'inside' nesting types,
* and there is no 'not_in' nesting between current nesting and the 'inside' nesting type
* in the chain.
*/
static void
must_be_inside_but_not_in (uint8_t not_in, /**< 'not_in' nesting type */
uint8_t insides_count, /**< 'inside' nestings number */
...) /**< 'inside' nestings list */
{
JERRY_ASSERT (STACK_SIZE (nestings) != 0);
va_list insides_list;
va_start (insides_list, insides_count);
uint8_t *insides = (uint8_t*) mem_heap_alloc_block (insides_count, MEM_HEAP_ALLOC_SHORT_TERM);
for (uint8_t i = 0; i < insides_count; i++)
{
insides[i] = (uint8_t) va_arg (insides_list, int);
}
va_end (insides_list);
for (uint8_t i = (uint8_t) STACK_SIZE (nestings); i != 0; i--)
{
for (uint8_t j = 0; j < insides_count; j++)
{
if (insides[j] == STACK_ELEMENT (nestings, i - 1))
{
mem_heap_free_block (insides);
return;
}
}
if (STACK_ELEMENT (nestings, i - 1) == not_in)
{
EMIT_ERROR_VARG ("Shall not be inside a '%s' nesting", NESTING_TO_STRING (not_in));
}
}
EMIT_ERROR ("Shall be inside a nesting");
} /* must_be_inside_but_not_in */
static bool
token_is (token_type tt)
{
@@ -344,9 +306,15 @@ parse_property_assignment (void)
token_after_newlines_must_be (TOK_OPEN_BRACE);
skip_newlines ();
jsp_label_t *masked_label_set_p = jsp_label_mask_set ();
push_nesting (NESTING_FUNCTION);
parse_source_element_list (false);
pop_nesting (NESTING_FUNCTION);
jsp_label_restore_set (masked_label_set_p);
token_after_newlines_must_be (TOK_CLOSE_BRACE);
scopes_tree_set_strict_mode (STACK_TOP (scopes), is_strict);
@@ -545,6 +513,8 @@ parse_function_declaration (void)
assert_keyword (KW_FUNCTION);
jsp_label_t *masked_label_set_p = jsp_label_mask_set ();
token_after_newlines_must_be (TOK_NAME);
const operand name = literal_operand (token_data ());
@@ -560,6 +530,7 @@ parse_function_declaration (void)
token_after_newlines_must_be (TOK_OPEN_BRACE);
skip_newlines ();
push_nesting (NESTING_FUNCTION);
parse_source_element_list (false);
pop_nesting (NESTING_FUNCTION);
@@ -573,6 +544,8 @@ parse_function_declaration (void)
serializer_set_scope (STACK_TOP (scopes));
lexer_set_strict_mode (scopes_tree_strict_mode (STACK_TOP (scopes)));
jsp_label_restore_set (masked_label_set_p);
STACK_CHECK_USAGE (scopes);
STACK_CHECK_USAGE (nestings);
}
@@ -607,9 +580,16 @@ parse_function_expression (void)
token_after_newlines_must_be (TOK_OPEN_BRACE);
skip_newlines ();
jsp_label_t *masked_label_set_p = jsp_label_mask_set ();
push_nesting (NESTING_FUNCTION);
parse_source_element_list (false);
pop_nesting (NESTING_FUNCTION);
jsp_label_restore_set (masked_label_set_p);
next_token_must_be (TOK_CLOSE_BRACE);
dump_ret ();
@@ -1693,27 +1673,10 @@ parse_variable_declaration_list (bool *several_decls)
}
static void
parse_plain_for (void)
parse_plain_for (jsp_label_t *outermost_stmt_label_p) /**< outermost (first) label, corresponding to
* the statement (or NULL, if there are no named
* labels associated with the statement) */
{
/* Represent loop like
for (i = 0; i < 10; i++) {
body;
}
as
assign i, 0
jmp_down %cond
%body
body
post_incr i
%cond
less_than i, 10
is_true_jmp_up %body
*/
dump_jump_to_end_for_rewrite ();
// Skip till body
@@ -1731,19 +1694,18 @@ parse_plain_for (void)
skip_newlines ();
}
start_collecting_continues ();
start_collecting_breaks ();
dumper_set_next_interation_target ();
// Parse body
skip_newlines ();
push_nesting (NESTING_ITERATIONAL);
parse_statement ();
parse_statement (NULL);
pop_nesting (NESTING_ITERATIONAL);
const locus end_loc = tok.loc;
dumper_set_continue_target ();
jsp_label_setup_continue_target (outermost_stmt_label_p,
serializer_get_current_opcode_counter ());
lexer_seek (incr_loc);
skip_token ();
@@ -1766,10 +1728,6 @@ parse_plain_for (void)
dump_continue_iterations_check (cond);
}
dumper_set_break_target ();
rewrite_breaks ();
rewrite_continues ();
lexer_seek (end_loc);
skip_token ();
if (tok.type != TOK_CLOSE_BRACE)
@@ -1779,8 +1737,12 @@ parse_plain_for (void)
}
static void
parse_for_in (void)
parse_for_in (jsp_label_t *outermost_stmt_label_p) /**< outermost (first) label, corresponding to
* the statement (or NULL, if there are no named
* labels associated with the statement) */
{
(void) outermost_stmt_label_p;
EMIT_SORRY ("'for in' loops are not supported yet");
}
@@ -1805,7 +1767,9 @@ parse_for_in (void)
;*/
static void
parse_for_or_for_in_statement (void)
parse_for_or_for_in_statement (jsp_label_t *outermost_stmt_label_p) /**< outermost (first) label, corresponding to
* the statement (or NULL, if there are no named
* labels associated with the statement) */
{
assert_keyword (KW_FOR);
token_after_newlines_must_be (TOK_OPEN_PAREN);
@@ -1813,7 +1777,7 @@ parse_for_or_for_in_statement (void)
skip_newlines ();
if (token_is (TOK_SEMICOLON))
{
parse_plain_for ();
parse_plain_for (outermost_stmt_label_p);
return;
}
/* Both for_statement_initialiser_part and for_in_statement_initialiser_part
@@ -1826,7 +1790,7 @@ parse_for_or_for_in_statement (void)
if (several_decls)
{
token_after_newlines_must_be (TOK_SEMICOLON);
parse_plain_for ();
parse_plain_for (outermost_stmt_label_p);
return;
}
else
@@ -1834,12 +1798,12 @@ parse_for_or_for_in_statement (void)
skip_newlines ();
if (token_is (TOK_SEMICOLON))
{
parse_plain_for ();
parse_plain_for (outermost_stmt_label_p);
return;
}
else if (is_keyword (KW_IN))
{
parse_for_in ();
parse_for_in (outermost_stmt_label_p);
return;
}
else
@@ -1855,12 +1819,12 @@ parse_for_or_for_in_statement (void)
skip_newlines ();
if (token_is (TOK_SEMICOLON))
{
parse_plain_for ();
parse_plain_for (outermost_stmt_label_p);
return;
}
else if (is_keyword (KW_IN))
{
parse_for_in ();
parse_for_in (outermost_stmt_label_p);
return;
}
else
@@ -1887,7 +1851,7 @@ parse_statement_list (void)
{
while (true)
{
parse_statement ();
parse_statement (NULL);
skip_newlines ();
while (token_is (TOK_SEMICOLON))
@@ -1919,7 +1883,7 @@ parse_if_statement (void)
dump_conditional_check_for_rewrite (cond);
skip_newlines ();
parse_statement ();
parse_statement (NULL);
skip_newlines ();
if (is_keyword (KW_ELSE))
@@ -1928,7 +1892,7 @@ parse_if_statement (void)
rewrite_conditional_check ();
skip_newlines ();
parse_statement ();
parse_statement (NULL);
rewrite_jump_to_end ();
}
@@ -1943,37 +1907,34 @@ parse_if_statement (void)
: 'do' LT!* statement LT!* 'while' LT!* '(' expression ')' (LT | ';')!
; */
static void
parse_do_while_statement (void)
parse_do_while_statement (jsp_label_t *outermost_stmt_label_p) /**< outermost (first) label, corresponding to
* the statement (or NULL, if there are no named
* labels associated with the statement) */
{
assert_keyword (KW_DO);
start_collecting_continues ();
start_collecting_breaks ();
dumper_set_next_interation_target ();
skip_newlines ();
push_nesting (NESTING_ITERATIONAL);
parse_statement ();
parse_statement (NULL);
pop_nesting (NESTING_ITERATIONAL);
dumper_set_continue_target ();
jsp_label_setup_continue_target (outermost_stmt_label_p,
serializer_get_current_opcode_counter ());
token_after_newlines_must_be_keyword (KW_WHILE);
const operand cond = parse_expression_inside_parens ();
dump_continue_iterations_check (cond);
dumper_set_break_target ();
rewrite_breaks ();
rewrite_continues ();
}
/* while_statement
: 'while' LT!* '(' LT!* expression LT!* ')' LT!* statement
; */
static void
parse_while_statement (void)
parse_while_statement (jsp_label_t *outermost_stmt_label_p) /**< outermost (first) label, corresponding to
* the statement (or NULL, if there are no named
* labels associated with the statement) */
{
assert_keyword (KW_WHILE);
@@ -1985,15 +1946,13 @@ parse_while_statement (void)
dumper_set_next_interation_target ();
start_collecting_continues ();
start_collecting_breaks ();
skip_newlines ();
push_nesting (NESTING_ITERATIONAL);
parse_statement ();
parse_statement (NULL);
pop_nesting (NESTING_ITERATIONAL);
dumper_set_continue_target ();
jsp_label_setup_continue_target (outermost_stmt_label_p,
serializer_get_current_opcode_counter ());
rewrite_jump_to_end ();
@@ -2002,11 +1961,6 @@ parse_while_statement (void)
const operand cond = parse_expression_inside_parens ();
dump_continue_iterations_check (cond);
dumper_set_break_target ();
rewrite_breaks ();
rewrite_continues ();
lexer_seek (end_loc);
skip_token ();
}
@@ -2024,15 +1978,17 @@ parse_with_statement (void)
}
const operand expr = parse_expression_inside_parens ();
jsp_label_raise_nested_jumpable_border ();
push_nesting (NESTING_WITH);
opcode_counter_t with_begin_oc = dump_with_for_rewrite (expr);
skip_newlines ();
parse_statement ();
parse_statement (NULL);
rewrite_with (with_begin_oc);
dump_with_end ();
pop_nesting (NESTING_WITH);
jsp_label_remove_nested_jumpable_border ();
}
static void
@@ -2106,7 +2062,11 @@ parse_switch_statement (void)
lexer_seek (start_loc);
next_token_must_be (TOK_OPEN_BRACE);
start_collecting_breaks ();
jsp_label_t label;
jsp_label_push (&label,
JSP_LABEL_TYPE_UNNAMED_BREAKS,
TOKEN_EMPTY_INITIALIZER);
push_nesting (NESTING_SWITCH);
// Second, parse case clauses' bodies and rewrite jumps
skip_newlines ();
@@ -2144,8 +2104,9 @@ parse_switch_statement (void)
skip_token ();
pop_nesting (NESTING_SWITCH);
dumper_set_break_target ();
rewrite_breaks ();
jsp_label_rewrite_jumps_and_pop (&label,
serializer_get_current_opcode_counter ());
finish_dumping_case_clauses ();
}
@@ -2199,6 +2160,7 @@ parse_try_statement (void)
{
assert_keyword (KW_TRY);
jsp_label_raise_nested_jumpable_border ();
push_nesting (NESTING_TRY);
dump_try_for_rewrite ();
@@ -2237,6 +2199,7 @@ parse_try_statement (void)
dump_end_try_catch_finally ();
pop_nesting (NESTING_TRY);
jsp_label_remove_nested_jumpable_border ();
}
static void
@@ -2260,6 +2223,45 @@ insert_semicolon (void)
}
}
/**
* iteration_statement
* : do_while_statement
* | while_statement
* | for_statement
* | for_in_statement
* ;
*/
static void
parse_iterational_statement (jsp_label_t *outermost_named_stmt_label_p) /**< outermost (first) named label,
* corresponding to the statement
* (or NULL, if there are no named
* labels associated with the statement) */
{
jsp_label_t label;
jsp_label_push (&label,
(jsp_label_type_flag_t) (JSP_LABEL_TYPE_UNNAMED_BREAKS | JSP_LABEL_TYPE_UNNAMED_CONTINUES),
TOKEN_EMPTY_INITIALIZER);
jsp_label_t *outermost_stmt_label_p = (outermost_named_stmt_label_p != NULL ? outermost_named_stmt_label_p : &label);
if (is_keyword (KW_DO))
{
parse_do_while_statement (outermost_stmt_label_p);
}
else if (is_keyword (KW_WHILE))
{
parse_while_statement (outermost_stmt_label_p);
}
else
{
JERRY_ASSERT (is_keyword (KW_FOR));
parse_for_or_for_in_statement (outermost_stmt_label_p);
}
jsp_label_rewrite_jumps_and_pop (&label,
serializer_get_current_opcode_counter ());
} /* parse_iterational_statement */
/* statement
: statement_block
| variable_statement
@@ -2324,7 +2326,9 @@ insert_semicolon (void)
: 'try' LT!* '{' LT!* statement_list LT!* '}' LT!* (finally_clause | catch_clause (LT!* finally_clause)?)
;*/
static void
parse_statement (void)
parse_statement (jsp_label_t *outermost_stmt_label_p) /**< outermost (first) label, corresponding to the statement
* (or NULL, if there are no named labels associated
* with the statement) */
{
dumper_new_statement ();
@@ -2367,19 +2371,11 @@ parse_statement (void)
parse_if_statement ();
return;
}
if (is_keyword (KW_DO))
if (is_keyword (KW_DO)
|| is_keyword (KW_WHILE)
|| is_keyword (KW_FOR))
{
parse_do_while_statement ();
return;
}
if (is_keyword (KW_WHILE))
{
parse_while_statement ();
return;
}
if (is_keyword (KW_FOR))
{
parse_for_or_for_in_statement ();
parse_iterational_statement (outermost_stmt_label_p);
return;
}
if (is_keyword (KW_CONTINUE)
@@ -2392,39 +2388,48 @@ parse_statement (void)
EMIT_ERROR ("Shall be inside a nesting");
}
nesting_t topmost_nesting = STACK_ELEMENT (nestings, STACK_SIZE (nestings) - 1);
skip_token ();
if (is_break)
jsp_label_t *label_p;
bool is_simply_jumpable = true;
if (token_is (TOK_NAME))
{
must_be_inside_but_not_in (NESTING_FUNCTION,
4,
NESTING_ITERATIONAL,
NESTING_SWITCH,
NESTING_TRY,
NESTING_WITH);
/* break or continue on a label */
label_p = jsp_label_find (JSP_LABEL_TYPE_NAMED, tok, &is_simply_jumpable);
if (label_p == NULL)
{
EMIT_ERROR ("Label not found");
}
}
else if (is_break)
{
label_p = jsp_label_find (JSP_LABEL_TYPE_UNNAMED_BREAKS,
TOKEN_EMPTY_INITIALIZER,
&is_simply_jumpable);
if (label_p == NULL)
{
EMIT_ERROR ("No corresponding statement for the break");
}
}
else
{
must_be_inside_but_not_in (NESTING_FUNCTION,
3,
NESTING_ITERATIONAL,
NESTING_TRY,
NESTING_WITH);
JERRY_ASSERT (!is_break);
label_p = jsp_label_find (JSP_LABEL_TYPE_UNNAMED_CONTINUES,
TOKEN_EMPTY_INITIALIZER,
&is_simply_jumpable);
if (label_p == NULL)
{
EMIT_ERROR ("No corresponding statement for the continue");
}
}
if (topmost_nesting == NESTING_ITERATIONAL
|| (topmost_nesting == NESTING_SWITCH && is_break))
{
dump_break_continue_for_rewrite (is_break, true);
}
else
{
JERRY_ASSERT (topmost_nesting == NESTING_TRY
|| topmost_nesting == NESTING_WITH
|| (topmost_nesting == NESTING_SWITCH && !is_break));
JERRY_ASSERT (label_p != NULL);
dump_break_continue_for_rewrite (is_break, false);
}
jsp_label_add_jump (label_p, is_simply_jumpable, is_break);
return;
}
@@ -2473,8 +2478,21 @@ parse_statement (void)
skip_newlines ();
if (token_is (TOK_COLON))
{
// STMT_LABELLED;
EMIT_SORRY ("Labelled statements are not supported yet");
skip_newlines ();
jsp_label_t *label_p = jsp_label_find (JSP_LABEL_TYPE_NAMED, temp, NULL);
if (label_p != NULL)
{
EMIT_ERROR ("Label is duplicated");
}
jsp_label_t label;
jsp_label_push (&label, JSP_LABEL_TYPE_NAMED, temp);
parse_statement (outermost_stmt_label_p != NULL ? outermost_stmt_label_p : &label);
jsp_label_rewrite_jumps_and_pop (&label,
serializer_get_current_opcode_counter ());
}
else
{
@@ -2514,7 +2532,7 @@ parse_source_element (void)
}
else
{
parse_statement ();
parse_statement (NULL);
}
}
@@ -2866,11 +2884,15 @@ parser_init (const char *source, size_t source_size, bool show_opcodes)
STACK_INIT (nestings);
STACK_INIT (scopes);
jsp_label_init ();
}
void
parser_free (void)
{
jsp_label_finalize ();
STACK_FREE (nestings);
STACK_FREE (scopes);
@@ -0,0 +1,26 @@
// Copyright 2015 Samsung Electronics Co., Ltd.
//
// 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.
a: for (var i = 0; i < 10; i++)
{
function f ()
{
for (var j = 0; n < 10; j++)
{
break a;
}
}
f ();
}
@@ -0,0 +1,18 @@
// Copyright 2015 Samsung Electronics Co., Ltd.
//
// 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.
a: a: for (var i = 0; i < 10; i++)
{
break a;
}
@@ -0,0 +1,18 @@
// Copyright 2015 Samsung Electronics Co., Ltd.
//
// 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.
for (var i = 0; i < 10; i++)
{
break a;
}
+320
View File
@@ -0,0 +1,320 @@
// Copyright 2015 Samsung Electronics Co., Ltd.
//
// 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.
/* 1 */
a: a = 1;
str = '';
a: for (j = 0; j < 10; j++)
{
str += 'A';
b: for (i = 0; i < 10; i++)
{
str += 'B';
break a;
str += 'C';
}
str += 'D';
}
assert (str === 'AB');
/* 2 */
str = '';
a: for (j = 0; j < 5; j++)
{
str += 'A';
b: for (i = 0; i < 5; i++)
{
str += 'B';
switch (1)
{
case 1:
continue b;
default:
break b;
}
str += 'C';
}
str += 'D';
}
assert (str === 'ABBBBBDABBBBBDABBBBBDABBBBBDABBBBBD');
/* 3 */
str = '';
a: for (j = 0; j < 5; j++)
{
str += 'A';
b: for (i = 0; i < 5; i++)
{
str += 'B';
switch (1)
{
case 1:
continue a;
}
str += 'C';
}
str += 'D';
}
assert (str === 'ABABABABAB');
/* 4 */
str = '';
a: for (j = 0; j < 5; j++)
{
str += 'A';
b: for (i = 0; i < 5; i++)
{
str += 'B';
switch (1)
{
case 1:
break b;
}
str += 'C';
}
str += 'D';
}
assert (str === 'ABDABDABDABDABD');
/* 5 */
str = '';
a: for (j = 0; j < 5; j++)
{
str += 'A';
b: for (i = 0; i < 5; i++)
{
str += 'B';
switch (1)
{
case 1:
break a;
}
str += 'C';
}
str += 'D';
}
assert (str === 'AB');
/* 6 */
str = '';
a: for (j = 0; j < 5; j++)
{
str += 'A';
b: for (i = 0; i < 5; i++)
{
str += 'B';
with ({})
{
break b;
}
str += 'C';
}
str += 'D';
}
assert (str === 'ABDABDABDABDABD');
/* 7 */
str = '';
a: for (j = 0; j < 5; j++)
{
c:
{
str += 'A';
b: for (i = 0; i < 5; i++)
{
str += 'B';
with ({})
{
break c;
}
str += 'C';
}
str += 'D';
}
}
assert (str === 'ABABABABAB');
/* 8 */
a: {
function f ()
{
str = '';
a: for (i = 0; i < 5; i++)
{
str += 'A';
for (j = 0; j < 5; j++)
{
str += 'B';
continue a;
str += 'C';
}
str += 'D';
}
assert (str === 'ABABABABAB');
}
}
f ();
/* 9 */
str = '';
for (i = 0; i < 5; i++)
{
str += 'A';
switch (i)
{
case 0:
{
str += '0';
break;
}
case 1:
{
str += '1';
break;
}
case 2:
{
str += '2';
break;
}
case 3:
{
str += '3';
break;
}
case 4:
{
str += '4';
break;
}
}
str += 'B';
}
assert (str === 'A0BA1BA2BA3BA4B');
/* 10 */
str = '';
for (i = 0; i < 2; i++)
{
str += '[A]';
a:
for (j = 0; j < 5; j++)
{
str += '[B]';
switch (j)
{
case 0:
{
str += '[0]';
break;
}
case 1:
{
str += '[1]';
if (i % 2 == 0)
{
str += '[1.1]';
break a;
}
else
{
str += '[1.2]';
}
}
case 2:
{
str += '[2]';
continue a;
}
case 3:
{
str += '[3]';
break;
}
case 4:
{
str += '[4]';
continue a;
}
}
str += '[C]';
}
str += '[D]';
}
assert (str === '[A][B][0][C][B][1][1.1][D]' +
'[A][B][0][C][B][1][1.2][2][B][2][B][3][C][B][4][D]');