Skip to content

Added the _restore function to "unpop" the argument stack. #2592

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion docs/09.EXT-REFERENCE-ARG.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ typedef struct

**Summary**

The structure is used in `jerryx_arg_array`. It provides the array items' corresponding
The structure is used in `jerryx_arg_array`. It provides the array items' corresponding
JS-to-C mappings and count.

**Prototype**
Expand Down Expand Up @@ -813,6 +813,32 @@ jerryx_arg_js_iterator_peek (jerryx_arg_js_iterator_t *js_arg_iter_p)
- return value - the current `jerry_value_t` argument.
- `js_arg_iter_p` - the JS arg iterator from which to peek.

## jerryx_arg_js_iterator_restore

**Summary**

Restore the last item popped from the stack. This can be called as
many times as there are arguments on the stack -- if called when the
first element in the array is the current top of the stack, this
function does nothing.

*Note:* This function relies on the underlying implementation of the
arg stack as an array, as its function is to simply back up the "top
of stack" pointer to point to the previous element of the array.

*Note:* Like `jerryx_arg_js_iterator_pop ()`, this function will
change the `js_arg_idx` and `js_arg_p` values in the iterator.

**Prototype**

```c
jerry_value_t
jerryx_arg_js_iterator_restore (jerryx_arg_js_iterator_t *js_arg_iter_p)
```
- return value - the the new top of the stack.
- `js_arg_iter_p` - the JS arg iterator to restore.


## jerryx_arg_js_iterator_index

**Summary**
Expand Down
20 changes: 20 additions & 0 deletions jerry-ext/arg/arg-js-iterator-helper.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,26 @@ jerryx_arg_js_iterator_pop (jerryx_arg_js_iterator_t *js_arg_iter_p) /**< the JS
: jerry_create_undefined ());
} /* jerryx_arg_js_iterator_pop */

/**
* Restore the previous JS argument from the iterator.
* It will change the index and js_arg_p value in the iterator.
*
* @return the restored (now current) JS argument.
*/
jerry_value_t
jerryx_arg_js_iterator_restore (jerryx_arg_js_iterator_t *js_arg_iter_p) /**< the JS arg iterator */
{
if (js_arg_iter_p->js_arg_idx == 0)
{
return jerry_create_undefined ();
}

--js_arg_iter_p->js_arg_idx;
--js_arg_iter_p->js_arg_p;

return *js_arg_iter_p->js_arg_p;
} /* jerryx_arg_js_iterator_restore */

/**
* Get the current JS argument from the iterator.
*
Expand Down
1 change: 1 addition & 0 deletions jerry-ext/include/jerryscript-ext/arg.h
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ jerryx_arg_transform_optional (jerryx_arg_js_iterator_t *js_arg_iter_p,

/* Helper functions for transform functions. */
jerry_value_t jerryx_arg_js_iterator_pop (jerryx_arg_js_iterator_t *js_arg_iter_p);
jerry_value_t jerryx_arg_js_iterator_restore (jerryx_arg_js_iterator_t *js_arg_iter_p);
jerry_value_t jerryx_arg_js_iterator_peek (jerryx_arg_js_iterator_t *js_arg_iter_p);
jerry_length_t jerryx_arg_js_iterator_index (jerryx_arg_js_iterator_t *js_arg_iter_p);

Expand Down
163 changes: 163 additions & 0 deletions tests/unit-ext/test-ext-arg.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ static const jerry_char_t test_source[] = TEST_STRING_LITERAL (
"test_validator_array1(arr);"
"test_validator_array1();"
"test_validator_array2(arr);"
"test_validator_restore(false, 3.0);"
"test_validator_restore(3.0, false);"
);

static const jerry_object_native_info_t thing_a_info =
Expand Down Expand Up @@ -87,6 +89,7 @@ static int validator2_count = 0;
static int validator_int_count = 0;
static int validator_prop_count = 0;
static int validator_array_count = 0;
static int validator_restore_count = 0;

/**
* The handler should have following arguments:
Expand Down Expand Up @@ -561,6 +564,164 @@ test_validator_array2_handler (const jerry_value_t func_obj_val __attribute__((u
return jerry_create_undefined ();
} /* test_validator_array2_handler */

/**
* This validator is designed to test the
* jerryx_arg_js_iterator_restore function. We'll introduce a union
* type to hold a bool or double and a transform function that will
* look for this type. Then, we'll call the handler with two
* parameters, one bool and one double and see if we correctly build
* the union types for each parameter. To check that the code protects
* against backing up too far, when the check for the double fails,
* we'll "restore" the stack three times; this shouldn't break
* anything.
*/
/*
* This enumeration type specifies the kind of thing held in the union.
*/
typedef enum
{
DOUBLE_VALUE,
BOOL_VALUE
} union_type_t;

/*
* This struct holds either a boolean or double in a union and has a
* second field that describes the type held in the union.
*/
typedef struct
{
union_type_t type_of_value;
union
{
double double_field;
bool bool_field;
} value;
} double_or_bool_t;

/**
* This creates a jerryx_arg_t that can be used like any
* of the installed functions, like jerryx_arg_bool().
*/
#define jerryx_arg_double_or_bool_t(value_ptr, coerce_or_not, optional_or_not, last_parameter) \
jerryx_arg_custom (value_ptr, \
(uintptr_t)&((uintptr_t []){(uintptr_t)coerce_or_not, \
(uintptr_t)optional_or_not, \
(uintptr_t)last_parameter}), \
jerry_arg_to_double_or_bool_t)
/*
* This function is the argument validator used in the above macro called
* jerryx_arg_double_or_bool. It calls jerryx_arg_js_iterator_restore()
* more times than it should to ensure that calling that function too
* often doesn't cause an error.
*/
static jerry_value_t
jerry_arg_to_double_or_bool_t (jerryx_arg_js_iterator_t *js_arg_iter_p,
const jerryx_arg_t *c_arg_p)
{
/* c_arg_p has two fields: dest, which is a pointer to the data that
* gets filled in, and extra_info, which contains the flags used to
* control coercion and optional-ness, respectively. For this test,
* we added an extra flag that tells us that we're working on the
* last parameter; when we know it's the last parameter, we'll "restore"
* the stack more times than there are actual stack values to ensure
* that the restore function doesn't produce an error. */
double_or_bool_t *destination = c_arg_p->dest;
uintptr_t *extra_info = (uintptr_t *)(c_arg_p->extra_info);
jerryx_arg_t conversion_function;
jerry_value_t conversion_result;
jerry_value_t restore_result;
bool last_parameter = (extra_info[2] == 1);

validator_restore_count++;

conversion_function = jerryx_arg_number ((double *) (&(destination->value.double_field)),
(jerryx_arg_coerce_t) extra_info[0],
JERRYX_ARG_OPTIONAL);
conversion_result = conversion_function.func (js_arg_iter_p, &conversion_function);
if (!jerry_value_is_error (conversion_result))
{
if (last_parameter)
{
/* The stack is only two parameters high, but we want to ensure that
* excessive calls will not result in aberrant behavior... */
jerryx_arg_js_iterator_restore (js_arg_iter_p);
jerryx_arg_js_iterator_restore (js_arg_iter_p);
jerryx_arg_js_iterator_restore (js_arg_iter_p);
restore_result = jerryx_arg_js_iterator_restore (js_arg_iter_p);
TEST_ASSERT (jerry_value_is_undefined (restore_result));
}

destination->type_of_value = DOUBLE_VALUE;
return conversion_result;
}

jerryx_arg_js_iterator_restore (js_arg_iter_p);

conversion_function = jerryx_arg_boolean ((bool *) (&(destination->value.bool_field)),
(jerryx_arg_coerce_t) extra_info[0],
(jerryx_arg_optional_t) extra_info[1]);

jerry_release_value (conversion_result);
conversion_result = conversion_function.func (js_arg_iter_p, &conversion_function);
if (!jerry_value_is_error (conversion_result))
{
if (last_parameter)
{
/* The stack is only two parameters high, but we want to ensure that
* excessive calls will not result in aberrant behavior... */
jerryx_arg_js_iterator_restore (js_arg_iter_p);
jerryx_arg_js_iterator_restore (js_arg_iter_p);
jerryx_arg_js_iterator_restore (js_arg_iter_p);
restore_result = jerryx_arg_js_iterator_restore (js_arg_iter_p);
TEST_ASSERT (jerry_value_is_undefined (restore_result));
}

destination->type_of_value = BOOL_VALUE;
return conversion_result;
}

/* Fall through indicates that whatever they gave us, it wasn't
* one of the types we were expecting... */
jerry_release_value (conversion_result);
return jerry_create_error (JERRY_ERROR_TYPE,
(const jerry_char_t *) "double_or_bool-type error.");
} /* jerry_arg_to_double_or_bool_t */

/**
* This validator expects two parameters, one a bool and one a double -- the
* order doesn't matter (so we'll call it twice with the orders reversed).
*/
static jerry_value_t
test_validator_restore_handler (const jerry_value_t func_obj_val __attribute__((unused)), /**< function object */
const jerry_value_t this_val __attribute__((unused)), /**< this value */
const jerry_value_t args_p[], /**< arguments list */
const jerry_length_t args_cnt __attribute__((unused))) /**< arguments length */
{
double_or_bool_t arg1;
double_or_bool_t arg2;

jerryx_arg_t item_mapping[] =
{
jerryx_arg_double_or_bool_t (&arg1, JERRYX_ARG_NO_COERCE, JERRYX_ARG_REQUIRED, 0),
jerryx_arg_double_or_bool_t (&arg2, JERRYX_ARG_NO_COERCE, JERRYX_ARG_REQUIRED, 1)
};

jerry_value_t is_ok = jerryx_arg_transform_args (args_p, args_cnt, item_mapping, ARRAY_SIZE (item_mapping));

TEST_ASSERT (!jerry_value_is_error (is_ok));

/* We are going to call this with [false, 3.0] and [3.0, false] parameters... */
bool arg1_is_false = (arg1.type_of_value == BOOL_VALUE && arg1.value.bool_field == false);
bool arg1_is_three = (arg1.type_of_value == DOUBLE_VALUE && arg1.value.double_field == 3.0);
bool arg2_is_false = (arg2.type_of_value == BOOL_VALUE && arg2.value.bool_field == false);
bool arg2_is_three = (arg2.type_of_value == DOUBLE_VALUE && arg2.value.double_field == 3.0);
TEST_ASSERT ((arg1_is_false && arg2_is_three) || (arg1_is_three && arg2_is_false));

jerry_release_value (is_ok);

return jerry_create_undefined ();
} /* test_validator_restore_handler */

static void
test_utf8_string (void)
{
Expand Down Expand Up @@ -657,6 +818,7 @@ main (void)
register_js_function ("test_validator_prop3", test_validator_prop3_handler);
register_js_function ("test_validator_array1", test_validator_array1_handler);
register_js_function ("test_validator_array2", test_validator_array2_handler);
register_js_function ("test_validator_restore", test_validator_restore_handler);

jerry_value_t parsed_code_val = jerry_parse (NULL,
0,
Expand All @@ -672,6 +834,7 @@ main (void)
TEST_ASSERT (validator_prop_count == 4);
TEST_ASSERT (validator_int_count == 3);
TEST_ASSERT (validator_array_count == 3);
TEST_ASSERT (validator_restore_count == 4);

jerry_release_value (res);
jerry_release_value (parsed_code_val);
Expand Down