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 */ 25jerryx_handle_scope_status 26jerryx_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 */ 37void 38jerryx_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 */ 70jerryx_handle_scope_status 71jerryx_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 */ 96jerryx_handle_scope_status 97jerryx_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 */ 110jerryx_handle_scope_status 111jerryx_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 */ 125jerry_value_t 126jerryx_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 */ 156jerryx_handle_scope_status 157jerryx_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 */ 273jerryx_handle_scope_status 274jerryx_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 */ 290jerryx_handle_scope_status 291jerryx_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 */ 305jerry_value_t 306jerryx_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 */ 330jerry_value_t 331jerryx_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 */ 357jerry_value_t 358jerryx_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