1/* Copyright JS Foundation and other contributors, http://js.foundation
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "ecma-alloc.h"
17#include "ecma-gc.h"
18#include "ecma-helpers.h"
19#include "vm-defines.h"
20#include "vm-stack.h"
21#include "ecma-iterator-object.h"
22
23/** \addtogroup vm Virtual machine
24 * @{
25 *
26 * \addtogroup stack VM stack
27 * @{
28 */
29
30JERRY_STATIC_ASSERT (PARSER_WITH_CONTEXT_STACK_ALLOCATION == PARSER_BLOCK_CONTEXT_STACK_ALLOCATION,
31                     parser_with_context_stack_allocation_must_be_equal_to_parser_block_context_stack_allocation);
32
33/**
34 * Abort (finalize) the current stack context, and remove it.
35 *
36 * @return new stack top
37 */
38ecma_value_t *
39vm_stack_context_abort (vm_frame_ctx_t *frame_ctx_p, /**< frame context */
40                        ecma_value_t *vm_stack_top_p) /**< current stack top */
41{
42  ecma_value_t context_info = vm_stack_top_p[-1];
43
44  if (context_info & VM_CONTEXT_HAS_LEX_ENV)
45  {
46    ecma_object_t *lex_env_p = frame_ctx_p->lex_env_p;
47    JERRY_ASSERT (lex_env_p->u2.outer_reference_cp != JMEM_CP_NULL);
48    frame_ctx_p->lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp);
49    ecma_deref_object (lex_env_p);
50  }
51
52  switch (VM_GET_CONTEXT_TYPE (context_info))
53  {
54    case VM_CONTEXT_FINALLY_THROW:
55    case VM_CONTEXT_FINALLY_RETURN:
56    {
57      ecma_free_value (vm_stack_top_p[-2]);
58      /* FALLTHRU */
59    }
60    case VM_CONTEXT_FINALLY_JUMP:
61    case VM_CONTEXT_TRY:
62    case VM_CONTEXT_CATCH:
63    {
64      VM_MINUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_TRY_CONTEXT_STACK_ALLOCATION);
65      vm_stack_top_p -= PARSER_TRY_CONTEXT_STACK_ALLOCATION;
66      break;
67    }
68#if ENABLED (JERRY_ES2015)
69    case VM_CONTEXT_BLOCK:
70#endif /* ENABLED (JERRY_ES2015) */
71    case VM_CONTEXT_WITH:
72    {
73      VM_MINUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_WITH_CONTEXT_STACK_ALLOCATION);
74      vm_stack_top_p -= PARSER_WITH_CONTEXT_STACK_ALLOCATION;
75      break;
76    }
77#if ENABLED (JERRY_ES2015)
78    case VM_CONTEXT_FOR_OF:
79    {
80      ecma_value_t iterator = vm_stack_top_p[-3];
81
82      if (context_info & VM_CONTEXT_CLOSE_ITERATOR)
83      {
84        ecma_op_iterator_close (iterator);
85      }
86      ecma_free_value (iterator);
87
88      ecma_free_value (vm_stack_top_p[-2]);
89      VM_MINUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION);
90      vm_stack_top_p -= PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION;
91      break;
92    }
93#endif /* ENABLED (JERRY_ES2015) */
94    default:
95    {
96      JERRY_ASSERT (VM_GET_CONTEXT_TYPE (vm_stack_top_p[-1]) == VM_CONTEXT_FOR_IN);
97
98      ecma_collection_t *collection_p;
99      collection_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, vm_stack_top_p[-2]);
100
101      ecma_value_t *buffer_p = collection_p->buffer_p;
102
103      for (uint32_t index = vm_stack_top_p[-3]; index < collection_p->item_count; index++)
104      {
105        ecma_free_value (buffer_p[index]);
106      }
107
108      ecma_collection_destroy (collection_p);
109
110      ecma_free_value (vm_stack_top_p[-4]);
111
112      VM_MINUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION);
113      vm_stack_top_p -= PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION;
114      break;
115    }
116  }
117
118  return vm_stack_top_p;
119} /* vm_stack_context_abort */
120
121/**
122 * Decode branch offset.
123 *
124 * @return branch offset
125 */
126static uint32_t
127vm_decode_branch_offset (const uint8_t *branch_offset_p, /**< start offset of byte code */
128                         uint32_t length) /**< length of the branch */
129{
130  uint32_t branch_offset = *branch_offset_p;
131
132  JERRY_ASSERT (length >= 1 && length <= 3);
133
134  switch (length)
135  {
136    case 3:
137    {
138      branch_offset <<= 8;
139      branch_offset |= *(++branch_offset_p);
140      /* FALLTHRU */
141    }
142    case 2:
143    {
144      branch_offset <<= 8;
145      branch_offset |= *(++branch_offset_p);
146      break;
147    }
148  }
149
150  return branch_offset;
151} /* vm_decode_branch_offset */
152
153/**
154 * Find a finally up to the end position.
155 *
156 * @return true - if 'finally' found,
157 *         false - otherwise
158 */
159bool
160vm_stack_find_finally (vm_frame_ctx_t *frame_ctx_p, /**< frame context */
161                       ecma_value_t **vm_stack_top_ref_p, /**< current stack top */
162                       vm_stack_context_type_t finally_type, /**< searching this finally */
163                       uint32_t search_limit) /**< search up-to this byte code */
164{
165  ecma_value_t *vm_stack_top_p = *vm_stack_top_ref_p;
166
167  JERRY_ASSERT (finally_type <= VM_CONTEXT_FINALLY_RETURN);
168
169  if (finally_type != VM_CONTEXT_FINALLY_JUMP)
170  {
171    search_limit = 0xffffffffu;
172  }
173
174  while (frame_ctx_p->context_depth > 0)
175  {
176    vm_stack_context_type_t context_type;
177    uint32_t context_end = VM_GET_CONTEXT_END (vm_stack_top_p[-1]);
178
179    if (search_limit < context_end)
180    {
181      *vm_stack_top_ref_p = vm_stack_top_p;
182      return false;
183    }
184
185    context_type = VM_GET_CONTEXT_TYPE (vm_stack_top_p[-1]);
186    if (context_type == VM_CONTEXT_TRY || context_type == VM_CONTEXT_CATCH)
187    {
188      const uint8_t *byte_code_p;
189      uint32_t branch_offset_length;
190      uint32_t branch_offset;
191
192      if (search_limit == context_end)
193      {
194        *vm_stack_top_ref_p = vm_stack_top_p;
195        return false;
196      }
197
198#if ENABLED (JERRY_ES2015)
199      if (vm_stack_top_p[-1] & VM_CONTEXT_HAS_LEX_ENV)
200      {
201        ecma_object_t *lex_env_p = frame_ctx_p->lex_env_p;
202        JERRY_ASSERT (lex_env_p->u2.outer_reference_cp != JMEM_CP_NULL);
203        frame_ctx_p->lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp);
204        ecma_deref_object (lex_env_p);
205      }
206#endif /* ENABLED (JERRY_ES2015) */
207
208      byte_code_p = frame_ctx_p->byte_code_start_p + context_end;
209
210      if (context_type == VM_CONTEXT_TRY)
211      {
212        JERRY_ASSERT (byte_code_p[0] == CBC_EXT_OPCODE);
213
214        if (byte_code_p[1] >= CBC_EXT_CATCH
215            && byte_code_p[1] <= CBC_EXT_CATCH_3)
216        {
217          branch_offset_length = CBC_BRANCH_OFFSET_LENGTH (byte_code_p[1]);
218          branch_offset = vm_decode_branch_offset (byte_code_p + 2,
219                                                   branch_offset_length);
220
221          if (finally_type == VM_CONTEXT_FINALLY_THROW)
222          {
223            branch_offset += (uint32_t) (byte_code_p - frame_ctx_p->byte_code_start_p);
224
225            vm_stack_top_p[-1] = VM_CREATE_CONTEXT (VM_CONTEXT_CATCH, branch_offset);
226
227            byte_code_p += 2 + branch_offset_length;
228            frame_ctx_p->byte_code_p = byte_code_p;
229
230            *vm_stack_top_ref_p = vm_stack_top_p;
231            return true;
232          }
233
234          byte_code_p += branch_offset;
235
236          if (*byte_code_p == CBC_CONTEXT_END)
237          {
238            VM_MINUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_TRY_CONTEXT_STACK_ALLOCATION);
239            vm_stack_top_p -= PARSER_TRY_CONTEXT_STACK_ALLOCATION;
240            continue;
241          }
242        }
243      }
244      else
245      {
246#if !ENABLED (JERRY_ES2015)
247        if (vm_stack_top_p[-1] & VM_CONTEXT_HAS_LEX_ENV)
248        {
249          ecma_object_t *lex_env_p = frame_ctx_p->lex_env_p;
250          JERRY_ASSERT (lex_env_p->u2.outer_reference_cp != JMEM_CP_NULL);
251          frame_ctx_p->lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp);
252          ecma_deref_object (lex_env_p);
253        }
254#endif /* !ENABLED (JERRY_ES2015) */
255
256        if (byte_code_p[0] == CBC_CONTEXT_END)
257        {
258          VM_MINUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_TRY_CONTEXT_STACK_ALLOCATION);
259          vm_stack_top_p -= PARSER_TRY_CONTEXT_STACK_ALLOCATION;
260          continue;
261        }
262      }
263
264      JERRY_ASSERT (byte_code_p[0] == CBC_EXT_OPCODE);
265      JERRY_ASSERT (byte_code_p[1] >= CBC_EXT_FINALLY
266                    && byte_code_p[1] <= CBC_EXT_FINALLY_3);
267
268      branch_offset_length = CBC_BRANCH_OFFSET_LENGTH (byte_code_p[1]);
269      branch_offset = vm_decode_branch_offset (byte_code_p + 2,
270                                               branch_offset_length);
271
272      branch_offset += (uint32_t) (byte_code_p - frame_ctx_p->byte_code_start_p);
273
274      vm_stack_top_p[-1] = VM_CREATE_CONTEXT ((uint32_t) finally_type, branch_offset);
275
276      byte_code_p += 2 + branch_offset_length;
277      frame_ctx_p->byte_code_p = byte_code_p;
278
279      *vm_stack_top_ref_p = vm_stack_top_p;
280      return true;
281    }
282
283    vm_stack_top_p = vm_stack_context_abort (frame_ctx_p, vm_stack_top_p);
284  }
285
286  *vm_stack_top_ref_p = vm_stack_top_p;
287  return false;
288} /* vm_stack_find_finally */
289
290#if ENABLED (JERRY_ES2015)
291
292/**
293 * Get the offsets of ecma values from the specified item of a context.
294 *
295 * @return array of offsets, last item represents the size of the context item
296 */
297uint32_t
298vm_get_context_value_offsets (ecma_value_t *context_item_p) /**< any item of a context */
299{
300  switch (VM_GET_CONTEXT_TYPE (context_item_p[-1]))
301  {
302    case VM_CONTEXT_FINALLY_THROW:
303    case VM_CONTEXT_FINALLY_RETURN:
304    {
305      return (2 << (VM_CONTEXT_OFFSET_SHIFT)) | PARSER_TRY_CONTEXT_STACK_ALLOCATION;
306    }
307    case VM_CONTEXT_FINALLY_JUMP:
308    case VM_CONTEXT_TRY:
309    case VM_CONTEXT_CATCH:
310    {
311      return PARSER_TRY_CONTEXT_STACK_ALLOCATION;
312    }
313#if ENABLED (JERRY_ES2015)
314    case VM_CONTEXT_BLOCK:
315#endif /* ENABLED (JERRY_ES2015) */
316    case VM_CONTEXT_WITH:
317    {
318      return PARSER_WITH_CONTEXT_STACK_ALLOCATION;
319    }
320#if ENABLED (JERRY_ES2015)
321    case VM_CONTEXT_FOR_OF:
322    {
323      return ((3 << (VM_CONTEXT_OFFSET_SHIFT * 2))
324              | (2 << (VM_CONTEXT_OFFSET_SHIFT))
325              | PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION);
326    }
327#endif /* ENABLED (JERRY_ES2015) */
328    default:
329    {
330      return (4 << (VM_CONTEXT_OFFSET_SHIFT)) | PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION;
331    }
332  }
333} /* vm_get_context_value_offsets */
334
335/**
336 * Ref / deref lexical environments in the chain using the current context.
337 */
338void
339vm_ref_lex_env_chain (ecma_object_t *lex_env_p, /**< top of lexical environment */
340                      uint16_t context_depth, /**< depth of function context */
341                      ecma_value_t *context_end_p, /**< end of function context */
342                      bool do_ref) /**< ref or deref lexical environments */
343{
344  ecma_value_t *context_top_p = context_end_p + context_depth;
345  JERRY_ASSERT (context_top_p > context_end_p);
346
347  do
348  {
349    if (context_top_p[-1] & VM_CONTEXT_HAS_LEX_ENV)
350    {
351      JERRY_ASSERT (lex_env_p->u2.outer_reference_cp != JMEM_CP_NULL);
352      ecma_object_t *next_lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp);
353
354      if (do_ref)
355      {
356        ecma_ref_object (lex_env_p);
357      }
358      else
359      {
360        ecma_deref_object (lex_env_p);
361      }
362
363      lex_env_p = next_lex_env_p;
364    }
365
366    uint32_t offsets = vm_get_context_value_offsets (context_top_p);
367
368    while (VM_CONTEXT_HAS_NEXT_OFFSET (offsets))
369    {
370      int32_t offset = VM_CONTEXT_GET_NEXT_OFFSET (offsets);
371
372      if (do_ref)
373      {
374        ecma_ref_if_object (context_top_p[offset]);
375      }
376      else
377      {
378        ecma_deref_if_object (context_top_p[offset]);
379      }
380
381      offsets >>= VM_CONTEXT_OFFSET_SHIFT;
382    }
383
384    JERRY_ASSERT (context_top_p >= context_end_p + offsets);
385    context_top_p -= offsets;
386  }
387  while (context_top_p > context_end_p);
388} /* vm_ref_lex_env_chain */
389
390#endif /* ENABLED (JERRY_ES2015) */
391
392/**
393 * @}
394 * @}
395 */
396