尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

JerryScript 扩展 API 实战:用 jerryx_arg 实现 JS 参数校验与 C 类型转换

JerryScript 扩展 API 实战:用 jerryx_arg 实现 JS 参数校验与 C 类型转换 语言运行时嵌入式物联网编译器【免费下载链接】jerryscriptUltra-lightweight JavaScript engine for the Internet of Things.项目地址https://gitcode.com/gh_mirrors/je/jerryscript点击查看免费下载本指南围绕 JerryScript 扩展库jerry-ext中的jerryx_arg参数处理组件展开讲解如何在外置函数处理器external handler中把 JavaScript 传入的参数批量校验并转换为 C 原生类型。读完本文你将掌握jerryx_arg_t变换链的构建方式、四大策略枚举coerce / optional / round / clamp的语义以及this、函数参数、对象属性、数组元素四类场景的完整绑定写法并能用jerryx_arg_custom自定义校验逻辑。为什么要用 jerryx_arg在 JerryScript 中编写外置函数时处理器收到的是const jerry_value_t args_p[]数组逐个手动检查类型、调用jerry_value_as_number()/jerry_value_to_string()再写回 C 变量代码冗长且容易漏掉错误分支。jerryx_arg把校验 转换封装成一组可组合的变换步骤transformation step每个步骤是一个jerryx_arg_t结构体按声明顺序依次消费输入参数一旦某个步骤失败就返回Error成功则返回undefined并把结果写入预先声明的 C 变量。整个组件位于 jerry-ext/arg 目录头文件为 jerryscript-ext/arg.h。核心数据结构jerryx_arg_t单个校验/变换步骤一个jerryx_arg_t描述了如何消费一个或多个JS 参数并写入哪里定义于 arg.hstruct jerryx_arg_t { jerryx_arg_transform_func_t func; /** the transform function */ void *dest; /** pointer to destination where func should store the result */ uintptr_t extra_info; /** extra information, specific to func */ };func执行校验与转换的回调函数见下文jerryx_arg_transform_func_t。dest结果写入的目标 C 变量指针例如bool *、double *、char[]的起始地址。extra_info传递给func的附加信息按函数不同承载不同含义——对整型变换它是舍入 钳制策略的打包值对字符串变换它是目标缓冲区大小对jerryx_arg_native_pointer它是期望的jerry_object_native_info_t *类型信息指针。大多数场景不需要手工填充该结构体arg.h提供了jerryx_arg_number()、jerryx_arg_boolean()等辅助函数来生成步骤实例。jerryx_arg_object_props_t对象属性变换的描述jerryx_arg_object_properties使用该结构描述要读取对象的哪些属性、按什么规则转换定义于 arg.htypedef struct { const jerry_char_t **name_p; /** property name list of the JS object */ jerry_length_t name_cnt; /** count of the name list */ const jerryx_arg_t *c_arg_p; /** points to the array of transformation steps */ jerry_length_t c_arg_cnt; /** the count of the c_arg_p array */ } jerryx_arg_object_props_t;属性名列表与变换步骤数组按位置一一对应name_p[0]对应的属性值由c_arg_p[0]校验转换依此类推。jerryx_arg_array_items_t数组元素变换的描述jerryx_arg_array使用该结构描述数组的每个元素如何转换定义于 arg.htypedef struct { const jerryx_arg_t *c_arg_p; /** points to the array of transformation steps */ jerry_length_t c_arg_cnt; /** the count of the c_arg_p array */ } jerryx_arg_array_items_t;jerryx_arg_transform_func_t变换函数签名typedef jerry_value_t (*jerryx_arg_transform_func_t) (jerryx_arg_js_iterator_t *js_arg_iter_p, const jerryx_arg_t *c_arg_p);变换函数通过参数迭代器js_arg_iter_p获取输入通过c_arg_p读取dest与extra_info成功时返回undefined失败时返回Error成功时把结果写入c_arg_p-dest校验失败时不得修改c_arg_p-dest可以使用jerryx_arg_js_iterator_pop()/jerryx_arg_js_iterator_peek()获取下一个输入值一个变换函数允许消费任意多个输入值——这是实现复杂签名匹配如同一 C 结构体由多个参数拼装、兼容多种 JS 调用形式的关键能力。用户可以用jerryx_arg_custom()包装自己的变换函数实现自定义校验。四大策略枚举决定转换行为的开关jerryx_arg_coerce_t是否允许类型强制转换定义于 arg.hJERRYX_ARG_COERCE允许强制转换变换内部会调用toNumber/toBoolean/toString等操作JERRYX_ARG_NO_COERCE不允许强制转换类型不匹配时直接失败抛出 TypeError。jerryx_arg_optional_t可选还是必选定义于 arg.hJERRYX_ARG_OPTIONAL可选。当传入参数为undefined时变换视为成功且c_arg_p-dest保持原值这正是文档示例中用默认值初始化 C 变量能生效的原因JERRYX_ARG_REQUIRED必选。当传入参数为undefined时变换失败c_arg_p-dest同样保持不变。jerryx_arg_round_t整型转换的舍入策略定义于 arg.h仅用于jerryx_arg_uint8/int8/uint16/int16/uint32/int32JERRYX_ARG_ROUND使用round()语义JERRYX_ARG_FLOOR使用floor()语义JERRYX_ARG_CEIL使用ceil()语义。注意源码里的 round 实现为(*d 0.0) ? floor (*d 0.5) : ceil (*d - 0.5)见 arg-transform-functions.c。jerryx_arg_clamp_t整型转换的越界策略定义于 arg.hJERRYX_ARG_CLAMP数值越界时钳制到类型边界值JERRYX_ARG_NO_CLAMP越界时抛出 range 错误实际实现中抛出的是一条 TypeErrorThe number is out of range.。五大主变换函数jerryx_arg_transform_this_and_args同时处理 this 与参数jerry_value_t jerryx_arg_transform_this_and_args (const jerry_value_t this_val, const jerry_value_t *js_arg_p, const jerry_length_t js_arg_cnt, const jerryx_arg_t *c_arg_p, jerry_length_t c_arg_cnt)这是外置函数处理器内最常用的入口。要点this_valthis值作为第一个值被处理先于参数数组js_arg_p/js_arg_cntJS 参数数组及长度即处理器收到的args_p与args_countc_arg_p/c_arg_cnt变换步骤数组及长度返回值全部校验通过返回undefined任一校验失败返回Error。从源码看该函数先构造一个只含this_val的临时迭代器执行第一个步骤若失败则抛出this validation failed.随后把剩余步骤交给jerryx_arg_transform_args处理见 arg.c。因此映射表中第一个步骤通常应放jerryx_arg_ignore()来跳过 this。jerryx_arg_transform_args纯参数数组变换jerry_value_t jerryx_arg_transform_args (const jerry_value_t *js_arg_p, const jerry_length_t js_arg_cnt, const jerryx_arg_t *c_arg_p, jerry_length_t c_arg_cnt)仅校验参数数组、不含this用于不需要关心this的场景。其核心循环见 arg.c按顺序执行c_arg_p-func (iterator, c_arg_p)一旦某一步返回 exception 立即中止。jerryx_arg_transform_object_properties对象属性变换jerry_value_t jerryx_arg_transform_object_properties (const jerry_value_t obj_val, const jerry_char_t **name_p, const jerry_length_t name_cnt, const jerryx_arg_t *c_arg_p, jerry_length_t c_arg_cnt);把单个 JS 对象的属性按名字读取出来并逐一转换。源码实现arg.c会先确认obj_val是对象否则抛 Not an object.然后用jerry_object_get取出每个属性值放入临时数组再复用jerryx_arg_transform_args批量转换最后释放临时值。注意该函数只转换单个对象的属性。若要在一次调用中转换多个参数例如外部处理器的多个入参都是对象应使用jerryx_arg_object_properties步骤配合jerryx_arg_transform_this_and_args或jerryx_arg_transform_args。jerryx_arg_transform_array数组元素变换jerry_value_t jerryx_arg_transform_array (const jerry_value_t array_val, const jerryx_arg_t *c_arg_p, jerry_length_t c_arg_cnt);把单个 JS 数组的前c_arg_cnt个元素按步骤转换。源码arg.c先检查jerry_value_is_array再按索引jerry_object_get_index取元素并批量转换。同样地若要一次转换多个参数请使用jerryx_arg_array步骤。jerryx_arg_transform_optional可选参数通用处理器jerry_value_t jerryx_arg_transform_optional (jerryx_arg_js_iterator_t *js_arg_iter_p, const jerryx_arg_t *c_arg_p, jerryx_arg_transform_func_t func);通用的可选参数分发函数先用peek查看当前参数若是undefined则直接pop并返回成功dest保持不变否则调用核心变换func实现见 arg-transform-functions.c。所有xxx_optional变体如jerryx_arg_transform_number_optional都由它包装生成。常用校验辅助函数一行声明一个变换步骤整型族jerryx_arg_uint8 / uint16 / uint32 / int8 / int16 / int32以jerryx_arg_int32为例static inline jerryx_arg_t jerryx_arg_int32 (int32_t *dest, jerryx_arg_round_t round_flag, jerryx_arg_clamp_t clamp_flag, jerryx_arg_coerce_t coerce_flag, jerryx_arg_optional_t opt_flag);各参数含义dest结果要写入的int32_t变量指针round_flag舍入策略JERRYX_ARG_ROUND/FLOOR/CEILclamp_flag越界钳制策略JERRYX_ARG_CLAMP/NO_CLAMPcoerce_flag是否允许类型强制转换opt_flag可选还是必选。每种整型都有对应的类型范围uint8为[0, UINT8_MAX]、int8为[INT8_MIN, INT8_MAX]、uint16为[0, UINT16_MAX]、int16为[INT16_MIN, INT16_MAX]、uint32为[0, UINT32_MAX]、int32为[INT32_MIN, INT32_MAX]这些边界直接内嵌在变换函数模板宏里arg-transform-functions.c。变换过程是先把 JS 参数转成double依次做 NaN 检查*d ! *d、越界钳制或报错、按舍入策略取整最后写回目标类型。注意整型步骤的舍入/钳制策略会打包进extra_info内部用jerryx_arg_int_option_t联合体承载并且arg.c开头用JERRYX_STATIC_ASSERT静态断言保证该结构能放进extra_info见 arg.c。jerryx_arg_numberJS number → C doublestatic inline jerryx_arg_t jerryx_arg_number (double *dest, jerryx_arg_coerce_t coerce_flag, jerryx_arg_optional_t opt_flag)消费一个numberJS 参数并写入 Cdouble。JERRYX_ARG_COERCE模式下内部调用jerry_value_to_number转换失败抛 It can not be converted to a number.NO_COERCE模式先检查jerry_value_is_number否则抛 It is not a number.见 arg-transform-functions.c。jerryx_arg_booleanJS boolean → C boolstatic inline jerryx_arg_t jerryx_arg_boolean (bool *dest, jerryx_arg_coerce_t coerce_flag, jerryx_arg_optional_t opt_flag)COERCE模式调用jerry_value_to_booleanNO_COERCE模式要求必须是布尔类型否则抛 It is not a boolean.。jerryx_arg_stringJS string → CESU-8 C 字符数组static inline jerryx_arg_t jerryx_arg_string (char *dest, uint32_t size, jerryx_arg_coerce_t coerce_flag, jerryx_arg_optional_t opt_flag)dest目标 Cchar数组起始地址size目标数组总大小字节。内部把size存进extra_info编码为CESU-8JerryScript 字符串的内部编码。字符串变换的公共例程arg-transform-functions.c用jerry_string_size计算所需字节数若size target_buf_size - 1则抛 Buffer size is not large enough.写入后自动补\0结尾。因此size应预留终止符空间。jerryx_arg_utf8_stringJS string → UTF-8 C 字符数组static inline jerryx_arg_t jerryx_arg_utf8_string (char *dest, uint32_t size, jerryx_arg_coerce_t coerce_flag, jerryx_arg_optional_t opt_flag)与jerryx_arg_string完全相同只是输出编码为 UTF-8。两者共享同一套公共例程仅传入的jerry_encoding_t不同JERRY_ENCODING_CESU8vsJERRY_ENCODING_UTF8。单元测试test_utf8_string验证了 CESU-8 字符串 str: {DESERET CAPITAL LETTER LONG I} 能被正确转换为 UTF-8 输出见 test-ext-arg.c。jerryx_arg_functionJS function → C jerry_value_tstatic inline jerryx_arg_t jerryx_arg_function (jerry_value_t *dest, jerryx_arg_optional_t opt_flag)要求参数是函数并把jerry_value_copy出的引用写入dest调用者负责在不再需要时jerry_value_free非函数抛 It is not a function.。注意函数类型不支持 coerce因此只有opt_flag一个策略参数。jerryx_arg_native_pointerJS object 中的 native pointer → C 指针static inline jerryx_arg_t jerryx_arg_native_pointer (void **dest, const jerry_object_native_info_t *info_p, jerryx_arg_optional_t opt_flag)消费一个背靠 native pointer的对象参数先确认是对象再用jerry_object_get_native_ptr取出指针并要求对象的 native info 与info_p匹配。若指针为 NULL无指针或类型不匹配则抛 The object has no native pointer or type does not match.见 arg-transform-functions.c。典型用途是校验this必须是某个自定义类型对象如测试中的thing_a_info。jerryx_arg_object_properties把对象参数作为属性变换步骤static inline jerryx_arg_t jerryx_arg_object_properties (const jerryx_arg_object_props_t *obj_prop_p, jerryx_arg_optional_t opt_flag);消费一个object参数内部调用jerryx_arg_transform_object_properties把它的属性转成原生值。obj_prop_p指向预先填好的jerryx_arg_object_props_t。实现上obj_prop_p被存入extra_info变换函数见 arg-transform-functions.c。jerryx_arg_array把数组参数作为元素变换步骤static inline jerryx_arg_t jerryx_arg_array (const jerryx_arg_array_items_t *array_items_p, jerryx_arg_optional_t opt_flag);消费一个array参数内部调用jerryx_arg_transform_array把它的元素转成原生值。自定义校验jerryx_arg_ignore、jerryx_arg_custom 与迭代器jerryx_arg_ignore忽略一个参数static inline jerryx_arg_t jerryx_arg_ignore (void);生成一个什么都不做的步骤变换函数直接返回undefined常用于映射表第一位跳过this。注意它不会消费输入值因此排在它之后的步骤仍从同一个位置读取。jerryx_arg_custom接入自定义变换static inline jerryx_arg_t jerryx_arg_custom (void *dest, uintptr_t extra_info, jerryx_arg_transform_func_t func)把任意dest、extra_info与自定义变换函数组装成一个步骤是扩展jerryx_arg能力的通用入口。extra_info可以承载任意附加数据例如测试中用其传递期望数值、coerce/optional 标志等。迭代器辅助函数迭代器结构定义于 arg-internal.h内部维护js_arg_p、js_arg_cnt与当前索引js_arg_idx。四个辅助函数实现于 arg-js-iterator-helper.cjerryx_arg_js_iterator_pop()取出当前参数并前移迭代器修改js_arg_idx与js_arg_p越界时返回undefinedjerryx_arg_js_iterator_peek()查看当前参数但不前移迭代器jerryx_arg_js_iterator_restore()回退一步--js_arg_idx; --js_arg_p;当栈顶已是第一个元素时调用不做任何事并返回undefined。文档特别说明该函数依赖参数栈底层是数组这一实现事实本质是回拨栈顶指针jerryx_arg_js_iterator_index()返回当前参数索引。实战示例从文档到可运行代码示例 1this 参数数组的标准绑定JS 侧签名function (requiredBool, requiredString, optionalNumber)C 侧处理器如下完整代码见 09.EXT-REFERENCE-ARG.md#include jerryscript.h #include jerryscript-ext/arg.h /* JS signature: function (requiredBool, requiredString, optionalNumber) */ static jerry_value_t my_external_handler (const jerry_value_t function_obj, const jerry_value_t this_val, const jerry_value_t args_p[], const jerry_length_t args_count) { bool required_bool; char required_str[16]; double optional_num 1234.567; // default value /* mapping defines the steps to transform input arguments to C variables. */ const jerryx_arg_t mapping[] { /* this is the first value. No checking needed on this for this function. */ jerryx_arg_ignore (), jerryx_arg_boolean (required_bool, JERRYX_ARG_NO_COERCE, JERRYX_ARG_REQUIRED), jerryx_arg_string (required_str, sizeof (required_str), JERRYX_ARG_NO_COERCE, JERRYX_ARG_REQUIRED), jerryx_arg_number (optional_num, JERRYX_ARG_NO_COERCE, JERRYX_ARG_OPTIONAL), }; /* Validate and transform. */ const jerry_value_t rv jerryx_arg_transform_this_and_args (this_val, args_p, args_count, mapping, 4); if (jerry_value_is_exception (rv)) { /* Handle error. */ return rv; } /* * Validated and transformed successfully! * required_bool, required_str and optional_num can now be used. */ return jerry_undefined (); /* Or return something more meaningful. */ }关键点jerryx_arg_ignore()占住this位置optional_num预置默认值当第三个参数缺省或为undefined时保持不变映射表长度此处为 4与c_arg_cnt必须一致。示例 2对象属性绑定JS 侧期望args_p[0]是含enableboolean必选、datanumber必选、extra_datanumber可选三个属性的对象static jerry_value_t my_external_handler (const jerry_value_t function_obj, const jerry_value_t this_val, const jerry_value_t args_p[], const jerry_length_t args_count) { bool required_bool; double required_num; double optional_num 1234.567; // default value /* prop_name_p defines the name list of the expected properties names. */ const char *prop_name_p[] { enable, data, extra_data }; /* prop_mapping defines the steps to transform properties to C variables. */ const jerryx_arg_t prop_mapping[] { jerryx_arg_boolean (required_bool, JERRYX_ARG_COERCE, JERRYX_ARG_REQUIRED), jerryx_arg_number (required_num, JERRYX_ARG_COERCE, JERRYX_ARG_REQUIRED), jerryx_arg_number (optional_num, JERRYX_ARG_COERCE, JERRYX_ARG_OPTIONAL) }; /* Prepare the jerryx_arg_object_props_t instance. */ const jerryx_arg_object_props_t prop_info { .name_p (const jerry_char_t **) prop_name_p, .name_cnt 3, .c_arg_p prop_mapping, .c_arg_cnt 3 }; /* It is the mapping used in the jerryx_arg_transform_args. */ const jerryx_arg_t mapping[] { jerryx_arg_object_properties (prop_info, JERRYX_ARG_REQUIRED) }; /* Validate and transform. */ const jerry_value_t rv jerryx_arg_transform_args (args_p, args_count, mapping, 1); if (jerry_value_is_exception (rv)) { return rv; } /* required_bool, required_num and optional_num can now be used. */ return jerry_undefined (); }要点prop_info的生命周期必须覆盖jerryx_arg_transform_args调用期间这里不需要jerryx_arg_ignore()因为用的是jerryx_arg_transform_args不含this。示例 3数组元素绑定JS 侧期望args_p[0]是含三个元素的数组boolean、number、可选 numberstatic jerry_value_t my_external_handler (const jerry_value_t function_obj, const jerry_value_t this_val, const jerry_value_t args_p[], const jerry_length_t args_count) { bool required_bool; double required_num; double optional_num 1234.567; // default value /* item_mapping defines the steps to transform array items to C variables. */ const jerryx_arg_t item_mapping[] { jerryx_arg_boolean (required_bool, JERRYX_ARG_COERCE, JERRYX_ARG_REQUIRED), jerryx_arg_number (required_num, JERRYX_ARG_COERCE, JERRYX_ARG_REQUIRED), jerryx_arg_number (optional_num, JERRYX_ARG_COERCE, JERRYX_ARG_OPTIONAL) }; /* Prepare the jerryx_arg_array_items_t instance. */ const jerryx_arg_array_items_t array_info { .c_arg_p item_mapping, .c_arg_cnt 3 }; /* It is the mapping used in the jerryx_arg_transform_args. */ const jerryx_arg_t mapping[] { jerryx_arg_array (array_info, JERRYX_ARG_REQUIRED) }; const jerry_value_t rv jerryx_arg_transform_args (args_p, args_count, mapping, 1); if (jerry_value_is_exception (rv)) { return rv; } /* required_bool, required_num and optional_num can now be used. */ return jerry_undefined (); }数组元素按索引对应item_mapping中的步骤jerryx_arg_array步骤本身会消费一个参数数组其内部再逐个消费数组元素。源码级原理与测试验证变换管线的执行模型所有变换最终收敛到jerryx_arg_transform_args的顺序循环一个共享的jerryx_arg_js_iterator_t在步骤间传递func通过pop/peek/restore自主推进。这种设计带来两个特性步骤可以消费任意数量参数复杂校验如文档所述的处理不同 JS 函数签名、多个输入参数映射到一个 C 结构体通过自定义func即可实现失败即中止任何一步返回 exception循环立即停止之前已写入dest的值保留文档约定校验失败不改 dest但已成功的步骤会留下中间结果例如对象属性示例中部分属性已被写入。单元测试覆盖仓库在 test-ext-arg.c 提供了完整的单元测试覆盖test_validator1_handlerbool/number/string/function 的混合绑定含可选函数参数与NO_COERCE数值严格模式test-ext-arg.ctest_validator2_handlerjerryx_arg_native_pointer校验this的类型 jerryx_arg_custom自定义校验test-ext-arg.ctest_validator_int1/2/3整型的舍入、钳制、NaN 报错边界test-ext-arg.ctest_validator_prop1/2/3直接调用与步骤化两种对象属性变换路径以及属性 getter 抛异常时的错误传播test-ext-arg.ctest_validator_array1/2数组元素变换与类型不匹配失败test-ext-arg.ctest_validator_restore用自定义变换反复调用jerryx_arg_js_iterator_restore验证过度回退不会出错test-ext-arg.c。编译与集成说明jerryx_arg属于 jerry-ext 扩展库使用前需在构建中启用扩展组件jerry-ext目录由顶层 CMakeLists.txt 参与构建并在代码中包含jerryscript-ext/arg.h。头文件通过#include arg.impl.h把整型/数值/布尔等 inline 构造函数直接内联进用户翻译单元见 arg.h因此无需额外链接即可使用所有jerryx_arg_*步骤构造函数变换与迭代器函数则在 arg.c 与 arg-js-iterator-helper.c 中实现。文档示例标注了[doctest]: # (testcompile)可通过仓库的 doctest 工具链tools/gen-doctest.py编译验证。小结jerryx_arg把类型检查 值转换声明化为可组合的变换步骤覆盖了外置函数绑定的绝大部分场景标量number/boolean/string/utf8_string、定宽整型带舍入与钳制策略、函数引用、native pointer、对象属性与数组元素并留有jerryx_arg_custom与迭代器接口处理任意复杂签名。编写新的外置函数处理器时遵循先建映射表 → 调用jerryx_arg_transform_this_and_args/jerryx_arg_transform_args→ 检查返回值是否 exception → 使用已转换的 C 变量这条链路即可获得既严谨又简洁的 JS-C 边界代码。赞分享语言运行时嵌入式物联网编译器【免费下载链接】jerryscriptUltra-lightweight JavaScript engine for the Internet of Things.项目地址https://gitcode.com/gh_mirrors/je/jerryscript点击查看免费下载相关推荐Click 参数类型 ParamType 完全指南输入校验、类型转换与自定义类型实战Click 参数类型 ParamType 完全指南输入校验、类型转换与自定义类型实战 本篇文章聚焦 Python Click 框架中的核心机制 ParamTy人工智能AI 应用AI AgentFastAPI 路径参数完全指南类型转换、数据校验与路径转换器实战path-params 详解FastAPI 路径参数完全指南类型转换、数据校验与路径转换器实战path params 详解 导读 路径参数Path Parameter是任何 We后端Web框架API设计CANN ops-nn 算子 API 数据类型互转换关系详解aclTensor 输出类型转换规则与参数校验机制CANN ops nn 算子 API 数据类型互转换关系详解aclTensor 输出类型转换规则与参数校验机制 导读 在 CANN ops nn 神经网络算子人工智能算子库深度学习CANNAscend上一篇CANN graph-autofusion 测试开发实战指南SuperKernel 与 Autofuse 组件 UT/ST 全流程下一篇RetrofitCache高级技巧自定义缓存拦截器与监听策略创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表