2014-09-23 20:05:10 +04:00

2887 lines
65 KiB
C

/* Copyright 2014 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 "optimizer-passes.h"
#include "jerry-libc.h"
#include "lexer.h"
#include "parser.h"
#include "opcodes.h"
#include "serializer.h"
#include "interpreter.h"
#include "stack.h"
#define INVALID_VALUE 255
#define INTRINSICS_COUNT 1
typedef enum
{
REWRITABLE_CONTINUE = 0,
REWRITABLE_BREAK,
REWRITABLE_OPCODES_COUNT
}
rewritable_opcode_type;
typedef struct
{
uint8_t args_count;
union
{
void (*fun1) (idx_t);
}
funs;
}
intrinsic_dumper;
#define NESTING_ITERATIONAL 1
#define NESTING_SWITCH 2
#define NESTING_FUNCTION 3
#define GLOBAL(NAME, VAR) STACK_ELEMENT (NAME, VAR)
enum
{
U8_global_size
};
STATIC_STACK (U8, uint8_t, uint8_t)
enum
{
IDX_global_size
};
STATIC_STACK (IDX, uint8_t, uint8_t)
enum
{
nestings_global_size
};
STATIC_STACK (nestings, uint8_t, uint8_t)
enum
{
temp_name,
min_temp_name,
max_temp_name,
temp_names_global_size
};
STATIC_STACK (temp_names, uint8_t, uint8_t)
#define MAX_TEMP_NAME() \
GLOBAL(temp_names, max_temp_name)
#define MIN_TEMP_NAME() \
GLOBAL(temp_names, min_temp_name)
#define TEMP_NAME() \
GLOBAL(temp_names, temp_name)
enum
{
tok = 0,
toks_global_size
};
STATIC_STACK (toks, uint8_t, token)
#define TOK() \
GLOBAL(toks, tok)
enum
{
opcode = 0,
ops_global_size
};
STATIC_STACK (ops, uint8_t, opcode_t)
#define OPCODE() \
GLOBAL(ops, opcode)
enum
{
opcode_counter = 0,
U16_global_size
};
STATIC_STACK (U16, uint8_t, uint16_t)
#define OPCODE_COUNTER() \
GLOBAL(U16, opcode_counter)
enum
{
rewritable_continue_global_size
};
STATIC_STACK (rewritable_continue, uint8_t, uint16_t)
enum
{
rewritable_break_global_size
};
STATIC_STACK (rewritable_break, uint8_t, uint16_t)
#ifndef JERRY_NDEBUG
#define STACK_CHECK_USAGE_LHS() \
JERRY_ASSERT (IDX.current == IDX_current + 1);
#else
#define STACK_CHECK_USAGE_LHS() ;
#endif
JERRY_STATIC_ASSERT (sizeof (idx_t) == sizeof (uint8_t));
JERRY_STATIC_ASSERT (sizeof (opcode_counter_t) == sizeof (uint16_t));
typedef enum
{
AL_FUNC_DECL,
AL_FUNC_EXPR,
AL_ARRAY_DECL,
AL_OBJ_DECL,
AL_CONSTRUCT_EXPR,
AL_CALL_EXPR
}
argument_list_type;
#define DEFINE_BACKET_TYPE(NAME, KEY_TYPE, VALUE_TYPE) \
typedef struct \
{ \
KEY_TYPE key; \
VALUE_TYPE value; \
} \
__packed \
NAME##_backet;
#define DEFINE_HASH_TYPE(NAME, KEY_TYPE, VALUE_TYPE) \
typedef struct \
{ \
uint8_t size; \
NAME##_backet_stack *backets; \
} \
__packed \
NAME##_hash_table;
#define HASH_INIT(NAME, SIZE) \
do { \
NAME.size = SIZE; \
size_t size = mem_heap_recommend_allocation_size (SIZE * sizeof (NAME##_backet_stack)); \
NAME.backets = (NAME##_backet_stack *) mem_heap_alloc_block (size, MEM_HEAP_ALLOC_SHORT_TERM); \
__memset (NAME.backets, 0, size); \
} while (0);
#define HASH_FREE(NAME) \
do { \
for (uint8_t i = 0; i < NAME.size; i++) { \
if (NAME.backets[i].length != 0) { \
mem_heap_free_block ((uint8_t *) NAME.backets[i].data); \
} \
} \
mem_heap_free_block ((uint8_t *) NAME.backets); \
} while (0)
#define DEFINE_HASH_INSERT(NAME, KEY_TYPE, VALUE_TYPE) \
static void hash_insert_##NAME (KEY_TYPE key, VALUE_TYPE value) __unused; \
static void hash_insert_##NAME (KEY_TYPE key, VALUE_TYPE value) { \
NAME##_backet backet = (NAME##_backet) { .key = key, .value = value }; \
uint8_t hash = KEY_TYPE##_hash (key); \
JERRY_ASSERT (hash < NAME.size); \
JERRY_ASSERT (NAME.backets != NULL); \
if (NAME.backets[hash].length == 0) \
{ \
size_t stack_size = mem_heap_recommend_allocation_size (0); \
NAME.backets[hash].data = (NAME##_backet *) mem_heap_alloc_block \
(stack_size, MEM_HEAP_ALLOC_SHORT_TERM); \
NAME.backets[hash].current = 0; \
NAME.backets[hash].length = (__typeof__ (NAME.backets[hash].length)) \
(stack_size / sizeof (NAME##_backet)); \
} \
else if (NAME.backets[hash].current == NAME.backets[hash].length) \
{ \
size_t old_size = NAME.backets[hash].length * sizeof (NAME##_backet); \
size_t temp1_size = mem_heap_recommend_allocation_size ( \
(size_t) (sizeof (NAME##_backet))); \
size_t new_size = mem_heap_recommend_allocation_size ( \
(size_t) (temp1_size + old_size)); \
NAME##_backet *temp1 = (NAME##_backet *) mem_heap_alloc_block \
(temp1_size, MEM_HEAP_ALLOC_SHORT_TERM); \
NAME##_backet *temp2 = (NAME##_backet *) mem_heap_alloc_block \
(old_size, MEM_HEAP_ALLOC_SHORT_TERM); \
if (temp2 == NULL) \
{ \
mem_heap_print (true, false, true); \
JERRY_UNREACHABLE (); \
} \
__memcpy (temp2, NAME.backets[hash].data, old_size); \
mem_heap_free_block ((uint8_t *) NAME.backets[hash].data); \
mem_heap_free_block ((uint8_t *) temp1); \
NAME.backets[hash].data = (NAME##_backet *) mem_heap_alloc_block \
(new_size, MEM_HEAP_ALLOC_SHORT_TERM); \
__memcpy (NAME.backets[hash].data, temp2, old_size); \
mem_heap_free_block ((uint8_t *) temp2); \
NAME.backets[hash].length = (__typeof__ (NAME.backets[hash].length)) \
(new_size / sizeof (NAME##_backet)); \
} \
NAME.backets[hash].data[NAME.backets[hash].current++] = backet; \
}
#define HASH_INSERT(NAME, KEY, VALUE) \
do { \
hash_insert_##NAME (KEY, VALUE); \
} while (0)
#define DEFINE_HASH_LOOKUP(NAME, KEY_TYPE, VALUE_TYPE) \
static VALUE_TYPE *lookup_##NAME (KEY_TYPE key) __unused; \
static VALUE_TYPE *lookup_##NAME (KEY_TYPE key) { \
uint8_t hash = KEY_TYPE##_hash (key); \
JERRY_ASSERT (hash < NAME.size); \
if (NAME.backets[hash].length == 0) { \
return NULL; \
} \
for (size_t i = 0; i < NAME.backets[hash].current; i++) { \
if (KEY_TYPE##_equal (NAME.backets[hash].data[i].key, key)) { \
return &NAME.backets[hash].data[i].value; \
} \
} \
return NULL; \
}
#define HASH_LOOKUP(NAME, KEY) \
lookup_##NAME (KEY)
#define HASH_TABLE(NAME, KEY_TYPE, VALUE_TYPE) \
DEFINE_BACKET_TYPE (NAME, KEY_TYPE, VALUE_TYPE) \
DEFINE_STACK_TYPE (NAME##_backet, uint8_t, NAME##_backet) \
DEFINE_HASH_TYPE (NAME, KEY_TYPE, VALUE_TYPE) \
NAME##_hash_table NAME; \
DEFINE_HASH_INSERT (NAME, KEY_TYPE, VALUE_TYPE) \
DEFINE_HASH_LOOKUP (NAME, KEY_TYPE, VALUE_TYPE)
#define STATIC_HASH_TABLE(NAME, KEY_TYPE, VALUE_TYPE) \
DEFINE_BACKET_TYPE (NAME, KEY_TYPE, VALUE_TYPE) \
DEFINE_STACK_TYPE (NAME##_backet, uint8_t, NAME##_backet) \
DEFINE_HASH_TYPE (NAME, KEY_TYPE, VALUE_TYPE) \
static NAME##_hash_table NAME; \
DEFINE_HASH_INSERT (NAME, KEY_TYPE, VALUE_TYPE) \
DEFINE_HASH_LOOKUP (NAME, KEY_TYPE, VALUE_TYPE)
static uint8_t lp_string_hash (lp_string);
STATIC_HASH_TABLE (intrinsics, lp_string, intrinsic_dumper)
static void parse_expression (void);
static void parse_statement (void);
static void parse_assignment_expression (void);
static void parse_source_element_list (void);
static void parse_argument_list (argument_list_type, idx_t);
static uint8_t
lp_string_hash (lp_string str)
{
return str.length % INTRINSICS_COUNT;
}
static idx_t
next_temp_name (void)
{
TEMP_NAME ()++;
if (MAX_TEMP_NAME () < TEMP_NAME ())
{
MAX_TEMP_NAME () = TEMP_NAME ();
}
return TEMP_NAME ();
}
static void
start_new_scope (void)
{
STACK_PUSH (temp_names, MAX_TEMP_NAME());
MAX_TEMP_NAME() = MIN_TEMP_NAME();
}
static void
finish_scope (void)
{
STACK_POP (temp_names, TEMP_NAME());
MAX_TEMP_NAME() = TEMP_NAME();
}
static void
reset_temp_name (void)
{
TEMP_NAME() = MIN_TEMP_NAME();
}
static void
push_nesting (uint8_t nesting_type)
{
STACK_PUSH (nestings, nesting_type);
}
static void
pop_nesting (uint8_t nesting_type)
{
JERRY_ASSERT (STACK_HEAD (nestings, 1) == nesting_type);
STACK_DROP (nestings, 1);
}
static void
must_be_inside_but_not_in (uint8_t *inside, uint8_t insides_count, uint8_t not_in)
{
STACK_DECLARE_USAGE (U8) // i, j
STACK_PUSH (U8, 0);
STACK_PUSH (U8, 0);
#define I() STACK_HEAD (U8, 2)
#define J() STACK_HEAD (U8, 1)
if (STACK_SIZE(nestings) == 0)
{
parser_fatal (ERR_PARSER);
}
for (I() = (uint8_t) (STACK_SIZE(nestings)); I() != 0; I()--)
{
if (nestings.data[I() - 1] == not_in)
{
parser_fatal (ERR_PARSER);
}
for (J() = 0; J() < insides_count; J()++)
{
if (nestings.data[I() - 1] == inside[J()])
{
goto cleanup;
}
}
}
parser_fatal (ERR_PARSER);
cleanup:
STACK_DROP (U8, 2);
#undef I
#undef J
STACK_CHECK_USAGE (U8);
}
static bool
token_is (token_type tt)
{
return TOK ().type == tt;
}
static uint8_t
token_data (void)
{
return TOK ().uid;
}
static void
skip_token (void)
{
TOK() = lexer_next_token ();
}
static void
assert_keyword (keyword kw)
{
if (!token_is (TOK_KEYWORD) || token_data () != kw)
{
#ifdef __TARGET_HOST_x64
__printf ("assert_keyword: %d\n", kw);
#endif
JERRY_UNREACHABLE ();
}
}
static bool
is_keyword (keyword kw)
{
return token_is (TOK_KEYWORD) && token_data () == kw;
}
static void
current_token_must_be (token_type tt)
{
if (!token_is (tt))
{
#ifdef __TARGET_HOST_x64
__printf ("current_token_must_be: %d\n", tt);
#endif
parser_fatal (ERR_PARSER);
}
}
static void
skip_newlines (void)
{
do
{
skip_token ();
}
while (token_is (TOK_NEWLINE));
}
static void
next_token_must_be (token_type tt)
{
skip_token ();
if (!token_is (tt))
{
#ifdef __TARGET_HOST_x64
__printf ("next_token_must_be: %d\n", tt);
#endif
parser_fatal (ERR_PARSER);
}
}
static void
token_after_newlines_must_be (token_type tt)
{
skip_newlines ();
if (!token_is (tt))
{
parser_fatal (ERR_PARSER);
}
}
static inline void
token_after_newlines_must_be_keyword (keyword kw)
{
skip_newlines ();
if (!is_keyword (kw))
{
parser_fatal (ERR_PARSER);
}
}
#define NEXT(TYPE) \
do { skip_newlines (); parse_##TYPE (); } while (0)
#define DUMP_VOID_OPCODE(GETOP) \
do { \
OPCODE()=getop_##GETOP (); \
serializer_dump_opcode (OPCODE()); \
OPCODE_COUNTER()++; \
} while (0)
#define DUMP_OPCODE_1(GETOP, OP1) \
do { \
JERRY_ASSERT (0+OP1 <= 255); \
OPCODE()=getop_##GETOP ((idx_t) (OP1)); \
serializer_dump_opcode (OPCODE()); \
OPCODE_COUNTER()++; \
} while (0)
#define DUMP_OPCODE_2(GETOP, OP1, OP2) \
do { \
JERRY_ASSERT (0+OP1 <= 255); \
JERRY_ASSERT (0+OP2 <= 255); \
OPCODE()=getop_##GETOP ((idx_t) (OP1), (idx_t) (OP2)); \
serializer_dump_opcode (OPCODE()); \
OPCODE_COUNTER()++; \
} while (0)
#define DUMP_OPCODE_3(GETOP, OP1, OP2, OP3) \
do { \
JERRY_ASSERT (0+OP1 <= 255); \
JERRY_ASSERT (0+OP2 <= 255); \
JERRY_ASSERT (0+OP3 <= 255); \
OPCODE()=getop_##GETOP ((idx_t) (OP1), (idx_t) (OP2), (idx_t) (OP3)); \
serializer_dump_opcode (OPCODE()); \
OPCODE_COUNTER()++; \
} while (0)
#define REWRITE_OPCODE_1(OC, GETOP, OP1) \
do { \
JERRY_ASSERT (0+OP1 <= 255); \
OPCODE()=getop_##GETOP ((idx_t) (OP1)); \
serializer_rewrite_opcode (OC, OPCODE()); \
} while (0)
#define REWRITE_OPCODE_2(OC, GETOP, OP1, OP2) \
do { \
JERRY_ASSERT (0+OP1 <= 255); \
JERRY_ASSERT (0+OP2 <= 255); \
OPCODE()=getop_##GETOP ((idx_t) (OP1), (idx_t) (OP2)); \
serializer_rewrite_opcode (OC, OPCODE()); \
} while (0)
#define REWRITE_OPCODE_3(OC, GETOP, OP1, OP2, OP3) \
do { \
JERRY_ASSERT (0+OP1 <= 255); \
JERRY_ASSERT (0+OP2 <= 255); \
JERRY_ASSERT (0+OP3 <= 255); \
OPCODE()=getop_##GETOP ((idx_t) (OP1), (idx_t) (OP2), (idx_t) (OP3)); \
serializer_rewrite_opcode (OC, OPCODE()); \
} while (0)
#define REWRITE_COND_JMP(OC, GETOP, DIFF) \
do { \
JERRY_ASSERT (0+DIFF <= 256*256 - 1); \
STACK_DECLARE_USAGE (IDX); \
JERRY_STATIC_ASSERT (sizeof (idx_t) == 1); \
STACK_PUSH (IDX, (idx_t) ((DIFF) >> JERRY_BITSINBYTE)); \
STACK_PUSH (IDX, (idx_t) ((DIFF) & ((1 << JERRY_BITSINBYTE) - 1))); \
JERRY_ASSERT ((DIFF) == calc_opcode_counter_from_idx_idx (STACK_HEAD (IDX, 2), STACK_HEAD (IDX, 1))); \
OPCODE()=getop_##GETOP (STACK_HEAD (IDX, 3), STACK_HEAD (IDX, 2), STACK_HEAD (IDX, 1)); \
serializer_rewrite_opcode (OC, OPCODE()); \
STACK_DROP (IDX, 2); \
STACK_CHECK_USAGE (IDX); \
} while (0)
#define REWRITE_JMP(OC, GETOP, DIFF) \
do { \
JERRY_ASSERT (0+DIFF <= 256*256 - 1); \
STACK_DECLARE_USAGE (IDX) \
JERRY_STATIC_ASSERT (sizeof (idx_t) == 1); \
STACK_PUSH (IDX, (idx_t) ((DIFF) >> JERRY_BITSINBYTE)); \
STACK_PUSH (IDX, (idx_t) ((DIFF) & ((1 << JERRY_BITSINBYTE) - 1))); \
JERRY_ASSERT ((DIFF) == calc_opcode_counter_from_idx_idx (STACK_HEAD (IDX, 2), STACK_HEAD (IDX, 1))); \
OPCODE()=getop_##GETOP (STACK_HEAD (IDX, 2), STACK_HEAD (IDX, 1)); \
serializer_rewrite_opcode (OC, OPCODE()); \
STACK_DROP (IDX, 2); \
STACK_CHECK_USAGE (IDX); \
} while (0)
#define REWRITE_TRY(OC) \
do { \
STACK_DECLARE_USAGE (IDX) \
JERRY_STATIC_ASSERT (sizeof (idx_t) == 1); \
STACK_PUSH (IDX, (idx_t) ((OPCODE_COUNTER ()) >> JERRY_BITSINBYTE)); \
STACK_PUSH (IDX, (idx_t) ((OPCODE_COUNTER ()) & ((1 << JERRY_BITSINBYTE) - 1))); \
JERRY_ASSERT ((OPCODE_COUNTER ()) \
== calc_opcode_counter_from_idx_idx (STACK_HEAD (IDX, 2), STACK_HEAD (IDX, 1))); \
OPCODE()=getop_try (STACK_HEAD (IDX, 2), STACK_HEAD (IDX, 1)); \
serializer_rewrite_opcode ((OC), OPCODE()); \
STACK_DROP (IDX, 2); \
STACK_CHECK_USAGE (IDX); \
} while (0)
static void
integer_zero (void)
{
STACK_DECLARE_USAGE (IDX)
STACK_PUSH (IDX, next_temp_name ());
DUMP_OPCODE_3 (assignment, STACK_HEAD (IDX, 1), OPCODE_ARG_TYPE_SMALLINT, 0);
STACK_CHECK_USAGE_LHS ();
}
static void
boolean_true (void)
{
STACK_DECLARE_USAGE (IDX)
STACK_PUSH (IDX, next_temp_name ());
DUMP_OPCODE_3 (assignment, STACK_HEAD (IDX, 1), OPCODE_ARG_TYPE_SIMPLE, ECMA_SIMPLE_VALUE_TRUE);
STACK_CHECK_USAGE_LHS ();
}
static void
add_to_rewritable_opcodes (rewritable_opcode_type type, opcode_counter_t oc)
{
switch (type)
{
case REWRITABLE_BREAK: STACK_PUSH (rewritable_break, oc); break;
case REWRITABLE_CONTINUE: STACK_PUSH (rewritable_continue, oc); break;
default: JERRY_UNREACHABLE ();
}
}
static void
rewrite_rewritable_opcodes (rewritable_opcode_type type, opcode_counter_t oc)
{
STACK_DECLARE_USAGE (U8)
STACK_PUSH (U8, 0);
switch (type)
{
case REWRITABLE_BREAK:
{
for (STACK_HEAD (U8, 1) = 0; STACK_HEAD (U8, 1) < STACK_SIZE (rewritable_break); STACK_HEAD (U8, 1)++)
{
REWRITE_JMP (STACK_ELEMENT (rewritable_break, STACK_HEAD (U8, 1)), jmp_down,
oc - STACK_ELEMENT (rewritable_break, STACK_HEAD (U8, 1)));
}
STACK_CLEAN (rewritable_break);
break;
}
case REWRITABLE_CONTINUE:
{
for (STACK_HEAD (U8, 1) = 0; STACK_HEAD (U8, 1) < STACK_SIZE (rewritable_continue); STACK_HEAD (U8, 1)++)
{
REWRITE_JMP (STACK_ELEMENT (rewritable_continue, STACK_HEAD (U8, 1)), jmp_up,
STACK_ELEMENT (rewritable_continue, STACK_HEAD (U8, 1)) - oc);
}
STACK_CLEAN (rewritable_continue);
break;
}
default: JERRY_UNREACHABLE ();
}
STACK_DROP (U8, 1);
STACK_CHECK_USAGE (U8);
}
static void
dump_assert (idx_t arg)
{
DUMP_OPCODE_3 (is_true_jmp_down, arg, 0, 2);
DUMP_OPCODE_1 (exitval, 1);
}
static void
fill_intrinsics (void)
{
lp_string str = (lp_string)
{
.length = 6,
.str = (const ecma_char_t *) "assert"
};
intrinsic_dumper dumper = (intrinsic_dumper)
{
.args_count = 1,
.funs.fun1 = dump_assert
};
HASH_INSERT (intrinsics, str, dumper);
}
static bool
is_intrinsic (idx_t obj)
{
/* Every literal is represented by assignment to tmp.
so if result of parse_primary_expression less then strings count,
it is identifier, check for intrinsics. */
// U8 strs
bool result = false;
STACK_DECLARE_USAGE (U8)
STACK_PUSH (U8, lexer_get_strings_count ());
if (obj < STACK_HEAD (U8, 1))
{
if (HASH_LOOKUP (intrinsics, lexer_get_string_by_id (obj)) != NULL)
{
result = true;
goto cleanup;
}
}
cleanup:
STACK_DROP (U8, 1);
STACK_CHECK_USAGE (U8);
return result;
}
static void
dump_intrinsic (idx_t obj, idx_t arg)
{
if (obj < lexer_get_strings_count ())
{
intrinsic_dumper *dumper = HASH_LOOKUP (intrinsics, lexer_get_string_by_id (obj));
JERRY_ASSERT (dumper);
switch (dumper->args_count)
{
case 1: dumper->funs.fun1 (arg); return;
default: JERRY_UNREACHABLE ();
}
}
JERRY_UNREACHABLE ();
}
/* property_name
: Identifier
| StringLiteral
| NumericLiteral
; */
static void
parse_property_name (void)
{
STACK_DECLARE_USAGE (IDX)
switch (TOK ().type)
{
case TOK_NAME:
case TOK_STRING:
case TOK_NUMBER:
{
STACK_PUSH (IDX, token_data ());
break;
}
case TOK_SMALL_INT:
{
STACK_PUSH (IDX, next_temp_name ());
DUMP_OPCODE_3 (assignment, STACK_HEAD (IDX, 1), OPCODE_ARG_TYPE_SMALLINT, token_data ());
break;
}
default:
{
JERRY_UNREACHABLE ();
}
}
STACK_CHECK_USAGE_LHS ();
}
/* property_name_and_value
: property_name LT!* ':' LT!* assignment_expression
; */
static void
parse_property_name_and_value (void)
{
// IDX lhs, name, expr
STACK_DECLARE_USAGE (IDX)
STACK_PUSH (IDX, next_temp_name ());
parse_property_name (); // push name
token_after_newlines_must_be (TOK_COLON);
NEXT (assignment_expression); // push expr
DUMP_OPCODE_3 (meta, OPCODE_META_TYPE_VARG_PROP_DATA, STACK_HEAD(IDX, 1), STACK_HEAD(IDX, 2));
STACK_DROP (IDX, 2);
STACK_CHECK_USAGE_LHS ();
}
static void
rewrite_meta_opcode_counter (opcode_counter_t meta_oc, opcode_meta_type type)
{
// IDX oc_idx_1, oc_idx_2
STACK_DECLARE_USAGE (IDX)
JERRY_STATIC_ASSERT (sizeof (idx_t) == 1);
STACK_PUSH (IDX, (idx_t) (OPCODE_COUNTER () >> JERRY_BITSINBYTE));
STACK_PUSH (IDX, (idx_t) (OPCODE_COUNTER () & ((1 << JERRY_BITSINBYTE) - 1)));
JERRY_ASSERT (OPCODE_COUNTER () == calc_opcode_counter_from_idx_idx (STACK_HEAD (IDX, 2), STACK_HEAD (IDX, 1)));
REWRITE_OPCODE_3 (meta_oc, meta, type, STACK_HEAD (IDX, 2), STACK_HEAD (IDX, 1));
STACK_DROP (IDX, 2);
STACK_CHECK_USAGE (IDX);
}
/* property_assignment
: property_name_and_value
| get LT!* property_name LT!* '(' LT!* ')' LT!* '{' LT!* function_body LT!* '}'
| set LT!* property_name LT!* '(' identifier ')' LT!* '{' LT!* function_body LT!* '}'
; */
static void
parse_property_assignment (void)
{
STACK_DECLARE_USAGE (IDX)
STACK_DECLARE_USAGE (U16)
current_token_must_be (TOK_NAME);
if (lp_string_equal_s (lexer_get_string_by_id (token_data ()), "get"))
{
// name, lhs
NEXT (property_name); // push name
skip_newlines ();
parse_argument_list (AL_FUNC_EXPR, next_temp_name ()); // push lhs
STACK_PUSH (U16, opcode_counter);
DUMP_OPCODE_3 (meta, OPCODE_META_TYPE_FUNCTION_END, INVALID_VALUE, INVALID_VALUE);
token_after_newlines_must_be (TOK_OPEN_BRACE);
skip_newlines ();
parse_source_element_list ();
token_after_newlines_must_be (TOK_CLOSE_BRACE);
DUMP_VOID_OPCODE (ret);
rewrite_meta_opcode_counter (STACK_HEAD (U16, 1), OPCODE_META_TYPE_FUNCTION_END);
DUMP_OPCODE_3 (meta, OPCODE_META_TYPE_VARG_PROP_GETTER, STACK_HEAD (IDX, 2), STACK_HEAD (IDX, 1));
STACK_HEAD (IDX, 2) = STACK_HEAD (IDX, 1);
STACK_DROP (IDX, 1);
STACK_DROP (U16, 1);
}
else if (lp_string_equal_s (lexer_get_string_by_id (token_data ()), "set"))
{
// name, lhs
NEXT (property_name); // push name
skip_newlines ();
parse_argument_list (AL_FUNC_EXPR, next_temp_name ()); // push lhs
STACK_PUSH (U16, opcode_counter);
DUMP_OPCODE_3 (meta, OPCODE_META_TYPE_FUNCTION_END, INVALID_VALUE, INVALID_VALUE);
token_after_newlines_must_be (TOK_OPEN_BRACE);
skip_newlines ();
parse_source_element_list ();
token_after_newlines_must_be (TOK_CLOSE_BRACE);
DUMP_VOID_OPCODE (ret);
rewrite_meta_opcode_counter (STACK_HEAD (U16, 1), OPCODE_META_TYPE_FUNCTION_END);
DUMP_OPCODE_3 (meta, OPCODE_META_TYPE_VARG_PROP_SETTER, STACK_HEAD (IDX, 2), STACK_HEAD (IDX, 1));
STACK_HEAD (IDX, 2) = STACK_HEAD (IDX, 1);
STACK_DROP (IDX, 1);
STACK_DROP (U16, 1);
}
else
{
parse_property_name_and_value ();
}
STACK_CHECK_USAGE (U16);
STACK_CHECK_USAGE_LHS ();
}
/** Parse list of identifiers, assigment expressions or properties, splitted by comma.
For each ALT dumps appropriate bytecode. Uses OBJ during dump if neccesary.
Returns temp var if expression has lhs, or 0 otherwise. */
static void
parse_argument_list (argument_list_type alt, idx_t obj)
{
// U8 open_tt, close_tt, args_count
// IDX lhs, current_arg
// U16 oc
STACK_DECLARE_USAGE (U8)
STACK_DECLARE_USAGE (U16)
STACK_DECLARE_USAGE (IDX)
STACK_PUSH (U16, OPCODE_COUNTER ());
switch (alt)
{
case AL_FUNC_DECL:
{
STACK_PUSH (U8, TOK_OPEN_PAREN); // Openning token;
STACK_PUSH (U8, TOK_CLOSE_PAREN); // Ending token;
DUMP_OPCODE_2 (func_decl_n, obj, INVALID_VALUE);
break;
}
case AL_FUNC_EXPR:
{
STACK_PUSH (U8, TOK_OPEN_PAREN);
STACK_PUSH (U8, TOK_CLOSE_PAREN);
STACK_PUSH (IDX, next_temp_name ());
DUMP_OPCODE_3 (func_expr_n, STACK_HEAD (IDX, 1), obj, INVALID_VALUE);
break;
}
case AL_CONSTRUCT_EXPR:
{
STACK_PUSH (U8, TOK_OPEN_PAREN);
STACK_PUSH (U8, TOK_CLOSE_PAREN);
STACK_PUSH (IDX, next_temp_name ());
DUMP_OPCODE_3 (construct_n, STACK_HEAD (IDX, 1), obj, INVALID_VALUE);
break;
}
case AL_CALL_EXPR:
{
STACK_PUSH (U8, TOK_OPEN_PAREN);
STACK_PUSH (U8, TOK_CLOSE_PAREN);
if (is_intrinsic (obj))
{
break;
}
else
{
STACK_PUSH (IDX, next_temp_name ());
DUMP_OPCODE_3 (call_n, STACK_HEAD (IDX, 1), obj, INVALID_VALUE);
}
break;
}
case AL_ARRAY_DECL:
{
STACK_PUSH (U8, TOK_OPEN_SQUARE);
STACK_PUSH (U8, TOK_CLOSE_SQUARE);
STACK_PUSH (IDX, next_temp_name ());
DUMP_OPCODE_2 (array_decl, STACK_HEAD (IDX, 1), INVALID_VALUE);
break;
}
case AL_OBJ_DECL:
{
STACK_PUSH (U8, TOK_OPEN_BRACE);
STACK_PUSH (U8, TOK_CLOSE_BRACE);
STACK_PUSH (IDX, next_temp_name ());
DUMP_OPCODE_2 (obj_decl, STACK_HEAD (IDX, 1), INVALID_VALUE);
break;
}
default:
{
JERRY_UNREACHABLE ();
}
}
STACK_PUSH (U8, 0);
current_token_must_be (STACK_HEAD (U8, 3));
skip_newlines ();
while (!token_is (STACK_HEAD (U8, 2)))
{
switch (alt)
{
case AL_FUNC_DECL:
{
current_token_must_be (TOK_NAME);
STACK_PUSH (IDX, token_data ());
break;
}
case AL_FUNC_EXPR:
case AL_ARRAY_DECL:
case AL_CONSTRUCT_EXPR:
case AL_CALL_EXPR:
{
parse_assignment_expression ();
if (is_intrinsic (obj))
{
dump_intrinsic (obj, STACK_HEAD (IDX, 1));
goto next;
}
break;
}
case AL_OBJ_DECL:
{
parse_property_assignment ();
break;
}
default:
{
JERRY_UNREACHABLE ();
}
}
DUMP_OPCODE_3 (meta, OPCODE_META_TYPE_VARG, STACK_HEAD (IDX, 1), INVALID_VALUE);
STACK_HEAD(U8, 1)++;
STACK_DROP (IDX, 1);
next:
skip_newlines ();
if (!token_is (TOK_COMMA))
{
current_token_must_be (STACK_HEAD (U8, 2));
break;
}
skip_newlines ();
}
switch (alt)
{
case AL_FUNC_DECL:
{
REWRITE_OPCODE_2 (STACK_HEAD (U16, 1), func_decl_n, obj, STACK_HEAD (U8, 1));
break;
}
case AL_FUNC_EXPR:
{
REWRITE_OPCODE_3 (STACK_HEAD (U16, 1), func_expr_n, STACK_HEAD (IDX, 1), obj, STACK_HEAD (U8, 1));
break;
}
case AL_CONSTRUCT_EXPR:
{
REWRITE_OPCODE_3 (STACK_HEAD (U16, 1), construct_n, STACK_HEAD (IDX, 1), obj, STACK_HEAD (U8, 1));
break;
}
case AL_CALL_EXPR:
{
if (is_intrinsic (obj))
{
break;
}
else
{
REWRITE_OPCODE_3 (STACK_HEAD (U16, 1), call_n, STACK_HEAD (IDX, 1), obj, STACK_HEAD (U8, 1));
}
break;
}
case AL_ARRAY_DECL:
{
REWRITE_OPCODE_2 (STACK_HEAD (U16, 1), array_decl, STACK_HEAD (IDX, 1), STACK_HEAD (U8, 1));
break;
}
case AL_OBJ_DECL:
{
REWRITE_OPCODE_2 (STACK_HEAD (U16, 1), obj_decl, STACK_HEAD (IDX, 1), STACK_HEAD (U8, 1));
break;
}
default:
{
JERRY_UNREACHABLE ();
}
}
STACK_DROP (U8, 3);
STACK_DROP (U16, 1);
STACK_CHECK_USAGE (U8);
STACK_CHECK_USAGE (U16);
#ifndef JERRY_NDEBUG
if (alt == AL_FUNC_DECL)
{
STACK_CHECK_USAGE (IDX);
}
else
{
STACK_CHECK_USAGE_LHS ();
}
#endif
}
/* function_declaration
: 'function' LT!* Identifier LT!*
'(' (LT!* Identifier (LT!* ',' LT!* Identifier)*) ? LT!* ')' LT!* function_body
;
function_body
: '{' LT!* sourceElements LT!* '}' */
static void
parse_function_declaration (void)
{
// IDX name
// U16 meta_oc
STACK_DECLARE_USAGE (IDX)
STACK_DECLARE_USAGE (U16)
STACK_DECLARE_USAGE (nestings)
assert_keyword (KW_FUNCTION);
token_after_newlines_must_be (TOK_NAME);
STACK_PUSH (IDX, token_data ());
skip_newlines ();
parse_argument_list (AL_FUNC_DECL, STACK_HEAD (IDX, 1));
STACK_PUSH (U16, OPCODE_COUNTER ());
DUMP_OPCODE_3 (meta, OPCODE_META_TYPE_UNDEFINED, INVALID_VALUE, INVALID_VALUE);
token_after_newlines_must_be (TOK_OPEN_BRACE);
skip_newlines ();
push_nesting (NESTING_FUNCTION);
parse_source_element_list ();
pop_nesting (NESTING_FUNCTION);
next_token_must_be (TOK_CLOSE_BRACE);
DUMP_VOID_OPCODE (ret);
rewrite_meta_opcode_counter (STACK_HEAD (U16, 1), OPCODE_META_TYPE_FUNCTION_END);
STACK_DROP (U16, 1);
STACK_DROP (IDX, 1);
STACK_CHECK_USAGE (IDX);
STACK_CHECK_USAGE (U16);
STACK_CHECK_USAGE (nestings);
}
/* function_expression
: 'function' LT!* Identifier? LT!* '(' formal_parameter_list? LT!* ')' LT!* function_body
; */
static void
parse_function_expression (void)
{
// IDX lhs, name
// U16 meta_oc
STACK_DECLARE_USAGE (IDX)
STACK_DECLARE_USAGE (U16)
STACK_DECLARE_USAGE (nestings)
assert_keyword (KW_FUNCTION);
skip_newlines ();
if (token_is (TOK_NAME))
{
STACK_PUSH (IDX, token_data ());
}
else
{
lexer_save_token (TOK());
STACK_PUSH (IDX, next_temp_name ());
}
skip_newlines ();
parse_argument_list (AL_FUNC_EXPR, STACK_HEAD (IDX, 1)); // push lhs
STACK_PUSH (U16, OPCODE_COUNTER ());
DUMP_OPCODE_3 (meta, OPCODE_META_TYPE_UNDEFINED, INVALID_VALUE, INVALID_VALUE);
token_after_newlines_must_be (TOK_OPEN_BRACE);
skip_newlines ();
push_nesting (NESTING_FUNCTION);
parse_source_element_list ();
pop_nesting (NESTING_FUNCTION);
token_after_newlines_must_be (TOK_CLOSE_BRACE);
DUMP_VOID_OPCODE (ret);
rewrite_meta_opcode_counter (STACK_HEAD (U16, 1), OPCODE_META_TYPE_FUNCTION_END);
STACK_HEAD (IDX, 2) = STACK_HEAD (IDX, 1);
STACK_DROP (IDX, 1);
STACK_DROP (U16, 1);
STACK_CHECK_USAGE (U16);
STACK_CHECK_USAGE_LHS ();
STACK_CHECK_USAGE (nestings);
}
/* array_literal
: '[' LT!* assignment_expression? (LT!* ',' (LT!* assignment_expression)?)* LT!* ']' LT!*
; */
static void
parse_array_literal (void)
{
STACK_DECLARE_USAGE (IDX)
parse_argument_list (AL_ARRAY_DECL, 0);
STACK_CHECK_USAGE_LHS ();
}
/* object_literal
: '{' LT!* property_assignment (LT!* ',' LT!* property_assignment)* LT!* '}'
; */
static void
parse_object_literal (void)
{
STACK_DECLARE_USAGE (IDX)
parse_argument_list (AL_OBJ_DECL, 0);
STACK_CHECK_USAGE_LHS ();;
}
static void
parse_literal (void)
{
// IDX lhs;
STACK_DECLARE_USAGE (IDX)
switch (TOK ().type)
{
case TOK_NULL:
{
STACK_PUSH (IDX, next_temp_name ());
DUMP_OPCODE_3 (assignment, STACK_HEAD (IDX, 1), OPCODE_ARG_TYPE_SIMPLE, ECMA_SIMPLE_VALUE_NULL);
break;
}
case TOK_BOOL:
{
STACK_PUSH (IDX, next_temp_name ());
DUMP_OPCODE_3 (assignment, STACK_HEAD (IDX, 1), OPCODE_ARG_TYPE_SIMPLE,
token_data () ? ECMA_SIMPLE_VALUE_TRUE : ECMA_SIMPLE_VALUE_FALSE);
break;
}
case TOK_NUMBER:
{
STACK_PUSH (IDX, next_temp_name ());
DUMP_OPCODE_3 (assignment, STACK_HEAD (IDX, 1), OPCODE_ARG_TYPE_NUMBER, token_data ());
break;
}
case TOK_SMALL_INT:
{
STACK_PUSH (IDX, next_temp_name ());
DUMP_OPCODE_3 (assignment, STACK_HEAD (IDX, 1), OPCODE_ARG_TYPE_SMALLINT, token_data ());
break;
}
case TOK_STRING:
{
STACK_PUSH (IDX, next_temp_name ());
DUMP_OPCODE_3 (assignment, STACK_HEAD (IDX, 1), OPCODE_ARG_TYPE_STRING, token_data ());
break;
}
default:
{
JERRY_UNREACHABLE ();
}
}
STACK_CHECK_USAGE_LHS ();
}
/* primary_expression
: 'this'
| Identifier
| literal
| '[' LT!* array_literal LT!* ']'
| '{' LT!* object_literal LT!* '}'
| '(' LT!* expression LT!* ')'
; */
static void
parse_primary_expression (void)
{
// IDX lhs;
STACK_DECLARE_USAGE (IDX)
if (is_keyword (KW_THIS))
{
STACK_PUSH (IDX, next_temp_name ());
DUMP_OPCODE_1 (this, STACK_HEAD (IDX, 1));
goto cleanup;
}
switch (TOK ().type)
{
case TOK_NAME:
{
STACK_PUSH (IDX, token_data ());
break;
}
case TOK_NULL:
case TOK_BOOL:
case TOK_SMALL_INT:
case TOK_NUMBER:
case TOK_STRING:
{
parse_literal ();
break;
}
case TOK_OPEN_SQUARE:
{
parse_array_literal ();
break;
}
case TOK_OPEN_BRACE:
{
parse_object_literal ();
break;
}
case TOK_OPEN_PAREN:
{
skip_newlines ();
if (!token_is (TOK_CLOSE_PAREN))
{
parse_expression ();
token_after_newlines_must_be (TOK_CLOSE_PAREN);
break;
}
/* FALLTHRU */
}
default:
{
JERRY_UNREACHABLE ();
}
}
cleanup:
STACK_CHECK_USAGE_LHS ();
}
/* member_expression
: (primary_expression | function_expression | 'new' LT!* member_expression (LT!* '(' LT!* arguments? LT!* ')')
(LT!* member_expression_suffix)*
;
arguments
: assignment_expression (LT!* ',' LT!* assignment_expression)*)?
;
member_expression_suffix
: index_suffix
| property_reference_suffix
;
index_suffix
: '[' LT!* expression LT!* ']'
;
property_reference_suffix
: '.' LT!* Identifier
; */
static void
parse_member_expression (void)
{
// IDX obj, lhs, prop;
STACK_DECLARE_USAGE (IDX)
if (is_keyword (KW_FUNCTION))
{
parse_function_expression ();
}
else if (is_keyword (KW_NEW))
{
NEXT (member_expression); // push member
parse_argument_list (AL_CONSTRUCT_EXPR, STACK_HEAD (IDX, 1)); // push obj
STACK_HEAD (IDX, 2) = STACK_HEAD (IDX, 1);
STACK_DROP (IDX, 1);
}
else
{
parse_primary_expression ();
}
skip_newlines ();
while (token_is (TOK_OPEN_SQUARE) || token_is (TOK_DOT))
{
STACK_PUSH (IDX, next_temp_name ());
if (token_is (TOK_OPEN_SQUARE))
{
NEXT (expression); // push prop
next_token_must_be (TOK_CLOSE_SQUARE);
}
else if (token_is (TOK_DOT))
{
skip_newlines ();
if (!token_is (TOK_NAME))
{
parser_fatal (ERR_PARSER);
}
STACK_PUSH (IDX, token_data ());
}
else
{
JERRY_UNREACHABLE ();
}
DUMP_OPCODE_3 (prop_getter, STACK_HEAD (IDX, 2), STACK_HEAD (IDX, 3), STACK_HEAD (IDX, 1));
STACK_HEAD (IDX, 3) = STACK_HEAD (IDX, 2);
skip_newlines ();
STACK_DROP (IDX, 2);
}
lexer_save_token (TOK ());
STACK_CHECK_USAGE_LHS ();
}
/* call_expression
: member_expression LT!* arguments (LT!* call_expression_suffix)*
;
call_expression_suffix
: arguments
| index_suffix
| property_reference_suffix
;
arguments
: '(' LT!* assignment_expression LT!* (',' LT!* assignment_expression * LT!*)* ')'
; */
static void
parse_call_expression (void)
{
// IDX obj, lhs, prop;
STACK_DECLARE_USAGE (IDX)
parse_member_expression ();
skip_newlines ();
if (!token_is (TOK_OPEN_PAREN))
{
lexer_save_token (TOK ());
goto cleanup;
}
parse_argument_list (AL_CALL_EXPR, STACK_HEAD (IDX, 1)); // push lhs
STACK_HEAD (IDX, 2) = STACK_HEAD (IDX, 1);
skip_newlines ();
while (token_is (TOK_OPEN_PAREN) || token_is (TOK_OPEN_SQUARE)
|| token_is (TOK_DOT))
{
switch (TOK ().type)
{
case TOK_OPEN_PAREN:
{
STACK_DROP (IDX, 1);
parse_argument_list (AL_CALL_EXPR, STACK_HEAD (IDX, 1)); // push lhs
skip_newlines ();
break;
}
case TOK_OPEN_SQUARE:
{
NEXT (expression); // push prop
next_token_must_be (TOK_CLOSE_SQUARE);
DUMP_OPCODE_3 (prop_getter, STACK_HEAD (IDX, 2), STACK_HEAD (IDX, 3), STACK_HEAD (IDX, 1));
STACK_DROP (IDX, 1);
STACK_HEAD (IDX, 2) = STACK_HEAD (IDX, 1);
skip_newlines ();
break;
}
case TOK_DOT:
{
token_after_newlines_must_be (TOK_NAME);
STACK_PUSH (IDX, token_data ());
DUMP_OPCODE_3 (prop_getter, STACK_HEAD (IDX, 2), STACK_HEAD (IDX, 3), STACK_HEAD (IDX, 1));
STACK_DROP (IDX, 1);
STACK_HEAD (IDX, 2) = STACK_HEAD (IDX, 1);
skip_newlines ();
break;
}
default:
{
JERRY_UNREACHABLE ();
}
}
}
lexer_save_token (TOK ());
STACK_DROP (IDX, 1);
cleanup:
STACK_CHECK_USAGE_LHS ();
}
/* left_hand_side_expression
: call_expression
| new_expression
; */
static void
parse_left_hand_side_expression (void)
{
STACK_DECLARE_USAGE (IDX)
parse_call_expression ();
STACK_CHECK_USAGE_LHS ();
}
/* postfix_expression
: left_hand_side_expression ('++' | '--')?
; */
static void
parse_postfix_expression (void)
{
// IDX expr, lhs
STACK_DECLARE_USAGE (IDX)
parse_left_hand_side_expression (); // push expr
skip_token ();
if (token_is (TOK_DOUBLE_PLUS))
{
DUMP_OPCODE_2 (post_incr, next_temp_name (), STACK_HEAD (IDX, 1));
}
else if (token_is (TOK_DOUBLE_MINUS))
{
DUMP_OPCODE_2 (post_decr, next_temp_name (), STACK_HEAD (IDX, 1));
}
else
{
lexer_save_token (TOK ());
}
STACK_CHECK_USAGE_LHS ();
}
/* unary_expression
: postfix_expression
| ('delete' | 'void' | 'typeof' | '++' | '--' | '+' | '-' | '~' | '!') unary_expression
; */
static void
parse_unary_expression (void)
{
// IDX expr, lhs;
STACK_DECLARE_USAGE (IDX)
switch (TOK ().type)
{
case TOK_DOUBLE_PLUS:
{
NEXT (unary_expression);
DUMP_OPCODE_2 (pre_incr, next_temp_name (), STACK_HEAD (IDX, 1));
break;
}
case TOK_DOUBLE_MINUS:
{
NEXT (unary_expression);
DUMP_OPCODE_2 (pre_decr, next_temp_name (), STACK_HEAD (IDX, 1));
break;
}
case TOK_PLUS:
{
STACK_PUSH (IDX, next_temp_name ());
NEXT (unary_expression);
integer_zero ();
DUMP_OPCODE_3 (addition, STACK_HEAD (IDX, 3), STACK_HEAD (IDX, 1), STACK_HEAD (IDX, 2));
STACK_DROP (IDX, 2);
break;
}
case TOK_MINUS:
{
STACK_PUSH (IDX, next_temp_name ());
NEXT (unary_expression);
integer_zero ();
DUMP_OPCODE_3 (substraction, STACK_HEAD (IDX, 3), STACK_HEAD (IDX, 1), STACK_HEAD (IDX, 2));
STACK_DROP (IDX, 2);
break;
}
case TOK_COMPL:
{
STACK_PUSH (IDX, next_temp_name ());
NEXT (unary_expression);
DUMP_OPCODE_2 (b_not, STACK_HEAD (IDX, 2), STACK_HEAD (IDX, 1));
STACK_DROP (IDX, 1);
break;
}
case TOK_NOT:
{
STACK_PUSH (IDX, next_temp_name ());
NEXT (unary_expression);
DUMP_OPCODE_2 (logical_not, STACK_HEAD (IDX, 2), STACK_HEAD (IDX, 1));
STACK_DROP (IDX, 1);
break;
}
case TOK_KEYWORD:
{
if (is_keyword (KW_DELETE))
{
TODO (/* lhs = delete_var for delete, applied to expression, that is evaluating to Identifier;
lhs = delete_prop for 'delete expr[expr]';
lhs = true - otherwise; */);
// DUMP_OPCODE_2 (delete, lhs, expr);
JERRY_UNIMPLEMENTED ();
}
if (is_keyword (KW_VOID))
{
JERRY_UNIMPLEMENTED ();
}
if (is_keyword (KW_TYPEOF))
{
STACK_PUSH (IDX, next_temp_name ());
NEXT (unary_expression);
DUMP_OPCODE_2 (typeof, STACK_HEAD (IDX, 2), STACK_HEAD (IDX, 1));
STACK_DROP (IDX, 1);
break;
}
/* FALLTHRU. */
}
default:
{
parse_postfix_expression ();
}
}
STACK_CHECK_USAGE_LHS ();
}
#define DUMP_OF(GETOP, EXPR) \
do { \
STACK_PUSH (IDX, next_temp_name ()); \
NEXT (EXPR);\
DUMP_OPCODE_3 (GETOP, STACK_HEAD (IDX, 2), STACK_HEAD (IDX, 3), STACK_HEAD (IDX, 1)); \
STACK_HEAD (IDX, 3) = STACK_HEAD (IDX, 2); \
STACK_DROP (IDX, 2); \
} while (0)
/* multiplicative_expression
: unary_expression (LT!* ('*' | '/' | '%') LT!* unary_expression)*
; */
static void
parse_multiplicative_expression (void)
{
// IDX expr1, lhs, expr2;
STACK_DECLARE_USAGE (IDX)
parse_unary_expression ();
skip_newlines ();
while (true)
{
switch (TOK ().type)
{
case TOK_MULT: DUMP_OF (multiplication, unary_expression); break;
case TOK_DIV: DUMP_OF (division, unary_expression); break;
case TOK_MOD: DUMP_OF (remainder, unary_expression); break;
default: lexer_save_token (TOK ()); goto cleanup;
}
skip_newlines ();
}
cleanup:
STACK_CHECK_USAGE_LHS ();
}
/* additive_expression
: multiplicative_expression (LT!* ('+' | '-') LT!* multiplicative_expression)*
; */
static void
parse_additive_expression (void)
{
// IDX expr1, lhs, expr2;
STACK_DECLARE_USAGE (IDX)
parse_multiplicative_expression ();
skip_newlines ();
while (true)
{
switch (TOK ().type)
{
case TOK_PLUS: DUMP_OF (addition, multiplicative_expression); break;
case TOK_MINUS: DUMP_OF (substraction, multiplicative_expression); break;
default: lexer_save_token (TOK ()); goto cleanup;
}
skip_newlines ();
}
cleanup:
STACK_CHECK_USAGE_LHS ();
}
/* shift_expression
: additive_expression (LT!* ('<<' | '>>' | '>>>') LT!* additive_expression)*
; */
static void
parse_shift_expression (void)
{
// IDX expr1, lhs, expr2;
STACK_DECLARE_USAGE (IDX)
parse_additive_expression ();
skip_newlines ();
while (true)
{
switch (TOK ().type)
{
case TOK_LSHIFT: DUMP_OF (b_shift_left, additive_expression); break;
case TOK_RSHIFT: DUMP_OF (b_shift_right, additive_expression); break;
case TOK_RSHIFT_EX: DUMP_OF (b_shift_uright, additive_expression); break;
default: lexer_save_token (TOK ()); goto cleanup;
}
skip_newlines ();
}
cleanup:
STACK_CHECK_USAGE_LHS ();
}
/* relational_expression
: shift_expression (LT!* ('<' | '>' | '<=' | '>=' | 'instanceof' | 'in') LT!* shift_expression)*
; */
static void
parse_relational_expression (void)
{
// IDX expr1, lhs, expr2;
STACK_DECLARE_USAGE (IDX)
parse_shift_expression ();
skip_newlines ();
while (true)
{
switch (TOK ().type)
{
case TOK_LESS: DUMP_OF (less_than, shift_expression); break;
case TOK_GREATER: DUMP_OF (greater_than, shift_expression); break;
case TOK_LESS_EQ: DUMP_OF (less_or_equal_than, shift_expression); break;
case TOK_GREATER_EQ: DUMP_OF (greater_or_equal_than, shift_expression); break;
case TOK_KEYWORD:
{
if (is_keyword (KW_INSTANCEOF))
{
DUMP_OF (instanceof, shift_expression);
break;
}
else if (is_keyword (KW_IN))
{
DUMP_OF (in, shift_expression);
break;
}
/* FALLTHROUGH */
}
default:
{
lexer_save_token (TOK ());
goto cleanup;
}
}
skip_newlines ();
}
cleanup:
STACK_CHECK_USAGE_LHS ();
}
/* equality_expression
: relational_expression (LT!* ('==' | '!=' | '===' | '!==') LT!* relational_expression)*
; */
static void
parse_equality_expression (void)
{
// IDX expr1, lhs, expr2;
STACK_DECLARE_USAGE (IDX)
parse_relational_expression ();
skip_newlines ();
while (true)
{
switch (TOK ().type)
{
case TOK_DOUBLE_EQ: DUMP_OF (equal_value, relational_expression); break;
case TOK_NOT_EQ: DUMP_OF (not_equal_value, relational_expression); break;
case TOK_TRIPLE_EQ: DUMP_OF (equal_value_type, relational_expression); break;
case TOK_NOT_DOUBLE_EQ: DUMP_OF (not_equal_value_type, relational_expression); break;
default:
{
lexer_save_token (TOK ());
goto cleanup;
}
}
skip_newlines ();
}
cleanup:
STACK_CHECK_USAGE_LHS ();
}
#define PARSE_OF(FUNC, EXPR, TOK_TYPE, GETOP) \
static void parse_##FUNC (void) { \
STACK_DECLARE_USAGE (IDX) \
parse_##EXPR (); \
skip_newlines (); \
while (true) \
{ \
switch (TOK ().type) \
{ \
case TOK_##TOK_TYPE: DUMP_OF (GETOP, EXPR); break; \
default: lexer_save_token (TOK ()); goto cleanup; \
} \
skip_newlines (); \
} \
cleanup: \
STACK_CHECK_USAGE_LHS () \
}
/* bitwise_and_expression
: equality_expression (LT!* '&' LT!* equality_expression)*
; */
PARSE_OF (bitwise_and_expression, equality_expression, AND, b_and)
/* bitwise_xor_expression
: bitwise_and_expression (LT!* '^' LT!* bitwise_and_expression)*
; */
PARSE_OF (bitwise_xor_expression, bitwise_and_expression, XOR, b_xor)
/* bitwise_or_expression
: bitwise_xor_expression (LT!* '|' LT!* bitwise_xor_expression)*
; */
PARSE_OF (bitwise_or_expression, bitwise_xor_expression, OR, b_or)
/* logical_and_expression
: bitwise_or_expression (LT!* '&&' LT!* bitwise_or_expression)*
; */
PARSE_OF (logical_and_expression, bitwise_or_expression, DOUBLE_AND, logical_and)
/* logical_or_expression
: logical_and_expression (LT!* '||' LT!* logical_and_expression)*
; */
PARSE_OF (logical_or_expression, logical_and_expression, DOUBLE_OR, logical_or)
/* conditional_expression
: logical_or_expression (LT!* '?' LT!* assignment_expression LT!* ':' LT!* assignment_expression)?
; */
static void
parse_conditional_expression (bool *was_conditional)
{
// IDX expr, res, lhs
STACK_DECLARE_USAGE (IDX)
STACK_DECLARE_USAGE (U16)
parse_logical_or_expression ();
skip_newlines ();
if (token_is (TOK_QUERY))
{
DUMP_OPCODE_3 (is_true_jmp_down, STACK_HEAD (IDX, 1), 0, 2);
STACK_PUSH (IDX, next_temp_name ());
STACK_PUSH (U16, OPCODE_COUNTER ());
DUMP_OPCODE_2 (jmp_down, INVALID_VALUE, INVALID_VALUE);
NEXT (assignment_expression);
DUMP_OPCODE_3 (assignment, STACK_HEAD (IDX, 2), OPCODE_ARG_TYPE_VARIABLE, STACK_HEAD (IDX, 1));
token_after_newlines_must_be (TOK_COLON);
REWRITE_JMP (STACK_HEAD (U16, 1), jmp_down, OPCODE_COUNTER () - STACK_HEAD (U16, 1));
STACK_HEAD (U16, 1) = OPCODE_COUNTER ();
DUMP_OPCODE_2 (jmp_down, INVALID_VALUE, INVALID_VALUE);
STACK_DROP (IDX, 1);
NEXT (assignment_expression);
DUMP_OPCODE_3 (assignment, STACK_HEAD (IDX, 2), OPCODE_ARG_TYPE_VARIABLE, STACK_HEAD (IDX, 1));
REWRITE_JMP (STACK_HEAD (U16, 1), jmp_down, OPCODE_COUNTER () - STACK_HEAD (U16, 1));
*was_conditional = true;
STACK_HEAD (IDX, 3) = STACK_HEAD (IDX, 2);
STACK_DROP (IDX, 2);
}
else
{
lexer_save_token (TOK ());
}
STACK_CHECK_USAGE (U16);
STACK_CHECK_USAGE_LHS ();
}
/* assignment_expression
: conditional_expression
| left_hand_side_expression LT!* assignment_operator LT!* assignment_expression
; */
static void
parse_assignment_expression (void)
{
// IDX lhs, rhs;
STACK_DECLARE_USAGE (IDX)
bool was_conditional = false;
parse_conditional_expression (&was_conditional);
if (was_conditional)
{
goto cleanup;
}
skip_newlines ();
switch (TOK ().type)
{
case TOK_EQ:
{
NEXT (assignment_expression);
DUMP_OPCODE_3 (assignment, STACK_HEAD (IDX, 2), OPCODE_ARG_TYPE_VARIABLE, STACK_HEAD (IDX, 1));
break;
}
case TOK_MULT_EQ:
{
NEXT (assignment_expression);
DUMP_OPCODE_3 (multiplication, STACK_HEAD (IDX, 2), STACK_HEAD (IDX, 2), STACK_HEAD (IDX, 1));
break;
}
case TOK_DIV_EQ:
{
NEXT (assignment_expression);
DUMP_OPCODE_3 (division, STACK_HEAD (IDX, 2), STACK_HEAD (IDX, 2), STACK_HEAD (IDX, 1));
break;
}
case TOK_MOD_EQ:
{
NEXT (assignment_expression);
DUMP_OPCODE_3 (remainder, STACK_HEAD (IDX, 2), STACK_HEAD (IDX, 2), STACK_HEAD (IDX, 1));
break;
}
case TOK_PLUS_EQ:
{
NEXT (assignment_expression);
DUMP_OPCODE_3 (addition, STACK_HEAD (IDX, 2), STACK_HEAD (IDX, 2), STACK_HEAD (IDX, 1));
break;
}
case TOK_MINUS_EQ:
{
NEXT (assignment_expression);
DUMP_OPCODE_3 (substraction, STACK_HEAD (IDX, 2), STACK_HEAD (IDX, 2), STACK_HEAD (IDX, 1));
break;
}
case TOK_LSHIFT_EQ:
{
NEXT (assignment_expression);
DUMP_OPCODE_3 (b_shift_left, STACK_HEAD (IDX, 2), STACK_HEAD (IDX, 2), STACK_HEAD (IDX, 1));
break;
}
case TOK_RSHIFT_EQ:
{
NEXT (assignment_expression);
DUMP_OPCODE_3 (b_shift_right, STACK_HEAD (IDX, 2), STACK_HEAD (IDX, 2), STACK_HEAD (IDX, 1));
break;
}
case TOK_RSHIFT_EX_EQ:
{
NEXT (assignment_expression);
DUMP_OPCODE_3 (b_shift_uright, STACK_HEAD (IDX, 2), STACK_HEAD (IDX, 2), STACK_HEAD (IDX, 1));
break;
}
case TOK_AND_EQ:
{
NEXT (assignment_expression);
DUMP_OPCODE_3 (b_and, STACK_HEAD (IDX, 2), STACK_HEAD (IDX, 2), STACK_HEAD (IDX, 1));
break;
}
case TOK_XOR_EQ:
{
NEXT (assignment_expression);
DUMP_OPCODE_3 (b_xor, STACK_HEAD (IDX, 2), STACK_HEAD (IDX, 2), STACK_HEAD (IDX, 1));
break;
}
case TOK_OR_EQ:
{
NEXT (assignment_expression);
DUMP_OPCODE_3 (b_or, STACK_HEAD (IDX, 2), STACK_HEAD (IDX, 2), STACK_HEAD (IDX, 1));
break;
}
default:
{
lexer_save_token (TOK ());
goto cleanup;
}
}
STACK_DROP (IDX, 1);
cleanup:
STACK_CHECK_USAGE_LHS ();
}
/* expression
: assignment_expression (LT!* ',' LT!* assignment_expression)*
;
*/
static void
parse_expression (void)
{
// IDX expr
STACK_DECLARE_USAGE (IDX)
parse_assignment_expression ();
while (true)
{
skip_newlines ();
if (token_is (TOK_COMMA))
{
NEXT (assignment_expression);
STACK_HEAD (IDX, 2) = STACK_HEAD (IDX, 1);
STACK_DROP (IDX, 1);
}
else
{
lexer_save_token (TOK ());
break;
}
}
STACK_CHECK_USAGE_LHS ();
}
/* variable_declaration
: Identifier LT!* initialiser?
;
initialiser
: '=' LT!* assignment_expression
; */
static void
parse_variable_declaration (void)
{
//IDX name, expr;
STACK_DECLARE_USAGE (IDX)
current_token_must_be (TOK_NAME);
STACK_PUSH (IDX, token_data ());
DUMP_OPCODE_1 (var_decl, STACK_HEAD (IDX, 1));
skip_newlines ();
if (token_is (TOK_EQ))
{
NEXT (assignment_expression);
DUMP_OPCODE_3 (assignment, STACK_HEAD (IDX, 2), OPCODE_ARG_TYPE_VARIABLE, STACK_HEAD (IDX, 1));
STACK_DROP (IDX, 1);
}
else
{
lexer_save_token (TOK ());
}
STACK_DROP (IDX, 1);
STACK_CHECK_USAGE (IDX);
}
/* variable_declaration_list
: variable_declaration
(LT!* ',' LT!* variable_declaration)*
; */
static void
parse_variable_declaration_list (bool *several_decls)
{
STACK_DECLARE_USAGE (IDX)
while (true)
{
parse_variable_declaration ();
skip_newlines ();
if (!token_is (TOK_COMMA))
{
lexer_save_token (TOK ());
return;
}
skip_newlines ();
if (several_decls)
{
*several_decls = true;
}
}
STACK_CHECK_USAGE (IDX);
}
/* for_statement
: 'for' LT!* '(' (LT!* for_statement_initialiser_part)? LT!* ';'
(LT!* expression)? LT!* ';' (LT!* expression)? LT!* ')' LT!* statement
;
for_statement_initialiser_part
: expression
| 'var' LT!* variable_declaration_list
;
for_in_statement
: 'for' LT!* '(' LT!* for_in_statement_initialiser_part LT!* 'in'
LT!* expression LT!* ')' LT!* statement
;
for_in_statement_initialiser_part
: left_hand_side_expression
| 'var' LT!* variable_declaration
;*/
static void
parse_for_or_for_in_statement (void)
{
// IDX stop;
// U16 cond_oc, body_oc, step_oc, end_oc;
STACK_DECLARE_USAGE (IDX)
STACK_DECLARE_USAGE (U16)
STACK_DECLARE_USAGE (nestings)
assert_keyword (KW_FOR);
token_after_newlines_must_be (TOK_OPEN_PAREN);
skip_newlines ();
if (token_is (TOK_SEMICOLON))
{
goto plain_for;
}
/* Both for_statement_initialiser_part and for_in_statement_initialiser_part
contains 'var'. Check it first. */
if (is_keyword (KW_VAR))
{
bool several_decls = false;
skip_newlines ();
parse_variable_declaration_list (&several_decls);
if (several_decls)
{
token_after_newlines_must_be (TOK_SEMICOLON);
goto plain_for;
}
else
{
skip_newlines ();
if (token_is (TOK_SEMICOLON))
{
goto plain_for;
}
else if (is_keyword (KW_IN))
{
goto for_in;
}
else
{
parser_fatal (ERR_PARSER);
}
}
}
/* expression contains left_hand_side_expression. */
parse_expression ();
STACK_DROP (IDX, 1);
skip_newlines ();
if (token_is (TOK_SEMICOLON))
{
goto plain_for;
}
else if (is_keyword (KW_IN))
{
goto for_in;
}
else
{
parser_fatal (ERR_PARSER);
}
JERRY_UNREACHABLE ();
plain_for:
/* Represent loop like
for (i = 0; i < 10; i++) {
body;
}
as
11 i = #0;
cond_oc:
12 tmp1 = #10;
13 tmp2 = i < tmp1;
end_oc:
14 is_false_jmp_down tmp2, +8 // end of loop
body_oc:
15 jmp_down 5 // body
step_oc:
16 tmp3 = #1;
17 tmp4 = i + 1;
18 i = tmp4;
19 jmp_up 7; // cond_oc
20 body
21 jmp_up 5; // step_oc;
22 ...
*/
STACK_PUSH (U16, OPCODE_COUNTER ()); // cond_oc;
skip_newlines ();
if (!token_is (TOK_SEMICOLON))
{
parse_assignment_expression ();
next_token_must_be (TOK_SEMICOLON);
}
else
{
boolean_true ();
}
STACK_PUSH (U16, OPCODE_COUNTER ()); // end_oc;
DUMP_OPCODE_3 (is_false_jmp_down, INVALID_VALUE, INVALID_VALUE, INVALID_VALUE);
STACK_PUSH (U16, OPCODE_COUNTER ()); // body_oc;
DUMP_OPCODE_2 (jmp_down, INVALID_VALUE, INVALID_VALUE);
STACK_PUSH (U16, OPCODE_COUNTER ()); // step_oc;
skip_newlines ();
if (!token_is (TOK_CLOSE_PAREN))
{
parse_assignment_expression ();
STACK_DROP (IDX, 1);
next_token_must_be (TOK_CLOSE_PAREN);
}
DUMP_OPCODE_2 (jmp_up, 0, OPCODE_COUNTER () - STACK_HEAD (U16, 4));
REWRITE_JMP (STACK_HEAD (U16, 2), jmp_down, OPCODE_COUNTER () - STACK_HEAD (U16, 2));
skip_newlines ();
push_nesting (NESTING_ITERATIONAL);
parse_statement ();
pop_nesting (NESTING_ITERATIONAL);
DUMP_OPCODE_2 (jmp_up, 0, OPCODE_COUNTER () - STACK_HEAD (U16, 1));
REWRITE_COND_JMP (STACK_HEAD (U16, 3), is_false_jmp_down, OPCODE_COUNTER () - STACK_HEAD (U16, 3));
rewrite_rewritable_opcodes (REWRITABLE_CONTINUE, STACK_HEAD (U16, 1));
rewrite_rewritable_opcodes (REWRITABLE_BREAK, OPCODE_COUNTER ());
STACK_DROP (IDX, 1);
STACK_DROP (U16, 4);
goto cleanup;
for_in:
JERRY_UNIMPLEMENTED ();
cleanup:
STACK_CHECK_USAGE (IDX);
STACK_CHECK_USAGE (U16);
STACK_CHECK_USAGE (nestings);
}
static void
parse_expression_inside_parens (void)
{
// IDX expr;
STACK_DECLARE_USAGE (IDX)
token_after_newlines_must_be (TOK_OPEN_PAREN);
NEXT (expression);
token_after_newlines_must_be (TOK_CLOSE_PAREN);
STACK_CHECK_USAGE_LHS ();
}
/* statement_list
: statement (LT!* statement)*
; */
static void
parse_statement_list (void)
{
STACK_DECLARE_USAGE (IDX)
while (true)
{
parse_statement ();
skip_newlines ();
while (token_is (TOK_SEMICOLON))
{
skip_newlines ();
}
if (token_is (TOK_CLOSE_BRACE))
{
lexer_save_token (TOK ());
break;
}
}
STACK_CHECK_USAGE (IDX);
}
/* if_statement
: 'if' LT!* '(' LT!* expression LT!* ')' LT!* statement (LT!* 'else' LT!* statement)?
; */
static void
parse_if_statement (void)
{
// IDX cond;
// U16 cond_oc;
STACK_DECLARE_USAGE (IDX)
STACK_DECLARE_USAGE (U16)
assert_keyword (KW_IF);
parse_expression_inside_parens ();
STACK_PUSH (U16, OPCODE_COUNTER ());
DUMP_OPCODE_3 (is_false_jmp_down, INVALID_VALUE, INVALID_VALUE, INVALID_VALUE);
skip_newlines ();
parse_statement ();
skip_newlines ();
if (is_keyword (KW_ELSE))
{
STACK_PUSH (U16, OPCODE_COUNTER ());
DUMP_OPCODE_2 (jmp_down, INVALID_VALUE, INVALID_VALUE);
REWRITE_COND_JMP (STACK_HEAD (U16, 2), is_false_jmp_down, OPCODE_COUNTER () - STACK_HEAD (U16, 2));
skip_newlines ();
parse_statement ();
REWRITE_JMP (STACK_HEAD (U16, 1), jmp_down, OPCODE_COUNTER () - STACK_HEAD (U16, 1));
STACK_DROP (U16, 1);
}
else
{
REWRITE_COND_JMP (STACK_HEAD (U16, 1), is_false_jmp_down, OPCODE_COUNTER () - STACK_HEAD (U16, 1));
lexer_save_token (TOK ());
}
STACK_DROP (U16, 1);
STACK_DROP (IDX, 1);
STACK_CHECK_USAGE (U16);
STACK_CHECK_USAGE (IDX);
}
/* do_while_statement
: 'do' LT!* statement LT!* 'while' LT!* '(' expression ')' (LT | ';')!
; */
static void
parse_do_while_statement (void)
{
// IDX cond;
// U16 loop_oc;
STACK_DECLARE_USAGE (IDX)
STACK_DECLARE_USAGE (U16)
STACK_DECLARE_USAGE (nestings)
assert_keyword (KW_DO);
STACK_PUSH (U16, OPCODE_COUNTER ());
skip_newlines ();
push_nesting (NESTING_ITERATIONAL);
parse_statement ();
pop_nesting (NESTING_ITERATIONAL);
token_after_newlines_must_be_keyword (KW_WHILE);
parse_expression_inside_parens ();
STACK_PUSH (U16, OPCODE_COUNTER ());
DUMP_OPCODE_3 (is_true_jmp_up, INVALID_VALUE, INVALID_VALUE, INVALID_VALUE);
REWRITE_COND_JMP (STACK_HEAD(U16, 1), is_true_jmp_up, OPCODE_COUNTER () - STACK_HEAD (U16, 2));
rewrite_rewritable_opcodes (REWRITABLE_CONTINUE, STACK_HEAD (U16, 2));
rewrite_rewritable_opcodes (REWRITABLE_BREAK, OPCODE_COUNTER ());
STACK_DROP (IDX, 1);
STACK_DROP (U16, 2);
STACK_CHECK_USAGE (U16);
STACK_CHECK_USAGE (IDX);
STACK_CHECK_USAGE (nestings);
}
/* while_statement
: 'while' LT!* '(' LT!* expression LT!* ')' LT!* statement
; */
static void
parse_while_statement (void)
{
// IDX cond;
// U16 cond_oc, jmp_oc;
STACK_DECLARE_USAGE (IDX)
STACK_DECLARE_USAGE (U16)
STACK_DECLARE_USAGE (nestings)
assert_keyword (KW_WHILE);
STACK_PUSH (U16, OPCODE_COUNTER ()); // cond_oc;
parse_expression_inside_parens ();
STACK_PUSH (U16, OPCODE_COUNTER ()); // jmp_oc;
DUMP_OPCODE_3 (is_false_jmp_down, INVALID_VALUE, INVALID_VALUE, INVALID_VALUE);
skip_newlines ();
push_nesting (NESTING_ITERATIONAL);
parse_statement ();
pop_nesting (NESTING_ITERATIONAL);
STACK_PUSH (U16, OPCODE_COUNTER ());
DUMP_OPCODE_2 (jmp_up, INVALID_VALUE, INVALID_VALUE);
REWRITE_JMP (STACK_HEAD (U16, 1), jmp_up, OPCODE_COUNTER () - STACK_HEAD (U16, 3));
REWRITE_COND_JMP (STACK_HEAD (U16, 2), is_false_jmp_down, OPCODE_COUNTER () - STACK_HEAD (U16, 2));
rewrite_rewritable_opcodes (REWRITABLE_CONTINUE, STACK_HEAD (U16, 3));
rewrite_rewritable_opcodes (REWRITABLE_BREAK, OPCODE_COUNTER ());
STACK_DROP (IDX, 1);
STACK_DROP (U16, 3);
STACK_CHECK_USAGE (U16);
STACK_CHECK_USAGE (IDX);
STACK_CHECK_USAGE (nestings);
}
/* with_statement
: 'with' LT!* '(' LT!* expression LT!* ')' LT!* statement
; */
static void
parse_with_statement (void)
{
// IDX expr;
STACK_DECLARE_USAGE (IDX)
assert_keyword (KW_WITH);
parse_expression_inside_parens ();
DUMP_OPCODE_1 (with, STACK_HEAD (IDX, 1));
skip_newlines ();
parse_statement ();
DUMP_OPCODE_3 (meta, OPCODE_META_TYPE_END_WITH, INVALID_VALUE, INVALID_VALUE);
STACK_DROP (IDX, 1);
STACK_CHECK_USAGE (IDX);
}
/* case_block
: '{' LT!* case_clause* LT!* '}'
| '{' LT!* case_clause* LT!* default_clause LT!* case_clause* LT!* '}'
;
case_clause
: 'case' LT!* expression LT!* ':' LT!* statement*
;
default_clause
: 'default' LT!* ':' LT!* statement*
; */
/* switch_statement
: 'switch' LT!* '(' LT!* expression LT!* ')' LT!* '{' LT!* case_block LT!* '}'
; */
static void
parse_switch_statement (void)
{
JERRY_UNIMPLEMENTED ();
}
/* catch_clause
: 'catch' LT!* '(' LT!* Identifier LT!* ')' LT!* '{' LT!* statement_list LT!* '}'
; */
static void
parse_catch_clause (void)
{
// IDX ex_name;
// U16 catch_oc;
STACK_DECLARE_USAGE (IDX)
STACK_DECLARE_USAGE (U16)
assert_keyword (KW_CATCH);
token_after_newlines_must_be (TOK_OPEN_PAREN);
token_after_newlines_must_be (TOK_NAME);
STACK_PUSH (IDX, token_data ());
token_after_newlines_must_be (TOK_CLOSE_PAREN);
STACK_PUSH (U16, OPCODE_COUNTER ());
DUMP_OPCODE_3 (meta, OPCODE_META_TYPE_CATCH, INVALID_VALUE, INVALID_VALUE);
DUMP_OPCODE_3 (meta, OPCODE_META_TYPE_CATCH_EXCEPTION_IDENTIFIER, STACK_HEAD (IDX, 1), INVALID_VALUE);
token_after_newlines_must_be (TOK_OPEN_BRACE);
skip_newlines ();
parse_statement_list ();
next_token_must_be (TOK_CLOSE_BRACE);
rewrite_meta_opcode_counter (STACK_HEAD (U16, 1), OPCODE_META_TYPE_CATCH);
STACK_DROP (U16, 1);
STACK_DROP (IDX, 1);
STACK_CHECK_USAGE (IDX);
STACK_CHECK_USAGE (U16);
}
/* finally_clause
: 'finally' LT!* '{' LT!* statement_list LT!* '}'
; */
static void
parse_finally_clause (void)
{
// U16 finally_oc;
STACK_DECLARE_USAGE (U16)
assert_keyword (KW_FINALLY);
STACK_PUSH (U16, OPCODE_COUNTER ());
DUMP_OPCODE_3 (meta, OPCODE_META_TYPE_FINALLY, INVALID_VALUE, INVALID_VALUE);
token_after_newlines_must_be (TOK_OPEN_BRACE);
skip_newlines ();
parse_statement_list ();
next_token_must_be (TOK_CLOSE_BRACE);
rewrite_meta_opcode_counter (STACK_HEAD (U16, 1), OPCODE_META_TYPE_FINALLY);
STACK_DROP (U16, 1);
STACK_CHECK_USAGE (U16);
}
/* try_statement
: 'try' LT!* '{' LT!* statement_list LT!* '}' LT!* (finally_clause | catch_clause (LT!* finally_clause)?)
; */
static void
parse_try_statement (void)
{
// U16 try_oc;
STACK_DECLARE_USAGE (U16)
assert_keyword (KW_TRY);
STACK_PUSH (U16, OPCODE_COUNTER ());
DUMP_OPCODE_2 (try, INVALID_VALUE, INVALID_VALUE);
token_after_newlines_must_be (TOK_OPEN_BRACE);
skip_newlines ();
parse_statement_list ();
next_token_must_be (TOK_CLOSE_BRACE);
REWRITE_TRY (STACK_HEAD (U16, 1));
token_after_newlines_must_be (TOK_KEYWORD);
if (is_keyword (KW_CATCH))
{
parse_catch_clause ();
skip_newlines ();
if (is_keyword (KW_FINALLY))
{
parse_finally_clause ();
}
else
{
lexer_save_token (TOK ());
}
}
else if (is_keyword (KW_FINALLY))
{
parse_finally_clause ();
}
else
{
parser_fatal (ERR_PARSER);
}
DUMP_OPCODE_3 (meta, OPCODE_META_TYPE_END_TRY_CATCH_FINALLY, INVALID_VALUE, INVALID_VALUE);
STACK_DROP (U16, 1);
STACK_CHECK_USAGE (U16);
}
static void
insert_semicolon (void)
{
skip_token ();
JERRY_ASSERT (token_is (TOK_SEMICOLON) || token_is (TOK_NEWLINE));
}
/* statement
: statement_block
| variable_statement
| empty_statement
| if_statement
| iteration_statement
| continue_statement
| break_statement
| return_statement
| with_statement
| labelled_statement
| switch_statement
| throw_statement
| try_statement
| expression_statement
;
statement_block
: '{' LT!* statement_list? LT!* '}'
;
variable_statement
: 'var' LT!* variable_declaration_list (LT | ';')!
;
empty_statement
: ';'
;
expression_statement
: expression (LT | ';')!
;
iteration_statement
: do_while_statement
| while_statement
| for_statement
| for_in_statement
;
continue_statement
: 'continue' Identifier? (LT | ';')!
;
break_statement
: 'break' Identifier? (LT | ';')!
;
return_statement
: 'return' expression? (LT | ';')!
;
switchStatement
: 'switch' LT!* '(' LT!* expression LT!* ')' LT!* caseBlock
;
throw_statement
: 'throw' expression (LT | ';')!
;
try_statement
: 'try' LT!* '{' LT!* statement_list LT!* '}' LT!* (finally_clause | catch_clause (LT!* finally_clause)?)
;*/
static void
parse_statement (void)
{
reset_temp_name ();
STACK_DECLARE_USAGE (IDX)
STACK_DECLARE_USAGE (toks)
if (token_is (TOK_CLOSE_BRACE))
{
lexer_save_token (TOK ());
goto cleanup;
}
if (token_is (TOK_OPEN_BRACE))
{
skip_newlines ();
if (!token_is (TOK_CLOSE_BRACE))
{
parse_statement_list ();
next_token_must_be (TOK_CLOSE_BRACE);
}
goto cleanup;
}
if (is_keyword (KW_VAR))
{
skip_newlines ();
parse_variable_declaration_list (NULL);
goto cleanup;
}
if (token_is (TOK_SEMICOLON))
{
goto cleanup;
}
if (is_keyword (KW_IF))
{
parse_if_statement ();
goto cleanup;
}
if (is_keyword (KW_DO))
{
parse_do_while_statement ();
goto cleanup;
}
if (is_keyword (KW_WHILE))
{
parse_while_statement ();
goto cleanup;
}
if (is_keyword (KW_FOR))
{
parse_for_or_for_in_statement ();
goto cleanup;
}
if (is_keyword (KW_CONTINUE))
{
uint8_t *temp = mem_heap_alloc_block (1, MEM_HEAP_ALLOC_SHORT_TERM);
temp[0] = NESTING_ITERATIONAL;
must_be_inside_but_not_in (temp, 1, NESTING_FUNCTION);
add_to_rewritable_opcodes (REWRITABLE_CONTINUE, OPCODE_COUNTER ());
mem_heap_free_block (temp);
DUMP_OPCODE_2 (jmp_up, INVALID_VALUE, INVALID_VALUE);
goto cleanup;
}
if (is_keyword (KW_BREAK))
{
uint8_t *temp = mem_heap_alloc_block (2, MEM_HEAP_ALLOC_SHORT_TERM);
temp[0] = NESTING_ITERATIONAL;
temp[1] = NESTING_SWITCH;
must_be_inside_but_not_in (temp, 2, NESTING_FUNCTION);
add_to_rewritable_opcodes (REWRITABLE_BREAK, opcode_counter);
mem_heap_free_block (temp);
DUMP_OPCODE_2 (jmp_down, INVALID_VALUE, INVALID_VALUE);
goto cleanup;
}
if (is_keyword (KW_RETURN))
{
skip_token ();
if (!token_is (TOK_SEMICOLON) && !token_is (TOK_NEWLINE))
{
parse_expression ();
DUMP_OPCODE_1 (retval, STACK_HEAD (IDX, 1));
STACK_DROP (IDX, 1);
insert_semicolon ();
goto cleanup;
}
else
{
DUMP_VOID_OPCODE (ret);
goto cleanup;
}
}
if (is_keyword (KW_WITH))
{
parse_with_statement ();
goto cleanup;
}
if (is_keyword (KW_SWITCH))
{
parse_switch_statement ();
goto cleanup;
}
if (is_keyword (KW_THROW))
{
skip_token ();
parse_expression ();
insert_semicolon ();
DUMP_OPCODE_1 (throw, STACK_HEAD (IDX, 1));
STACK_DROP (IDX, 1);
goto cleanup;
}
if (is_keyword (KW_TRY))
{
parse_try_statement ();
goto cleanup;
}
if (token_is (TOK_NAME))
{
STACK_PUSH (toks, TOK ());
skip_newlines ();
if (token_is (TOK_COLON))
{
// STMT_LABELLED;
JERRY_UNIMPLEMENTED ();
}
else
{
lexer_save_token (TOK ());
STACK_POP (toks, TOK());
parse_expression ();
STACK_DROP (IDX, 1);
goto cleanup;
}
}
else
{
parse_expression ();
STACK_DROP (IDX, 1);
goto cleanup;
}
cleanup:
STACK_CHECK_USAGE (IDX);
STACK_CHECK_USAGE (toks);
}
/* source_element
: function_declaration
| statement
; */
static void
parse_source_element (void)
{
STACK_DECLARE_USAGE (IDX)
if (is_keyword (KW_FUNCTION))
{
parse_function_declaration ();
}
else
{
parse_statement ();
}
STACK_CHECK_USAGE (IDX);
}
/* source_element_list
: source_element (LT!* source_element)*
; */
static void
parse_source_element_list (void)
{
// U16 reg_var_decl_loc;
STACK_DECLARE_USAGE (U16)
STACK_DECLARE_USAGE (IDX)
STACK_DECLARE_USAGE (temp_names)
start_new_scope ();
STACK_PUSH (U16, OPCODE_COUNTER ());
DUMP_OPCODE_2 (reg_var_decl, MIN_TEMP_NAME (), INVALID_VALUE);
while (!token_is (TOK_EOF) && !token_is (TOK_CLOSE_BRACE))
{
parse_source_element ();
skip_newlines ();
}
lexer_save_token (TOK ());
REWRITE_OPCODE_2 (STACK_HEAD (U16, 1), reg_var_decl, MIN_TEMP_NAME (), MAX_TEMP_NAME ());
finish_scope ();
STACK_DROP (U16, 1);
STACK_CHECK_USAGE (U16);
STACK_CHECK_USAGE (IDX);
STACK_CHECK_USAGE (temp_names);
}
/* program
: LT!* source_element_list LT!* EOF!
; */
void
parser_parse_program (void)
{
STACK_DECLARE_USAGE (IDX)
skip_newlines ();
parse_source_element_list ();
skip_newlines ();
JERRY_ASSERT (token_is (TOK_EOF));
DUMP_OPCODE_1 (exitval, 0);
serializer_adjust_strings ();
STACK_CHECK_USAGE (IDX);
}
void
parser_init (const char *source, size_t source_size, bool show_opcodes)
{
lexer_init (source, source_size, show_opcodes);
serializer_init (show_opcodes);
lexer_run_first_pass ();
lexer_adjust_num_ids ();
serializer_dump_strings_and_nums (lexer_get_strings (), lexer_get_strings_count (),
lexer_get_nums (), lexer_get_nums_count ());
STACK_INIT (uint8_t, U8);
STACK_INIT (uint8_t, IDX);
STACK_INIT (uint8_t, nestings);
STACK_INIT (uint8_t, temp_names);
STACK_INIT (token, toks);
STACK_INIT (opcode_t, ops);
STACK_INIT (uint16_t, U16);
STACK_INIT (opcode_counter_t, rewritable_continue);
STACK_INIT (opcode_counter_t, rewritable_break);
HASH_INIT (intrinsics, 1);
fill_intrinsics ();
MAX_TEMP_NAME () = TEMP_NAME () = MIN_TEMP_NAME () = lexer_get_reserved_ids_count ();
OPCODE_COUNTER () = 0;
}
void
parser_free (void)
{
STACK_FREE (U8);
STACK_FREE (IDX);
STACK_FREE (nestings);
STACK_FREE (temp_names);
STACK_FREE (toks);
STACK_FREE (ops);
STACK_FREE (U16);
STACK_FREE (rewritable_continue);
STACK_FREE (rewritable_break);
HASH_FREE (intrinsics);
serializer_free ();
lexer_free ();
}
void
parser_fatal (jerry_status_t code)
{
__printf ("FATAL: %d\n", code);
lexer_dump_buffer_state ();
jerry_exit (code);
}