forked from xuos/xiuos
365 lines
11 KiB
C
365 lines
11 KiB
C
/* Copyright JS Foundation and other contributors, http://js.foundation
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include "handle-scope-internal.h"
|
|
#include "jext-common.h"
|
|
|
|
JERRYX_STATIC_ASSERT (JERRYX_SCOPE_PRELIST_SIZE < 32, JERRYX_SCOPE_PRELIST_SIZE_MUST_BE_LESS_THAN_SIZE_OF_UINT8_T);
|
|
|
|
/**
|
|
* Opens a new handle scope and attach it to current global scope as a child scope.
|
|
*
|
|
* @param result - [out value] opened scope.
|
|
* @return status code, jerryx_handle_scope_ok if success.
|
|
*/
|
|
jerryx_handle_scope_status
|
|
jerryx_open_handle_scope (jerryx_handle_scope *result)
|
|
{
|
|
*result = jerryx_handle_scope_alloc ();
|
|
return jerryx_handle_scope_ok;
|
|
} /** jerryx_open_handle_scope */
|
|
|
|
/**
|
|
* Release all jerry values attached to given scope
|
|
*
|
|
* @param scope - the scope of handles to be released.
|
|
*/
|
|
void
|
|
jerryx_handle_scope_release_handles (jerryx_handle_scope scope)
|
|
{
|
|
size_t prelist_handle_count = scope->prelist_handle_count;
|
|
if (prelist_handle_count == JERRYX_HANDLE_PRELIST_SIZE && scope->handle_ptr != NULL)
|
|
{
|
|
jerryx_handle_t *a_handle = scope->handle_ptr;
|
|
while (a_handle != NULL)
|
|
{
|
|
jerry_release_value (a_handle->jval);
|
|
jerryx_handle_t *sibling = a_handle->sibling;
|
|
jerry_heap_free (a_handle, sizeof (jerryx_handle_t));
|
|
a_handle = sibling;
|
|
}
|
|
scope->handle_ptr = NULL;
|
|
prelist_handle_count = JERRYX_HANDLE_PRELIST_SIZE;
|
|
}
|
|
|
|
for (size_t idx = 0; idx < prelist_handle_count; idx++)
|
|
{
|
|
jerry_release_value (scope->handle_prelist[idx]);
|
|
}
|
|
scope->prelist_handle_count = 0;
|
|
} /** jerryx_handle_scope_release_handles */
|
|
|
|
/**
|
|
* Close the scope and its child scopes and release all jerry values that
|
|
* resides in the scopes.
|
|
* Scopes must be closed in the reverse order from which they were created.
|
|
*
|
|
* @param scope - the scope closed.
|
|
* @return status code, jerryx_handle_scope_ok if success.
|
|
*/
|
|
jerryx_handle_scope_status
|
|
jerryx_close_handle_scope (jerryx_handle_scope scope)
|
|
{
|
|
/**
|
|
* Release all handles related to given scope and its child scopes
|
|
*/
|
|
jerryx_handle_scope a_scope = scope;
|
|
do
|
|
{
|
|
jerryx_handle_scope_release_handles (a_scope);
|
|
jerryx_handle_scope child = jerryx_handle_scope_get_child (a_scope);
|
|
jerryx_handle_scope_free (a_scope);
|
|
a_scope = child;
|
|
}
|
|
while (a_scope != NULL);
|
|
|
|
return jerryx_handle_scope_ok;
|
|
} /** jerryx_close_handle_scope */
|
|
|
|
/**
|
|
* Opens a new handle scope from which one object can be promoted to the outer scope
|
|
* and attach it to current global scope as a child scope.
|
|
*
|
|
* @param result - [out value] opened escapable handle scope.
|
|
* @return status code, jerryx_handle_scope_ok if success.
|
|
*/
|
|
jerryx_handle_scope_status
|
|
jerryx_open_escapable_handle_scope (jerryx_handle_scope *result)
|
|
{
|
|
return jerryx_open_handle_scope (result);
|
|
} /** jerryx_open_escapable_handle_scope */
|
|
|
|
/**
|
|
* Close the scope and its child scopes and release all jerry values that
|
|
* resides in the scopes.
|
|
* Scopes must be closed in the reverse order from which they were created.
|
|
*
|
|
* @param scope - the one to be closed.
|
|
* @return status code, jerryx_handle_scope_ok if success.
|
|
*/
|
|
jerryx_handle_scope_status
|
|
jerryx_close_escapable_handle_scope (jerryx_handle_scope scope)
|
|
{
|
|
return jerryx_close_handle_scope (scope);
|
|
} /** jerryx_close_escapable_handle_scope */
|
|
|
|
/**
|
|
* Internal helper.
|
|
* Escape a jerry value from the scope, yet did not promote it to outer scope.
|
|
* An assertion of if parent exists shall be made before invoking this function.
|
|
*
|
|
* @param scope - scope of the handle added to.
|
|
* @param idx - expected index of the handle in the scope's prelist.
|
|
* @returns escaped jerry value id
|
|
*/
|
|
jerry_value_t
|
|
jerryx_hand_scope_escape_handle_from_prelist (jerryx_handle_scope scope, size_t idx)
|
|
{
|
|
jerry_value_t jval = scope->handle_prelist[idx];
|
|
if (scope->prelist_handle_count == JERRYX_HANDLE_PRELIST_SIZE && scope->handle_ptr != NULL)
|
|
{
|
|
jerryx_handle_t *handle = scope->handle_ptr;
|
|
scope->handle_ptr = handle->sibling;
|
|
scope->handle_prelist[idx] = handle->jval;
|
|
jerry_heap_free (handle, sizeof (jerryx_handle_t));
|
|
return jval;
|
|
}
|
|
|
|
if (idx < JERRYX_HANDLE_PRELIST_SIZE - 1)
|
|
{
|
|
scope->handle_prelist[idx] = scope->handle_prelist[scope->prelist_handle_count - 1];
|
|
}
|
|
return jval;
|
|
} /** jerryx_hand_scope_escape_handle_from_prelist */
|
|
|
|
/**
|
|
* Internal helper.
|
|
* Escape a jerry value from the given escapable handle scope.
|
|
*
|
|
* @param scope - the expected scope to be escaped from.
|
|
* @param escapee - the jerry value to be escaped.
|
|
* @param result - [out value] escaped jerry value result.
|
|
* @param should_promote - true if the escaped value should be added to parent
|
|
* scope after escaped from the given handle scope.
|
|
* @return status code, jerryx_handle_scope_ok if success.
|
|
*/
|
|
jerryx_handle_scope_status
|
|
jerryx_escape_handle_internal (jerryx_escapable_handle_scope scope,
|
|
jerry_value_t escapee,
|
|
jerry_value_t *result,
|
|
bool should_promote)
|
|
{
|
|
if (scope->escaped)
|
|
{
|
|
return jerryx_escape_called_twice;
|
|
}
|
|
|
|
jerryx_handle_scope parent = jerryx_handle_scope_get_parent (scope);
|
|
if (parent == NULL)
|
|
{
|
|
return jerryx_handle_scope_mismatch;
|
|
}
|
|
|
|
bool found = false;
|
|
{
|
|
size_t found_idx = 0;
|
|
size_t prelist_count = scope->prelist_handle_count;
|
|
/**
|
|
* Search prelist in a reversed order since last added handle
|
|
* is possible the one to be escaped
|
|
*/
|
|
for (size_t idx_plus_1 = prelist_count; idx_plus_1 > 0; --idx_plus_1)
|
|
{
|
|
if (escapee == scope->handle_prelist[idx_plus_1 - 1])
|
|
{
|
|
found = true;
|
|
found_idx = idx_plus_1 - 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (found)
|
|
{
|
|
*result = jerryx_hand_scope_escape_handle_from_prelist (scope, found_idx);
|
|
|
|
--scope->prelist_handle_count;
|
|
if (should_promote)
|
|
{
|
|
scope->escaped = true;
|
|
/**
|
|
* Escape handle to parent scope
|
|
*/
|
|
jerryx_create_handle_in_scope (*result, jerryx_handle_scope_get_parent (scope));
|
|
}
|
|
return jerryx_handle_scope_ok;
|
|
}
|
|
};
|
|
|
|
if (scope->prelist_handle_count <= JERRYX_HANDLE_PRELIST_SIZE && scope->handle_ptr == NULL)
|
|
{
|
|
return jerryx_handle_scope_mismatch;
|
|
}
|
|
|
|
/**
|
|
* Handle chain list is already in an reversed order,
|
|
* search through it as it is
|
|
*/
|
|
jerryx_handle_t *handle = scope->handle_ptr;
|
|
jerryx_handle_t *memo_handle = NULL;
|
|
jerryx_handle_t *found_handle = NULL;
|
|
while (!found)
|
|
{
|
|
if (handle == NULL)
|
|
{
|
|
return jerryx_handle_scope_mismatch;
|
|
}
|
|
if (handle->jval != escapee)
|
|
{
|
|
memo_handle = handle;
|
|
handle = handle->sibling;
|
|
continue;
|
|
}
|
|
/**
|
|
* Remove found handle from current scope's handle chain
|
|
*/
|
|
found = true;
|
|
found_handle = handle;
|
|
if (memo_handle == NULL)
|
|
{
|
|
// found handle is the first handle in heap
|
|
scope->handle_ptr = found_handle->sibling;
|
|
}
|
|
else
|
|
{
|
|
memo_handle->sibling = found_handle->sibling;
|
|
}
|
|
}
|
|
|
|
if (should_promote)
|
|
{
|
|
/**
|
|
* Escape handle to parent scope
|
|
*/
|
|
*result = jerryx_handle_scope_add_handle_to (found_handle, parent);
|
|
}
|
|
|
|
if (should_promote)
|
|
{
|
|
scope->escaped = true;
|
|
}
|
|
return jerryx_handle_scope_ok;
|
|
} /** jerryx_escape_handle_internal */
|
|
|
|
/**
|
|
* Promotes the handle to the JavaScript object so that it is valid for the lifetime of
|
|
* the outer scope. It can only be called once per scope. If it is called more than
|
|
* once an error will be returned.
|
|
*
|
|
* @param scope - the expected scope to be escaped from.
|
|
* @param escapee - the jerry value to be escaped.
|
|
* @param result - [out value] escaped jerry value result.
|
|
* @return status code, jerryx_handle_scope_ok if success.
|
|
*/
|
|
jerryx_handle_scope_status
|
|
jerryx_escape_handle (jerryx_escapable_handle_scope scope,
|
|
jerry_value_t escapee,
|
|
jerry_value_t *result)
|
|
{
|
|
return jerryx_escape_handle_internal (scope, escapee, result, true);
|
|
} /** jerryx_escape_handle */
|
|
|
|
/**
|
|
* Escape a handle from scope yet do not promote it to the outer scope.
|
|
* Leave the handle's life time management up to user.
|
|
*
|
|
* @param scope - the expected scope to be removed from.
|
|
* @param escapee - the jerry value to be removed.
|
|
* @param result - [out value] removed jerry value result.
|
|
* @return status code, jerryx_handle_scope_ok if success.
|
|
*/
|
|
jerryx_handle_scope_status
|
|
jerryx_remove_handle (jerryx_escapable_handle_scope scope,
|
|
jerry_value_t escapee,
|
|
jerry_value_t *result)
|
|
{
|
|
return jerryx_escape_handle_internal (scope, escapee, result, false);
|
|
} /** jerryx_remove_handle */
|
|
|
|
/**
|
|
* Try to reuse given handle if possible while adding to the scope.
|
|
*
|
|
* @param handle - the one to be added to the scope.
|
|
* @param scope - the scope of handle to be added to.
|
|
* @returns the jerry value id wrapped by given handle.
|
|
*/
|
|
jerry_value_t
|
|
jerryx_handle_scope_add_handle_to (jerryx_handle_t *handle, jerryx_handle_scope scope)
|
|
{
|
|
size_t prelist_handle_count = scope->prelist_handle_count;
|
|
if (prelist_handle_count < JERRYX_HANDLE_PRELIST_SIZE)
|
|
{
|
|
++scope->prelist_handle_count;
|
|
jerry_value_t jval = handle->jval;
|
|
jerry_heap_free (handle, sizeof (jerryx_handle_t));
|
|
scope->handle_prelist[prelist_handle_count] = jval;
|
|
return jval;
|
|
}
|
|
|
|
handle->sibling = scope->handle_ptr;
|
|
scope->handle_ptr = handle;
|
|
return handle->jval;
|
|
} /** jerryx_handle_scope_add_handle_to */
|
|
|
|
/**
|
|
* Add given jerry value to the scope.
|
|
*
|
|
* @param jval - jerry value to be added to scope.
|
|
* @param scope - the scope of the jerry value been expected to be added to.
|
|
* @return jerry value that added to scope.
|
|
*/
|
|
jerry_value_t
|
|
jerryx_create_handle_in_scope (jerry_value_t jval, jerryx_handle_scope scope)
|
|
{
|
|
size_t prelist_handle_count = scope->prelist_handle_count;
|
|
if (prelist_handle_count < JERRYX_HANDLE_PRELIST_SIZE)
|
|
{
|
|
scope->handle_prelist[prelist_handle_count] = jval;
|
|
|
|
++scope->prelist_handle_count;
|
|
return jval;
|
|
}
|
|
jerryx_handle_t *handle = (jerryx_handle_t *) jerry_heap_alloc (sizeof (jerryx_handle_t));
|
|
JERRYX_ASSERT (handle != NULL);
|
|
handle->jval = jval;
|
|
|
|
handle->sibling = scope->handle_ptr;
|
|
scope->handle_ptr = handle;
|
|
|
|
return jval;
|
|
} /** jerryx_create_handle_in_scope */
|
|
|
|
/**
|
|
* Add given jerry value to current top scope.
|
|
*
|
|
* @param jval - jerry value to be added to scope.
|
|
* @return jerry value that added to scope.
|
|
*/
|
|
jerry_value_t
|
|
jerryx_create_handle (jerry_value_t jval)
|
|
{
|
|
return jerryx_create_handle_in_scope (jval, jerryx_handle_scope_get_current ());
|
|
} /** jerryx_create_handle */
|