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 <math.h> 17 18#include "jerryscript-ext/arg.h" 19#include "jerryscript.h" 20 21/** 22 * The common function to deal with optional arguments. 23 * The core transform function is provided by argument `func`. 24 * 25 * @return jerry undefined: the transformer passes, 26 * jerry error: the transformer fails. 27 */ 28jerry_value_t 29jerryx_arg_transform_optional (jerryx_arg_js_iterator_t *js_arg_iter_p, /**< available JS args */ 30 const jerryx_arg_t *c_arg_p, /**< native arg */ 31 jerryx_arg_transform_func_t func) /**< the core transform function */ 32{ 33 jerry_value_t js_arg = jerryx_arg_js_iterator_peek (js_arg_iter_p); 34 35 if (jerry_value_is_undefined (js_arg)) 36 { 37 return jerryx_arg_js_iterator_pop (js_arg_iter_p); 38 } 39 40 return func (js_arg_iter_p, c_arg_p); 41} /* jerryx_arg_transform_optional */ 42 43/** 44 * The common part in transforming a JS argument to a number (double or certain int) type. 45 * Type coercion is not allowed. 46 * 47 * @return jerry undefined: the transformer passes, 48 * jerry error: the transformer fails. 49 */ 50static jerry_value_t 51jerryx_arg_transform_number_strict_common (jerryx_arg_js_iterator_t *js_arg_iter_p, /**< available JS args */ 52 double *number_p) /**< [out] the number in JS arg */ 53{ 54 jerry_value_t js_arg = jerryx_arg_js_iterator_pop (js_arg_iter_p); 55 56 if (!jerry_value_is_number (js_arg)) 57 { 58 return jerry_create_error (JERRY_ERROR_TYPE, 59 (jerry_char_t *) "It is not a number."); 60 } 61 62 *number_p = jerry_get_number_value (js_arg); 63 64 return jerry_create_undefined (); 65} /* jerryx_arg_transform_number_strict_common */ 66 67/** 68 * The common part in transforming a JS argument to a number (double or certain int) type. 69 * Type coercion is allowed. 70 * 71 * @return jerry undefined: the transformer passes, 72 * jerry error: the transformer fails. 73 */ 74static jerry_value_t 75jerryx_arg_transform_number_common (jerryx_arg_js_iterator_t *js_arg_iter_p, /**< available JS args */ 76 double *number_p) /**< [out] the number in JS arg */ 77{ 78 jerry_value_t js_arg = jerryx_arg_js_iterator_pop (js_arg_iter_p); 79 80 jerry_value_t to_number = jerry_value_to_number (js_arg); 81 82 if (jerry_value_is_error (to_number)) 83 { 84 jerry_release_value (to_number); 85 86 return jerry_create_error (JERRY_ERROR_TYPE, 87 (jerry_char_t *) "It can not be converted to a number."); 88 } 89 90 *number_p = jerry_get_number_value (to_number); 91 jerry_release_value (to_number); 92 93 return jerry_create_undefined (); 94} /* jerryx_arg_transform_number_common */ 95 96/** 97 * Transform a JS argument to a double. Type coercion is not allowed. 98 * 99 * @return jerry undefined: the transformer passes, 100 * jerry error: the transformer fails. 101 */ 102jerry_value_t 103jerryx_arg_transform_number_strict (jerryx_arg_js_iterator_t *js_arg_iter_p, /**< available JS args */ 104 const jerryx_arg_t *c_arg_p) /**< the native arg */ 105{ 106 return jerryx_arg_transform_number_strict_common (js_arg_iter_p, c_arg_p->dest); 107} /* jerryx_arg_transform_number_strict */ 108 109/** 110 * Transform a JS argument to a double. Type coercion is allowed. 111 * 112 * @return jerry undefined: the transformer passes, 113 * jerry error: the transformer fails. 114 */ 115jerry_value_t 116jerryx_arg_transform_number (jerryx_arg_js_iterator_t *js_arg_iter_p, /**< available JS args */ 117 const jerryx_arg_t *c_arg_p) /**< the native arg */ 118{ 119 return jerryx_arg_transform_number_common (js_arg_iter_p, c_arg_p->dest); 120} /* jerryx_arg_transform_number */ 121 122/** 123 * Helper function to process a double number before converting it 124 * to an integer. 125 * 126 * @return jerry undefined: the transformer passes, 127 * jerry error: the transformer fails. 128 */ 129static jerry_value_t 130jerryx_arg_helper_process_double (double *d, /**< [in, out] the number to be processed */ 131 double min, /**< the min value for clamping */ 132 double max, /**< the max value for clamping */ 133 jerryx_arg_int_option_t option) /**< the converting policies */ 134{ 135 if (isnan (*d)) 136 { 137 return jerry_create_error (JERRY_ERROR_TYPE, 138 (jerry_char_t *) "The number is NaN."); 139 } 140 141 if (option.clamp == JERRYX_ARG_NO_CLAMP) 142 { 143 if (*d > max || *d < min) 144 { 145 return jerry_create_error (JERRY_ERROR_TYPE, 146 (jerry_char_t *) "The number is out of range."); 147 } 148 } 149 else 150 { 151 *d = *d < min ? min : *d; 152 *d = *d > max ? max : *d; 153 } 154 155 if (option.round == JERRYX_ARG_ROUND) 156 { 157 *d = (*d >= 0.0) ? floor (*d + 0.5) : ceil (*d - 0.5); 158 } 159 else if (option.round == JERRYX_ARG_FLOOR) 160 { 161 *d = floor (*d); 162 } 163 else 164 { 165 *d = ceil (*d); 166 } 167 168 return jerry_create_undefined (); 169} /* jerryx_arg_helper_process_double */ 170 171/** 172 * Use the macro to define thr transform functions for int type. 173 */ 174#define JERRYX_ARG_TRANSFORM_FUNC_FOR_INT_TEMPLATE(type, suffix, min, max) \ 175 jerry_value_t jerryx_arg_transform_ ## type ## suffix (jerryx_arg_js_iterator_t *js_arg_iter_p, \ 176 const jerryx_arg_t *c_arg_p) \ 177 { \ 178 double tmp = 0.0; \ 179 jerry_value_t rv = jerryx_arg_transform_number ## suffix ## _common (js_arg_iter_p, &tmp); \ 180 if (jerry_value_is_error (rv)) \ 181 { \ 182 return rv; \ 183 } \ 184 jerry_release_value (rv); \ 185 union \ 186 { \ 187 jerryx_arg_int_option_t int_option; \ 188 uintptr_t extra_info; \ 189 } u = { .extra_info = c_arg_p->extra_info }; \ 190 rv = jerryx_arg_helper_process_double (&tmp, min, max, u.int_option); \ 191 if (jerry_value_is_error (rv)) \ 192 { \ 193 return rv; \ 194 } \ 195 *(type ## _t *) c_arg_p->dest = (type ## _t) tmp; \ 196 return rv; \ 197 } 198 199#define JERRYX_ARG_TRANSFORM_FUNC_FOR_INT(type, min, max) \ 200 JERRYX_ARG_TRANSFORM_FUNC_FOR_INT_TEMPLATE (type, _strict, min, max) \ 201 JERRYX_ARG_TRANSFORM_FUNC_FOR_INT_TEMPLATE (type, , min, max) 202 203JERRYX_ARG_TRANSFORM_FUNC_FOR_INT (uint8, 0, UINT8_MAX) 204JERRYX_ARG_TRANSFORM_FUNC_FOR_INT (int8, INT8_MIN, INT8_MAX) 205JERRYX_ARG_TRANSFORM_FUNC_FOR_INT (uint16, 0, UINT16_MAX) 206JERRYX_ARG_TRANSFORM_FUNC_FOR_INT (int16, INT16_MIN, INT16_MAX) 207JERRYX_ARG_TRANSFORM_FUNC_FOR_INT (uint32, 0, UINT32_MAX) 208JERRYX_ARG_TRANSFORM_FUNC_FOR_INT (int32, INT32_MIN, INT32_MAX) 209 210#undef JERRYX_ARG_TRANSFORM_FUNC_FOR_INT_TEMPLATE 211#undef JERRYX_ARG_TRANSFORM_FUNC_FOR_INT 212/** 213 * Transform a JS argument to a boolean. Type coercion is not allowed. 214 * 215 * @return jerry undefined: the transformer passes, 216 * jerry error: the transformer fails. 217 */ 218jerry_value_t 219jerryx_arg_transform_boolean_strict (jerryx_arg_js_iterator_t *js_arg_iter_p, /**< available JS args */ 220 const jerryx_arg_t *c_arg_p) /**< the native arg */ 221{ 222 jerry_value_t js_arg = jerryx_arg_js_iterator_pop (js_arg_iter_p); 223 224 if (!jerry_value_is_boolean (js_arg)) 225 { 226 return jerry_create_error (JERRY_ERROR_TYPE, 227 (jerry_char_t *) "It is not a boolean."); 228 } 229 230 bool *dest = c_arg_p->dest; 231 *dest = jerry_get_boolean_value (js_arg); 232 233 return jerry_create_undefined (); 234} /* jerryx_arg_transform_boolean_strict */ 235 236/** 237 * Transform a JS argument to a boolean. Type coercion is allowed. 238 * 239 * @return jerry undefined: the transformer passes, 240 * jerry error: the transformer fails. 241 */ 242jerry_value_t 243jerryx_arg_transform_boolean (jerryx_arg_js_iterator_t *js_arg_iter_p, /**< available JS args */ 244 const jerryx_arg_t *c_arg_p) /**< the native arg */ 245{ 246 jerry_value_t js_arg = jerryx_arg_js_iterator_pop (js_arg_iter_p); 247 248 bool to_boolean = jerry_value_to_boolean (js_arg); 249 250 bool *dest = c_arg_p->dest; 251 *dest = to_boolean; 252 253 return jerry_create_undefined (); 254} /* jerryx_arg_transform_boolean */ 255 256/** 257 * The common routine for string transformer. 258 * It works for both CESU-8 and UTF-8 string. 259 * 260 * @return jerry undefined: the transformer passes, 261 * jerry error: the transformer fails. 262 */ 263static jerry_value_t 264jerryx_arg_string_to_buffer_common_routine (jerry_value_t js_arg, /**< JS arg */ 265 const jerryx_arg_t *c_arg_p, /**< native arg */ 266 bool is_utf8) /**< whether it is UTF-8 string */ 267{ 268 jerry_char_t *target_p = (jerry_char_t *) c_arg_p->dest; 269 jerry_size_t target_buf_size = (jerry_size_t) c_arg_p->extra_info; 270 jerry_size_t size; 271 jerry_length_t len; 272 273 if (!is_utf8) 274 { 275 size = jerry_string_to_char_buffer (js_arg, 276 target_p, 277 target_buf_size); 278 len = jerry_get_string_length (js_arg); 279 } 280 else 281 { 282 size = jerry_string_to_utf8_char_buffer (js_arg, 283 target_p, 284 target_buf_size); 285 len = jerry_get_utf8_string_length (js_arg); 286 } 287 288 if ((size == target_buf_size) || (size == 0 && len != 0)) 289 { 290 return jerry_create_error (JERRY_ERROR_TYPE, 291 (jerry_char_t *) "Buffer size is not large enough."); 292 } 293 294 target_p[size] = '\0'; 295 296 return jerry_create_undefined (); 297} /* jerryx_arg_string_to_buffer_common_routine */ 298 299/** 300 * Transform a JS argument to a UTF-8/CESU-8 char array. Type coercion is not allowed. 301 * 302 * @return jerry undefined: the transformer passes, 303 * jerry error: the transformer fails. 304 */ 305static jerry_value_t 306jerryx_arg_transform_string_strict_common (jerryx_arg_js_iterator_t *js_arg_iter_p, /**< available JS args */ 307 const jerryx_arg_t *c_arg_p, /**< the native arg */ 308 bool is_utf8) /**< whether it is a UTF-8 string */ 309{ 310 jerry_value_t js_arg = jerryx_arg_js_iterator_pop (js_arg_iter_p); 311 312 if (!jerry_value_is_string (js_arg)) 313 { 314 return jerry_create_error (JERRY_ERROR_TYPE, 315 (jerry_char_t *) "It is not a string."); 316 } 317 318 return jerryx_arg_string_to_buffer_common_routine (js_arg, c_arg_p, is_utf8); 319} /* jerryx_arg_transform_string_strict_common */ 320 321/** 322 * Transform a JS argument to a UTF-8/CESU-8 char array. Type coercion is allowed. 323 * 324 * @return jerry undefined: the transformer passes, 325 * jerry error: the transformer fails. 326 */ 327static jerry_value_t 328jerryx_arg_transform_string_common (jerryx_arg_js_iterator_t *js_arg_iter_p, /**< available JS args */ 329 const jerryx_arg_t *c_arg_p, /**< the native arg */ 330 bool is_utf8) /**< whether it is a UTF-8 string */ 331{ 332 jerry_value_t js_arg = jerryx_arg_js_iterator_pop (js_arg_iter_p); 333 334 jerry_value_t to_string = jerry_value_to_string (js_arg); 335 336 if (jerry_value_is_error (to_string)) 337 { 338 jerry_release_value (to_string); 339 340 return jerry_create_error (JERRY_ERROR_TYPE, 341 (jerry_char_t *) "It can not be converted to a string."); 342 } 343 344 jerry_value_t ret = jerryx_arg_string_to_buffer_common_routine (to_string, c_arg_p, is_utf8); 345 jerry_release_value (to_string); 346 347 return ret; 348} /* jerryx_arg_transform_string_common */ 349 350/** 351 * Transform a JS argument to a cesu8 char array. Type coercion is not allowed. 352 * 353 * Note: 354 * returned value must be freed with jerry_release_value, when it is no longer needed. 355 * 356 * @return jerry undefined: the transformer passes, 357 * jerry error: the transformer fails. 358 */ 359jerry_value_t 360jerryx_arg_transform_string_strict (jerryx_arg_js_iterator_t *js_arg_iter_p, /**< available JS args */ 361 const jerryx_arg_t *c_arg_p) /**< the native arg */ 362{ 363 return jerryx_arg_transform_string_strict_common (js_arg_iter_p, c_arg_p, false); 364} /* jerryx_arg_transform_string_strict */ 365 366/** 367 * Transform a JS argument to a utf8 char array. Type coercion is not allowed. 368 * 369 * Note: 370 * returned value must be freed with jerry_release_value, when it is no longer needed. 371 * 372 * @return jerry undefined: the transformer passes, 373 * jerry error: the transformer fails. 374 */ 375jerry_value_t 376jerryx_arg_transform_utf8_string_strict (jerryx_arg_js_iterator_t *js_arg_iter_p, /**< available JS args */ 377 const jerryx_arg_t *c_arg_p) /**< the native arg */ 378{ 379 return jerryx_arg_transform_string_strict_common (js_arg_iter_p, c_arg_p, true); 380} /* jerryx_arg_transform_utf8_string_strict */ 381 382/** 383 * Transform a JS argument to a cesu8 char array. Type coercion is allowed. 384 * 385 * Note: 386 * returned value must be freed with jerry_release_value, when it is no longer needed. 387 * 388 * @return jerry undefined: the transformer passes, 389 * jerry error: the transformer fails. 390 */ 391jerry_value_t 392jerryx_arg_transform_string (jerryx_arg_js_iterator_t *js_arg_iter_p, /**< available JS args */ 393 const jerryx_arg_t *c_arg_p) /**< the native arg */ 394{ 395 return jerryx_arg_transform_string_common (js_arg_iter_p, c_arg_p, false); 396} /* jerryx_arg_transform_string */ 397 398/** 399 * Transform a JS argument to a utf8 char array. Type coercion is allowed. 400 * 401 * Note: 402 * returned value must be freed with jerry_release_value, when it is no longer needed. 403 * 404 * @return jerry undefined: the transformer passes, 405 * jerry error: the transformer fails. 406 */ 407jerry_value_t 408jerryx_arg_transform_utf8_string (jerryx_arg_js_iterator_t *js_arg_iter_p, /**< available JS args */ 409 const jerryx_arg_t *c_arg_p) /**< the native arg */ 410{ 411 return jerryx_arg_transform_string_common (js_arg_iter_p, c_arg_p, true); 412} /* jerryx_arg_transform_utf8_string */ 413 414/** 415 * Check whether the JS argument is jerry function, if so, assign to the native argument. 416 * 417 * @return jerry undefined: the transformer passes, 418 * jerry error: the transformer fails. 419 */ 420jerry_value_t 421jerryx_arg_transform_function (jerryx_arg_js_iterator_t *js_arg_iter_p, /**< available JS args */ 422 const jerryx_arg_t *c_arg_p) /**< the native arg */ 423{ 424 jerry_value_t js_arg = jerryx_arg_js_iterator_pop (js_arg_iter_p); 425 426 if (!jerry_value_is_function (js_arg)) 427 { 428 return jerry_create_error (JERRY_ERROR_TYPE, 429 (jerry_char_t *) "It is not a function."); 430 } 431 432 jerry_value_t *func_p = c_arg_p->dest; 433 *func_p = jerry_acquire_value (js_arg); 434 435 return jerry_create_undefined (); 436} /* jerryx_arg_transform_function */ 437 438/** 439 * Check whether the native pointer has the expected type info. 440 * If so, assign it to the native argument. 441 * 442 * @return jerry undefined: the transformer passes, 443 * jerry error: the transformer fails. 444 */ 445jerry_value_t 446jerryx_arg_transform_native_pointer (jerryx_arg_js_iterator_t *js_arg_iter_p, /**< available JS args */ 447 const jerryx_arg_t *c_arg_p) /**< the native arg */ 448{ 449 jerry_value_t js_arg = jerryx_arg_js_iterator_pop (js_arg_iter_p); 450 451 if (!jerry_value_is_object (js_arg)) 452 { 453 return jerry_create_error (JERRY_ERROR_TYPE, 454 (jerry_char_t *) "It is not an object."); 455 } 456 457 const jerry_object_native_info_t *expected_info_p; 458 expected_info_p = (const jerry_object_native_info_t *) c_arg_p->extra_info; 459 void **ptr_p = (void **) c_arg_p->dest; 460 bool is_ok = jerry_get_object_native_pointer (js_arg, ptr_p, expected_info_p); 461 462 if (!is_ok) 463 { 464 return jerry_create_error (JERRY_ERROR_TYPE, 465 (jerry_char_t *) "The object has no native pointer or type does not match."); 466 } 467 468 return jerry_create_undefined (); 469} /* jerryx_arg_transform_native_pointer */ 470 471/** 472 * Check whether the JS object's properties have expected types, and transform them into native args. 473 * 474 * @return jerry undefined: the transformer passes, 475 * jerry error: the transformer fails. 476 */ 477jerry_value_t 478jerryx_arg_transform_object_props (jerryx_arg_js_iterator_t *js_arg_iter_p, /**< available JS args */ 479 const jerryx_arg_t *c_arg_p) /**< the native arg */ 480{ 481 jerry_value_t js_arg = jerryx_arg_js_iterator_pop (js_arg_iter_p); 482 483 const jerryx_arg_object_props_t *object_props = (const jerryx_arg_object_props_t *) c_arg_p->extra_info; 484 485 return jerryx_arg_transform_object_properties (js_arg, 486 object_props->name_p, 487 object_props->name_cnt, 488 object_props->c_arg_p, 489 object_props->c_arg_cnt); 490} /* jerryx_arg_transform_object_props */ 491 492/** 493 * Check whether the JS array's items have expected types, and transform them into native args. 494 * 495 * @return jerry undefined: the transformer passes, 496 * jerry error: the transformer fails. 497 */ 498jerry_value_t 499jerryx_arg_transform_array_items (jerryx_arg_js_iterator_t *js_arg_iter_p, /**< available JS args */ 500 const jerryx_arg_t *c_arg_p) /**< the native arg */ 501{ 502 jerry_value_t js_arg = jerryx_arg_js_iterator_pop (js_arg_iter_p); 503 504 const jerryx_arg_array_items_t *array_items_p = (const jerryx_arg_array_items_t *) c_arg_p->extra_info; 505 506 return jerryx_arg_transform_array (js_arg, 507 array_items_p->c_arg_p, 508 array_items_p->c_arg_cnt); 509} /* jerryx_arg_transform_array_items */ 510 511/** 512 * Define transformer for optional argument. 513 */ 514#define JERRYX_ARG_TRANSFORM_OPTIONAL(type) \ 515 jerry_value_t \ 516 jerryx_arg_transform_ ## type ## _optional (jerryx_arg_js_iterator_t *js_arg_iter_p, \ 517 const jerryx_arg_t *c_arg_p) \ 518 { \ 519 return jerryx_arg_transform_optional (js_arg_iter_p, c_arg_p, jerryx_arg_transform_ ## type); \ 520 } 521 522JERRYX_ARG_TRANSFORM_OPTIONAL (number) 523JERRYX_ARG_TRANSFORM_OPTIONAL (number_strict) 524JERRYX_ARG_TRANSFORM_OPTIONAL (boolean) 525JERRYX_ARG_TRANSFORM_OPTIONAL (boolean_strict) 526JERRYX_ARG_TRANSFORM_OPTIONAL (string) 527JERRYX_ARG_TRANSFORM_OPTIONAL (string_strict) 528JERRYX_ARG_TRANSFORM_OPTIONAL (utf8_string) 529JERRYX_ARG_TRANSFORM_OPTIONAL (utf8_string_strict) 530JERRYX_ARG_TRANSFORM_OPTIONAL (function) 531JERRYX_ARG_TRANSFORM_OPTIONAL (native_pointer) 532JERRYX_ARG_TRANSFORM_OPTIONAL (object_props) 533JERRYX_ARG_TRANSFORM_OPTIONAL (array_items) 534 535JERRYX_ARG_TRANSFORM_OPTIONAL (uint8) 536JERRYX_ARG_TRANSFORM_OPTIONAL (uint16) 537JERRYX_ARG_TRANSFORM_OPTIONAL (uint32) 538JERRYX_ARG_TRANSFORM_OPTIONAL (int8) 539JERRYX_ARG_TRANSFORM_OPTIONAL (int16) 540JERRYX_ARG_TRANSFORM_OPTIONAL (int32) 541JERRYX_ARG_TRANSFORM_OPTIONAL (int8_strict) 542JERRYX_ARG_TRANSFORM_OPTIONAL (int16_strict) 543JERRYX_ARG_TRANSFORM_OPTIONAL (int32_strict) 544JERRYX_ARG_TRANSFORM_OPTIONAL (uint8_strict) 545JERRYX_ARG_TRANSFORM_OPTIONAL (uint16_strict) 546JERRYX_ARG_TRANSFORM_OPTIONAL (uint32_strict) 547 548#undef JERRYX_ARG_TRANSFORM_OPTIONAL 549 550/** 551 * Ignore the JS argument. 552 * 553 * @return jerry undefined 554 */ 555jerry_value_t 556jerryx_arg_transform_ignore (jerryx_arg_js_iterator_t *js_arg_iter_p, /**< available JS args */ 557 const jerryx_arg_t *c_arg_p) /**< the native arg */ 558{ 559 (void) js_arg_iter_p; /* unused */ 560 (void) c_arg_p; /* unused */ 561 562 return jerry_create_undefined (); 563} /* jerryx_arg_transform_ignore */ 564