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 <stdlib.h>
17#include "handle-scope-internal.h"
18
19static jerryx_handle_scope_t jerryx_handle_scope_root =
20{
21  .prelist_handle_count = 0,
22  .handle_ptr = NULL,
23};
24static jerryx_handle_scope_t *jerryx_handle_scope_current = &jerryx_handle_scope_root;
25static jerryx_handle_scope_pool_t jerryx_handle_scope_pool =
26{
27  .count = 0,
28  .start = NULL,
29};
30
31#define JERRYX_HANDLE_SCOPE_POOL_PRELIST_LAST \
32  jerryx_handle_scope_pool.prelist + JERRYX_SCOPE_PRELIST_SIZE - 1
33
34#define JERRYX_HANDLE_SCOPE_PRELIST_IDX(scope) (scope - jerryx_handle_scope_pool.prelist)
35
36/**
37 * Get current handle scope top of stack.
38 */
39jerryx_handle_scope_t *
40jerryx_handle_scope_get_current (void)
41{
42  return jerryx_handle_scope_current;
43} /* jerryx_handle_scope_get_current */
44
45/**
46 * Get root handle scope.
47 */
48jerryx_handle_scope_t *
49jerryx_handle_scope_get_root (void)
50{
51  return &jerryx_handle_scope_root;
52} /* jerryx_handle_scope_get_root */
53
54/**
55 * Determines if given handle scope is located in pre-allocated list.
56 *
57 * @param scope - the one to be determined.
58 */
59static bool
60jerryx_handle_scope_is_in_prelist (jerryx_handle_scope_t *scope)
61{
62  return (jerryx_handle_scope_pool.prelist <= scope)
63  && (scope <= (jerryx_handle_scope_pool.prelist + JERRYX_SCOPE_PRELIST_SIZE - 1));
64} /** jerryx_handle_scope_is_in_prelist */
65
66/**
67 * Get the parent of given handle scope.
68 * If given handle scope is in prelist, the parent must be in prelist too;
69 * if given is the first item of heap chain list, the parent must be the last one of prelist;
70 * the parent must be in chain list otherwise.
71 *
72 * @param scope - the one to be permformed on.
73 * @returns - the parent of the given scope.
74 */
75jerryx_handle_scope_t *
76jerryx_handle_scope_get_parent (jerryx_handle_scope_t *scope)
77{
78  if (scope == &jerryx_handle_scope_root)
79  {
80    return NULL;
81  }
82  if (!jerryx_handle_scope_is_in_prelist (scope))
83  {
84    jerryx_handle_scope_dynamic_t *dy_scope = (jerryx_handle_scope_dynamic_t *) scope;
85    if (dy_scope == jerryx_handle_scope_pool.start)
86    {
87      return JERRYX_HANDLE_SCOPE_POOL_PRELIST_LAST;
88    }
89    jerryx_handle_scope_dynamic_t *parent = dy_scope->parent;
90    return (jerryx_handle_scope_t *) parent;
91  }
92  if (scope == jerryx_handle_scope_pool.prelist)
93  {
94    return &jerryx_handle_scope_root;
95  }
96  return jerryx_handle_scope_pool.prelist + JERRYX_HANDLE_SCOPE_PRELIST_IDX (scope) - 1;
97} /** jerryx_handle_scope_get_parent */
98
99/**
100 * Get the child of given handle scope.
101 * If the given handle scope is in heap chain list, its child must be in heap chain list too;
102 * if the given handle scope is the last one of prelist, its child must be the first item of chain list;
103 * the children are in prelist otherwise.
104 *
105 * @param scope - the one to be permformed on.
106 * @returns the child of the given scope.
107 */
108jerryx_handle_scope_t *
109jerryx_handle_scope_get_child (jerryx_handle_scope_t *scope)
110{
111  if (scope == &jerryx_handle_scope_root)
112  {
113    if (jerryx_handle_scope_pool.count > 0)
114    {
115      return jerryx_handle_scope_pool.prelist;
116    }
117    return NULL;
118  }
119  if (!jerryx_handle_scope_is_in_prelist (scope))
120  {
121    jerryx_handle_scope_dynamic_t *child = ((jerryx_handle_scope_dynamic_t *) scope)->child;
122    return (jerryx_handle_scope_t *) child;
123  }
124  if (scope == JERRYX_HANDLE_SCOPE_POOL_PRELIST_LAST)
125  {
126    return (jerryx_handle_scope_t *) jerryx_handle_scope_pool.start;
127  }
128  long idx = (long)(JERRYX_HANDLE_SCOPE_PRELIST_IDX (scope));
129  if (idx < 0)
130  {
131    return NULL;
132  }
133  if ((unsigned long) idx >= jerryx_handle_scope_pool.count - 1)
134  {
135    return NULL;
136  }
137  return jerryx_handle_scope_pool.prelist + idx + 1;
138} /** jerryx_handle_scope_get_child */
139
140/**
141 * Claims a handle scope either from prelist or allocating a new memory block,
142 * and increment pool's scope count by 1, and set current scope to the newly claimed one.
143 * If there are still available spaces in prelist, claims a block in prelist;
144 * otherwise allocates a new memory block from heap and sets its fields to default values,
145 * and link it to previously dynamically allocated scope, or link it to pool's start pointer.
146 *
147 * @returns the newly claimed handle scope pointer.
148 */
149jerryx_handle_scope_t *
150jerryx_handle_scope_alloc (void)
151{
152  jerryx_handle_scope_t *scope;
153  if (jerryx_handle_scope_pool.count < JERRYX_SCOPE_PRELIST_SIZE)
154  {
155    scope = jerryx_handle_scope_pool.prelist + jerryx_handle_scope_pool.count;
156  }
157  else
158  {
159    jerryx_handle_scope_dynamic_t *dy_scope = malloc (sizeof (jerryx_handle_scope_dynamic_t));
160    JERRYX_HANDLE_SCOPE_ASSERT (dy_scope != NULL);
161    dy_scope->child = NULL;
162
163    if (jerryx_handle_scope_pool.count != JERRYX_SCOPE_PRELIST_SIZE)
164    {
165      jerryx_handle_scope_dynamic_t *dy_current = (jerryx_handle_scope_dynamic_t *) jerryx_handle_scope_current;
166      dy_scope->parent = dy_current;
167      dy_current->child = dy_scope;
168    }
169    else
170    {
171      jerryx_handle_scope_pool.start = dy_scope;
172      dy_scope->parent = NULL;
173    }
174
175    scope = (jerryx_handle_scope_t *) dy_scope;
176  }
177
178  scope->prelist_handle_count = 0;
179  scope->escaped = false;
180  scope->handle_ptr = NULL;
181
182  jerryx_handle_scope_current = scope;
183  ++jerryx_handle_scope_pool.count;
184  return (jerryx_handle_scope_t *) scope;
185} /** jerryx_handle_scope_alloc */
186
187/**
188 * Deannounce a previously claimed handle scope, return it to pool
189 * or free the allocated memory block.
190 *
191 * @param scope - the one to be freed.
192 */
193void
194jerryx_handle_scope_free (jerryx_handle_scope_t *scope)
195{
196  if (scope == &jerryx_handle_scope_root)
197  {
198    return;
199  }
200
201  --jerryx_handle_scope_pool.count;
202  if (scope == jerryx_handle_scope_current)
203  {
204    jerryx_handle_scope_current = jerryx_handle_scope_get_parent (scope);
205  }
206
207  if (!jerryx_handle_scope_is_in_prelist (scope))
208  {
209    jerryx_handle_scope_dynamic_t *dy_scope = (jerryx_handle_scope_dynamic_t *) scope;
210    if (dy_scope == jerryx_handle_scope_pool.start)
211    {
212      jerryx_handle_scope_pool.start = dy_scope->child;
213    }
214    else if (dy_scope->parent != NULL)
215    {
216      dy_scope->parent->child = dy_scope->child;
217    }
218    free (dy_scope);
219    return;
220  }
221  /**
222   * Nothing to do with scopes in prelist
223   */
224} /** jerryx_handle_scope_free */
225