• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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