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