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 | |
20 | JERRYX_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 | */ |
28 | jerryx_handle_scope_status |
29 | jerryx_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 | */ |
40 | void |
41 | jerryx_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 | */ |
73 | jerryx_handle_scope_status |
74 | jerryx_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 | */ |
99 | jerryx_handle_scope_status |
100 | jerryx_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 | */ |
113 | jerryx_handle_scope_status |
114 | jerryx_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 | */ |
128 | jerry_value_t |
129 | jerryx_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 | */ |
159 | jerryx_handle_scope_status |
160 | jerryx_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 | */ |
276 | jerryx_handle_scope_status |
277 | jerryx_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 | */ |
293 | jerryx_handle_scope_status |
294 | jerryx_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 | */ |
308 | jerry_value_t |
309 | jerryx_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 | */ |
333 | jerry_value_t |
334 | jerryx_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 | */ |
360 | jerry_value_t |
361 | jerryx_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 | |