Add try-catch-finally support: parse and generate opcodes for this construct

Fix varg generation: generate *_n opcodes with parameters in following meta opcodes
Add stack internal structure: dimanically allocated stack.
Use dynamically allocated memory in parser: every local and global variables are stored in dinamically allocated stacks.
Use dynamically allocated memory in serializer: opcodes are also stored in stack.
Change is_true_jmp and is_false_jmp opcodes to relative.
Change *jmp* opcodes to be able to store opcode_counter_t instead of idx_t.
This commit is contained in:
Ilmir Usmanov
2014-09-16 21:22:11 +04:00
parent cd41b236d9
commit e77bd4f4e5
26 changed files with 1644 additions and 1710 deletions
+180
View File
@@ -0,0 +1,180 @@
/* 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.
*/
/**
This file contains macros to define and use stacks.
Do define stack type use macro DEFINE_STACK_TYPE.
After definition of type use macro STACK to create stack variable and define all necessaty routines.
Also, define variable with name NAME##_global_size. If the variable more than 0,
first NAME##_global_size element will remain untouched during PUSH and POP operations.
Before using the stack, init it by calling INIT_STACK macro.
Use macros PUSH, POP, DROP, CLEAN and HEAD to manipulate the stack.
DO NOT FORGET to free stack memory by calling FREE_STACK macro.
For check usage of stack during a function, use DECLARE_USAGE and CHECK_USAGE macros.
Example (parser.c):
#ifndef UINT8_T_STACK_DEFINED
#define UINT8_T_STACK_DEFINED
DEFINE_STACK_TYPE (uint8_t)
#endif
enum
{
temp_name,
min_temp_name,
max_temp_name,
temp_names_global_size
};
STACK(uint8_t, temp_names)
#define GLOBAL(NAME, VAR) \
NAME.data[VAR]
#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)
void
parser_init (void)
{
INIT_STACK(uint8_t, temp_names)
}
void
parser_free (void)
{
FREE_STACK(temp_names)
}
*/
#ifndef STACK_H
#define STACK_H
#define DEFINE_STACK_TYPE(DATA_TYPE, TYPE) \
typedef struct \
{ \
DATA_TYPE length; \
DATA_TYPE current; \
TYPE *data; \
} \
__packed \
TYPE##_stack;
#define INIT_STACK(TYPE, NAME) \
do { \
size_t NAME##_size = mem_heap_recommend_allocation_size (sizeof (TYPE) * NAME##_global_size); \
NAME.data = (TYPE *) mem_heap_alloc_block (NAME##_size, MEM_HEAP_ALLOC_SHORT_TERM); \
NAME.current = NAME##_global_size; \
NAME.length = (__typeof__ (NAME.length)) (NAME##_size / sizeof (TYPE)); \
} while (0)
#define FREE_STACK(NAME) \
do { \
mem_heap_free_block ((uint8_t *) NAME.data); \
NAME.length = NAME.current = 0; \
} while (0)
/* In most cases (for example, in parser) default size of stack is enough.
However, in serializer, there is a need for reallocation of memory.
Do this in several steps:
1) Allocate already allocated memory size twice.
2) Copy used memory to the last.
3) Dealocate first two chuncks.
4) Allocate new memory. (It must point to the memory before increasing).
5) Copy data back.
6) Free temp buffer. */
#define DEFINE_INCREASE_STACK_SIZE(TYPE, NAME) \
static void increase_##NAME##_stack_size (__typeof__ (NAME.length)) __unused; \
static void \
increase_##NAME##_stack_size (__typeof__ (NAME.length) elements_count) { \
if (NAME.current + elements_count >= NAME.length) \
{ \
size_t old_size = NAME.length * sizeof (TYPE); \
size_t temp1_size = mem_heap_recommend_allocation_size ( \
(size_t) (elements_count * sizeof (TYPE))); \
size_t new_size = mem_heap_recommend_allocation_size ( \
(size_t) (temp1_size + old_size)); \
TYPE *temp1 = (TYPE *) mem_heap_alloc_block (temp1_size, MEM_HEAP_ALLOC_SHORT_TERM); \
TYPE *temp2 = (TYPE *) 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.data, old_size); \
mem_heap_free_block ((uint8_t *) NAME.data); \
mem_heap_free_block ((uint8_t *) temp1); \
NAME.data = (TYPE *) mem_heap_alloc_block (new_size, MEM_HEAP_ALLOC_SHORT_TERM); \
__memcpy (NAME.data, temp2, old_size); \
mem_heap_free_block ((uint8_t *) temp2); \
NAME.length = (__typeof__ (NAME.length)) (new_size / sizeof (TYPE)); \
} \
NAME.current = (__typeof__ (NAME.current)) (NAME.current + elements_count); \
}
#define DEFINE_DECREASE_STACE_SIZE(NAME) \
static void decrease_##NAME##_stack_size (uint8_t) __unused; \
static void \
decrease_##NAME##_stack_size (uint8_t elements_count) { \
JERRY_ASSERT (NAME.current - elements_count >= NAME##_global_size); \
NAME.current = (__typeof__ (NAME.current)) (NAME.current - elements_count); \
}
#define PUSH(NAME, VALUE) \
increase_##NAME##_stack_size (1); \
NAME.data[NAME.current - 1] = VALUE;
#define POP(VALUE, NAME) \
decrease_##NAME##_stack_size (1); \
VALUE = NAME.data[NAME.current];
#define DROP(NAME, I) \
decrease_##NAME##_stack_size (I);
#define CLEAN(NAME) \
DROP (NAME, NAME.current - NAME##_global_size);
#define HEAD(NAME, I) \
NAME.data[NAME.current - I]
#define STACK_SIZE(NAME) \
NAME.current
#define STACK(TYPE, NAME) \
TYPE##_stack NAME; \
DEFINE_DECREASE_STACE_SIZE (NAME) \
DEFINE_INCREASE_STACK_SIZE (TYPE, NAME)
#define STATIC_STACK(TYPE, NAME) \
static TYPE##_stack NAME; \
DEFINE_DECREASE_STACE_SIZE (NAME) \
DEFINE_INCREASE_STACK_SIZE (TYPE, NAME)
#ifndef JERRY_NDEBUG
#define DECLARE_USAGE(NAME) \
uint8_t NAME##_current = NAME.current;
#define CHECK_USAGE(NAME) \
JERRY_ASSERT (NAME.current == NAME##_current);
#else
#define DECLARE_USAGE(NAME) ;
#define CHECK_USAGE(NAME) ;
#endif /* JERRY_NDEBUG */
#endif /* STACK_H */