Skip to content

Commit cfdb5ed

Browse files
t-harveyzherczeg
authored andcommitted
Added the _restore function to "unpop" the argument stack. (#2592)
Added a unit test for the jerryx_arg_js_iterator_restore function. Added the specification to the documentation. JerryScript-DCO-1.0-Signed-off-by: Timothy Harvey [email protected]
1 parent 7e3d688 commit cfdb5ed

File tree

4 files changed

+211
-1
lines changed

4 files changed

+211
-1
lines changed

docs/09.EXT-REFERENCE-ARG.md

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ typedef struct
6161

6262
**Summary**
6363

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

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

816+
## jerryx_arg_js_iterator_restore
817+
818+
**Summary**
819+
820+
Restore the last item popped from the stack. This can be called as
821+
many times as there are arguments on the stack -- if called when the
822+
first element in the array is the current top of the stack, this
823+
function does nothing.
824+
825+
*Note:* This function relies on the underlying implementation of the
826+
arg stack as an array, as its function is to simply back up the "top
827+
of stack" pointer to point to the previous element of the array.
828+
829+
*Note:* Like `jerryx_arg_js_iterator_pop ()`, this function will
830+
change the `js_arg_idx` and `js_arg_p` values in the iterator.
831+
832+
**Prototype**
833+
834+
```c
835+
jerry_value_t
836+
jerryx_arg_js_iterator_restore (jerryx_arg_js_iterator_t *js_arg_iter_p)
837+
```
838+
- return value - the the new top of the stack.
839+
- `js_arg_iter_p` - the JS arg iterator to restore.
840+
841+
816842
## jerryx_arg_js_iterator_index
817843
818844
**Summary**

jerry-ext/arg/arg-js-iterator-helper.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,26 @@ jerryx_arg_js_iterator_pop (jerryx_arg_js_iterator_t *js_arg_iter_p) /**< the JS
3030
: jerry_create_undefined ());
3131
} /* jerryx_arg_js_iterator_pop */
3232

33+
/**
34+
* Restore the previous JS argument from the iterator.
35+
* It will change the index and js_arg_p value in the iterator.
36+
*
37+
* @return the restored (now current) JS argument.
38+
*/
39+
jerry_value_t
40+
jerryx_arg_js_iterator_restore (jerryx_arg_js_iterator_t *js_arg_iter_p) /**< the JS arg iterator */
41+
{
42+
if (js_arg_iter_p->js_arg_idx == 0)
43+
{
44+
return jerry_create_undefined ();
45+
}
46+
47+
--js_arg_iter_p->js_arg_idx;
48+
--js_arg_iter_p->js_arg_p;
49+
50+
return *js_arg_iter_p->js_arg_p;
51+
} /* jerryx_arg_js_iterator_restore */
52+
3353
/**
3454
* Get the current JS argument from the iterator.
3555
*

jerry-ext/include/jerryscript-ext/arg.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ jerryx_arg_transform_optional (jerryx_arg_js_iterator_t *js_arg_iter_p,
186186

187187
/* Helper functions for transform functions. */
188188
jerry_value_t jerryx_arg_js_iterator_pop (jerryx_arg_js_iterator_t *js_arg_iter_p);
189+
jerry_value_t jerryx_arg_js_iterator_restore (jerryx_arg_js_iterator_t *js_arg_iter_p);
189190
jerry_value_t jerryx_arg_js_iterator_peek (jerryx_arg_js_iterator_t *js_arg_iter_p);
190191
jerry_length_t jerryx_arg_js_iterator_index (jerryx_arg_js_iterator_t *js_arg_iter_p);
191192

tests/unit-ext/test-ext-arg.c

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ static const jerry_char_t test_source[] = TEST_STRING_LITERAL (
5757
"test_validator_array1(arr);"
5858
"test_validator_array1();"
5959
"test_validator_array2(arr);"
60+
"test_validator_restore(false, 3.0);"
61+
"test_validator_restore(3.0, false);"
6062
);
6163

6264
static const jerry_object_native_info_t thing_a_info =
@@ -87,6 +89,7 @@ static int validator2_count = 0;
8789
static int validator_int_count = 0;
8890
static int validator_prop_count = 0;
8991
static int validator_array_count = 0;
92+
static int validator_restore_count = 0;
9093

9194
/**
9295
* The handler should have following arguments:
@@ -561,6 +564,164 @@ test_validator_array2_handler (const jerry_value_t func_obj_val __attribute__((u
561564
return jerry_create_undefined ();
562565
} /* test_validator_array2_handler */
563566

567+
/**
568+
* This validator is designed to test the
569+
* jerryx_arg_js_iterator_restore function. We'll introduce a union
570+
* type to hold a bool or double and a transform function that will
571+
* look for this type. Then, we'll call the handler with two
572+
* parameters, one bool and one double and see if we correctly build
573+
* the union types for each parameter. To check that the code protects
574+
* against backing up too far, when the check for the double fails,
575+
* we'll "restore" the stack three times; this shouldn't break
576+
* anything.
577+
*/
578+
/*
579+
* This enumeration type specifies the kind of thing held in the union.
580+
*/
581+
typedef enum
582+
{
583+
DOUBLE_VALUE,
584+
BOOL_VALUE
585+
} union_type_t;
586+
587+
/*
588+
* This struct holds either a boolean or double in a union and has a
589+
* second field that describes the type held in the union.
590+
*/
591+
typedef struct
592+
{
593+
union_type_t type_of_value;
594+
union
595+
{
596+
double double_field;
597+
bool bool_field;
598+
} value;
599+
} double_or_bool_t;
600+
601+
/**
602+
* This creates a jerryx_arg_t that can be used like any
603+
* of the installed functions, like jerryx_arg_bool().
604+
*/
605+
#define jerryx_arg_double_or_bool_t(value_ptr, coerce_or_not, optional_or_not, last_parameter) \
606+
jerryx_arg_custom (value_ptr, \
607+
(uintptr_t)&((uintptr_t []){(uintptr_t)coerce_or_not, \
608+
(uintptr_t)optional_or_not, \
609+
(uintptr_t)last_parameter}), \
610+
jerry_arg_to_double_or_bool_t)
611+
/*
612+
* This function is the argument validator used in the above macro called
613+
* jerryx_arg_double_or_bool. It calls jerryx_arg_js_iterator_restore()
614+
* more times than it should to ensure that calling that function too
615+
* often doesn't cause an error.
616+
*/
617+
static jerry_value_t
618+
jerry_arg_to_double_or_bool_t (jerryx_arg_js_iterator_t *js_arg_iter_p,
619+
const jerryx_arg_t *c_arg_p)
620+
{
621+
/* c_arg_p has two fields: dest, which is a pointer to the data that
622+
* gets filled in, and extra_info, which contains the flags used to
623+
* control coercion and optional-ness, respectively. For this test,
624+
* we added an extra flag that tells us that we're working on the
625+
* last parameter; when we know it's the last parameter, we'll "restore"
626+
* the stack more times than there are actual stack values to ensure
627+
* that the restore function doesn't produce an error. */
628+
double_or_bool_t *destination = c_arg_p->dest;
629+
uintptr_t *extra_info = (uintptr_t *)(c_arg_p->extra_info);
630+
jerryx_arg_t conversion_function;
631+
jerry_value_t conversion_result;
632+
jerry_value_t restore_result;
633+
bool last_parameter = (extra_info[2] == 1);
634+
635+
validator_restore_count++;
636+
637+
conversion_function = jerryx_arg_number ((double *) (&(destination->value.double_field)),
638+
(jerryx_arg_coerce_t) extra_info[0],
639+
JERRYX_ARG_OPTIONAL);
640+
conversion_result = conversion_function.func (js_arg_iter_p, &conversion_function);
641+
if (!jerry_value_is_error (conversion_result))
642+
{
643+
if (last_parameter)
644+
{
645+
/* The stack is only two parameters high, but we want to ensure that
646+
* excessive calls will not result in aberrant behavior... */
647+
jerryx_arg_js_iterator_restore (js_arg_iter_p);
648+
jerryx_arg_js_iterator_restore (js_arg_iter_p);
649+
jerryx_arg_js_iterator_restore (js_arg_iter_p);
650+
restore_result = jerryx_arg_js_iterator_restore (js_arg_iter_p);
651+
TEST_ASSERT (jerry_value_is_undefined (restore_result));
652+
}
653+
654+
destination->type_of_value = DOUBLE_VALUE;
655+
return conversion_result;
656+
}
657+
658+
jerryx_arg_js_iterator_restore (js_arg_iter_p);
659+
660+
conversion_function = jerryx_arg_boolean ((bool *) (&(destination->value.bool_field)),
661+
(jerryx_arg_coerce_t) extra_info[0],
662+
(jerryx_arg_optional_t) extra_info[1]);
663+
664+
jerry_release_value (conversion_result);
665+
conversion_result = conversion_function.func (js_arg_iter_p, &conversion_function);
666+
if (!jerry_value_is_error (conversion_result))
667+
{
668+
if (last_parameter)
669+
{
670+
/* The stack is only two parameters high, but we want to ensure that
671+
* excessive calls will not result in aberrant behavior... */
672+
jerryx_arg_js_iterator_restore (js_arg_iter_p);
673+
jerryx_arg_js_iterator_restore (js_arg_iter_p);
674+
jerryx_arg_js_iterator_restore (js_arg_iter_p);
675+
restore_result = jerryx_arg_js_iterator_restore (js_arg_iter_p);
676+
TEST_ASSERT (jerry_value_is_undefined (restore_result));
677+
}
678+
679+
destination->type_of_value = BOOL_VALUE;
680+
return conversion_result;
681+
}
682+
683+
/* Fall through indicates that whatever they gave us, it wasn't
684+
* one of the types we were expecting... */
685+
jerry_release_value (conversion_result);
686+
return jerry_create_error (JERRY_ERROR_TYPE,
687+
(const jerry_char_t *) "double_or_bool-type error.");
688+
} /* jerry_arg_to_double_or_bool_t */
689+
690+
/**
691+
* This validator expects two parameters, one a bool and one a double -- the
692+
* order doesn't matter (so we'll call it twice with the orders reversed).
693+
*/
694+
static jerry_value_t
695+
test_validator_restore_handler (const jerry_value_t func_obj_val __attribute__((unused)), /**< function object */
696+
const jerry_value_t this_val __attribute__((unused)), /**< this value */
697+
const jerry_value_t args_p[], /**< arguments list */
698+
const jerry_length_t args_cnt __attribute__((unused))) /**< arguments length */
699+
{
700+
double_or_bool_t arg1;
701+
double_or_bool_t arg2;
702+
703+
jerryx_arg_t item_mapping[] =
704+
{
705+
jerryx_arg_double_or_bool_t (&arg1, JERRYX_ARG_NO_COERCE, JERRYX_ARG_REQUIRED, 0),
706+
jerryx_arg_double_or_bool_t (&arg2, JERRYX_ARG_NO_COERCE, JERRYX_ARG_REQUIRED, 1)
707+
};
708+
709+
jerry_value_t is_ok = jerryx_arg_transform_args (args_p, args_cnt, item_mapping, ARRAY_SIZE (item_mapping));
710+
711+
TEST_ASSERT (!jerry_value_is_error (is_ok));
712+
713+
/* We are going to call this with [false, 3.0] and [3.0, false] parameters... */
714+
bool arg1_is_false = (arg1.type_of_value == BOOL_VALUE && arg1.value.bool_field == false);
715+
bool arg1_is_three = (arg1.type_of_value == DOUBLE_VALUE && arg1.value.double_field == 3.0);
716+
bool arg2_is_false = (arg2.type_of_value == BOOL_VALUE && arg2.value.bool_field == false);
717+
bool arg2_is_three = (arg2.type_of_value == DOUBLE_VALUE && arg2.value.double_field == 3.0);
718+
TEST_ASSERT ((arg1_is_false && arg2_is_three) || (arg1_is_three && arg2_is_false));
719+
720+
jerry_release_value (is_ok);
721+
722+
return jerry_create_undefined ();
723+
} /* test_validator_restore_handler */
724+
564725
static void
565726
test_utf8_string (void)
566727
{
@@ -657,6 +818,7 @@ main (void)
657818
register_js_function ("test_validator_prop3", test_validator_prop3_handler);
658819
register_js_function ("test_validator_array1", test_validator_array1_handler);
659820
register_js_function ("test_validator_array2", test_validator_array2_handler);
821+
register_js_function ("test_validator_restore", test_validator_restore_handler);
660822

661823
jerry_value_t parsed_code_val = jerry_parse (NULL,
662824
0,
@@ -672,6 +834,7 @@ main (void)
672834
TEST_ASSERT (validator_prop_count == 4);
673835
TEST_ASSERT (validator_int_count == 3);
674836
TEST_ASSERT (validator_array_count == 3);
837+
TEST_ASSERT (validator_restore_count == 4);
675838

676839
jerry_release_value (res);
677840
jerry_release_value (parsed_code_val);

0 commit comments

Comments
 (0)