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#include "jext-common.h"
19
20JERRYX_STATIC_ASSERT (JERRYX_SCOPE_PRELIST_SIZE < 32, JERRYX_SCOPE_PRELIST_SIZE_MUST_BE_LESS_THAN_SIZE_OF_UINT8_T);
21
22/**
23 * Opens a new handle scope and attach it to current global scope as a child scope.
24 *
25 * @param result - [out value] opened scope.
26 * @return status code, jerryx_handle_scope_ok if success.
27 */
28jerryx_handle_scope_status
29jerryx_open_handle_scope (jerryx_handle_scope *result)
30{
31 *result = jerryx_handle_scope_alloc ();
32 return jerryx_handle_scope_ok;
33} /** jerryx_open_handle_scope */
34
35/**
36 * Release all jerry values attached to given scope
37 *
38 * @param scope - the scope of handles to be released.
39 */
40void
41jerryx_handle_scope_release_handles (jerryx_handle_scope scope)
42{
43 size_t prelist_handle_count = scope->prelist_handle_count;
44 if (prelist_handle_count == JERRYX_HANDLE_PRELIST_SIZE && scope->handle_ptr != NULL)
45 {
46 jerryx_handle_t *a_handle = scope->handle_ptr;
47 while (a_handle != NULL)
48 {
49 jerry_release_value (a_handle->jval);
50 jerryx_handle_t *sibling = a_handle->sibling;
51 jerry_heap_free (a_handle, sizeof (jerryx_handle_t));
52 a_handle = sibling;
53 }
54 scope->handle_ptr = NULL;
55 prelist_handle_count = JERRYX_HANDLE_PRELIST_SIZE;
56 }
57
58 for (size_t idx = 0; idx < prelist_handle_count; idx++)
59 {
60 jerry_release_value (scope->handle_prelist[idx]);
61 }
62 scope->prelist_handle_count = 0;
63} /** jerryx_handle_scope_release_handles */
64
65/**
66 * Close the scope and its child scopes and release all jerry values that
67 * resides in the scopes.
68 * Scopes must be closed in the reverse order from which they were created.
69 *
70 * @param scope - the scope closed.
71 * @return status code, jerryx_handle_scope_ok if success.
72 */
73jerryx_handle_scope_status
74jerryx_close_handle_scope (jerryx_handle_scope scope)
75{
76 /**
77 * Release all handles related to given scope and its child scopes
78 */
79 jerryx_handle_scope a_scope = scope;
80 do
81 {
82 jerryx_handle_scope_release_handles (a_scope);
83 jerryx_handle_scope child = jerryx_handle_scope_get_child (a_scope);
84 jerryx_handle_scope_free (a_scope);
85 a_scope = child;
86 }
87 while (a_scope != NULL);
88
89 return jerryx_handle_scope_ok;
90} /** jerryx_close_handle_scope */
91
92/**
93 * Opens a new handle scope from which one object can be promoted to the outer scope
94 * and attach it to current global scope as a child scope.
95 *
96 * @param result - [out value] opened escapable handle scope.
97 * @return status code, jerryx_handle_scope_ok if success.
98 */
99jerryx_handle_scope_status
100jerryx_open_escapable_handle_scope (jerryx_handle_scope *result)
101{
102 return jerryx_open_handle_scope (result);
103} /** jerryx_open_escapable_handle_scope */
104
105/**
106 * Close the scope and its child scopes and release all jerry values that
107 * resides in the scopes.
108 * Scopes must be closed in the reverse order from which they were created.
109 *
110 * @param scope - the one to be closed.
111 * @return status code, jerryx_handle_scope_ok if success.
112 */
113jerryx_handle_scope_status
114jerryx_close_escapable_handle_scope (jerryx_handle_scope scope)
115{
116 return jerryx_close_handle_scope (scope);
117} /** jerryx_close_escapable_handle_scope */
118
119/**
120 * Internal helper.
121 * Escape a jerry value from the scope, yet did not promote it to outer scope.
122 * An assertion of if parent exists shall be made before invoking this function.
123 *
124 * @param scope - scope of the handle added to.
125 * @param idx - expected index of the handle in the scope's prelist.
126 * @returns escaped jerry value id
127 */
128jerry_value_t
129jerryx_hand_scope_escape_handle_from_prelist (jerryx_handle_scope scope, size_t idx)
130{
131 jerry_value_t jval = scope->handle_prelist[idx];
132 if (scope->prelist_handle_count == JERRYX_HANDLE_PRELIST_SIZE && scope->handle_ptr != NULL)
133 {
134 jerryx_handle_t *handle = scope->handle_ptr;
135 scope->handle_ptr = handle->sibling;
136 scope->handle_prelist[idx] = handle->jval;
137 jerry_heap_free (handle, sizeof (jerryx_handle_t));
138 return jval;
139 }
140
141 if (idx < JERRYX_HANDLE_PRELIST_SIZE - 1)
142 {
143 scope->handle_prelist[idx] = scope->handle_prelist[scope->prelist_handle_count - 1];
144 }
145 return jval;
146} /** jerryx_hand_scope_escape_handle_from_prelist */
147
148/**
149 * Internal helper.
150 * Escape a jerry value from the given escapable handle scope.
151 *
152 * @param scope - the expected scope to be escaped from.
153 * @param escapee - the jerry value to be escaped.
154 * @param result - [out value] escaped jerry value result.
155 * @param should_promote - true if the escaped value should be added to parent
156 * scope after escaped from the given handle scope.
157 * @return status code, jerryx_handle_scope_ok if success.
158 */
159jerryx_handle_scope_status
160jerryx_escape_handle_internal (jerryx_escapable_handle_scope scope,
161 jerry_value_t escapee,
162 jerry_value_t *result,
163 bool should_promote)
164{
165 if (scope->escaped)
166 {
167 return jerryx_escape_called_twice;
168 }
169
170 jerryx_handle_scope parent = jerryx_handle_scope_get_parent (scope);
171 if (parent == NULL)
172 {
173 return jerryx_handle_scope_mismatch;
174 }
175
176 bool found = false;
177 {
178 size_t found_idx = 0;
179 size_t prelist_count = scope->prelist_handle_count;
180 /**
181 * Search prelist in a reversed order since last added handle
182 * is possible the one to be escaped
183 */
184 for (size_t idx_plus_1 = prelist_count; idx_plus_1 > 0; --idx_plus_1)
185 {
186 if (escapee == scope->handle_prelist[idx_plus_1 - 1])
187 {
188 found = true;
189 found_idx = idx_plus_1 - 1;
190 break;
191 }
192 }
193
194 if (found)
195 {
196 *result = jerryx_hand_scope_escape_handle_from_prelist (scope, found_idx);
197
198 --scope->prelist_handle_count;
199 if (should_promote)
200 {
201 scope->escaped = true;
202 /**
203 * Escape handle to parent scope
204 */
205 jerryx_create_handle_in_scope (*result, jerryx_handle_scope_get_parent (scope));
206 }
207 return jerryx_handle_scope_ok;
208 }
209 };
210
211 if (scope->prelist_handle_count <= JERRYX_HANDLE_PRELIST_SIZE && scope->handle_ptr == NULL)
212 {
213 return jerryx_handle_scope_mismatch;
214 }
215
216 /**
217 * Handle chain list is already in an reversed order,
218 * search through it as it is
219 */
220 jerryx_handle_t *handle = scope->handle_ptr;
221 jerryx_handle_t *memo_handle = NULL;
222 jerryx_handle_t *found_handle = NULL;
223 while (!found)
224 {
225 if (handle == NULL)
226 {
227 return jerryx_handle_scope_mismatch;
228 }
229 if (handle->jval != escapee)
230 {
231 memo_handle = handle;
232 handle = handle->sibling;
233 continue;
234 }
235 /**
236 * Remove found handle from current scope's handle chain
237 */
238 found = true;
239 found_handle = handle;
240 if (memo_handle == NULL)
241 {
242 // found handle is the first handle in heap
243 scope->handle_ptr = found_handle->sibling;
244 }
245 else
246 {
247 memo_handle->sibling = found_handle->sibling;
248 }
249 }
250
251 if (should_promote)
252 {
253 /**
254 * Escape handle to parent scope
255 */
256 *result = jerryx_handle_scope_add_handle_to (found_handle, parent);
257 }
258
259 if (should_promote)
260 {
261 scope->escaped = true;
262 }
263 return jerryx_handle_scope_ok;
264} /** jerryx_escape_handle_internal */
265
266/**
267 * Promotes the handle to the JavaScript object so that it is valid for the lifetime of
268 * the outer scope. It can only be called once per scope. If it is called more than
269 * once an error will be returned.
270 *
271 * @param scope - the expected scope to be escaped from.
272 * @param escapee - the jerry value to be escaped.
273 * @param result - [out value] escaped jerry value result.
274 * @return status code, jerryx_handle_scope_ok if success.
275 */
276jerryx_handle_scope_status
277jerryx_escape_handle (jerryx_escapable_handle_scope scope,
278 jerry_value_t escapee,
279 jerry_value_t *result)
280{
281 return jerryx_escape_handle_internal (scope, escapee, result, true);
282} /** jerryx_escape_handle */
283
284/**
285 * Escape a handle from scope yet do not promote it to the outer scope.
286 * Leave the handle's life time management up to user.
287 *
288 * @param scope - the expected scope to be removed from.
289 * @param escapee - the jerry value to be removed.
290 * @param result - [out value] removed jerry value result.
291 * @return status code, jerryx_handle_scope_ok if success.
292 */
293jerryx_handle_scope_status
294jerryx_remove_handle (jerryx_escapable_handle_scope scope,
295 jerry_value_t escapee,
296 jerry_value_t *result)
297{
298 return jerryx_escape_handle_internal (scope, escapee, result, false);
299} /** jerryx_remove_handle */
300
301/**
302 * Try to reuse given handle if possible while adding to the scope.
303 *
304 * @param handle - the one to be added to the scope.
305 * @param scope - the scope of handle to be added to.
306 * @returns the jerry value id wrapped by given handle.
307 */
308jerry_value_t
309jerryx_handle_scope_add_handle_to (jerryx_handle_t *handle, jerryx_handle_scope scope)
310{
311 size_t prelist_handle_count = scope->prelist_handle_count;
312 if (prelist_handle_count < JERRYX_HANDLE_PRELIST_SIZE)
313 {
314 ++scope->prelist_handle_count;
315 jerry_value_t jval = handle->jval;
316 jerry_heap_free (handle, sizeof (jerryx_handle_t));
317 scope->handle_prelist[prelist_handle_count] = jval;
318 return jval;
319 }
320
321 handle->sibling = scope->handle_ptr;
322 scope->handle_ptr = handle;
323 return handle->jval;
324} /** jerryx_handle_scope_add_handle_to */
325
326/**
327 * Add given jerry value to the scope.
328 *
329 * @param jval - jerry value to be added to scope.
330 * @param scope - the scope of the jerry value been expected to be added to.
331 * @return jerry value that added to scope.
332 */
333jerry_value_t
334jerryx_create_handle_in_scope (jerry_value_t jval, jerryx_handle_scope scope)
335{
336 size_t prelist_handle_count = scope->prelist_handle_count;
337 if (prelist_handle_count < JERRYX_HANDLE_PRELIST_SIZE)
338 {
339 scope->handle_prelist[prelist_handle_count] = jval;
340
341 ++scope->prelist_handle_count;
342 return jval;
343 }
344 jerryx_handle_t *handle = (jerryx_handle_t *) jerry_heap_alloc (sizeof (jerryx_handle_t));
345 JERRYX_ASSERT (handle != NULL);
346 handle->jval = jval;
347
348 handle->sibling = scope->handle_ptr;
349 scope->handle_ptr = handle;
350
351 return jval;
352} /** jerryx_create_handle_in_scope */
353
354/**
355 * Add given jerry value to current top scope.
356 *
357 * @param jval - jerry value to be added to scope.
358 * @return jerry value that added to scope.
359 */
360jerry_value_t
361jerryx_create_handle (jerry_value_t jval)
362{
363 return jerryx_create_handle_in_scope (jval, jerryx_handle_scope_get_current ());
364} /** jerryx_create_handle */
365