news 2026/9/16 16:20:38

Momentum Firmware JS 引擎数据类型详解:mJS 的 string、number、foreign、ArrayBuffer 与 DataView 全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Momentum Firmware JS 引擎数据类型详解:mJS 的 string、number、foreign、ArrayBuffer 与 DataView 全解析

Momentum Firmware JS 引擎数据类型详解:mJS 的 string、number、foreign、ArrayBuffer 与 DataView 全解析

【免费下载链接】Momentum-Firmware🐬 Feature-rich, stable and customizable Flipper Firmware项目地址: https://gitcode.com/GitHub_Trending/mo/Momentum-Firmware

mJS 是内置于 Momentum Firmware(基于 Flipper Zero 的固件)中的轻量级 JavaScript 引擎,用于运行用户编写的 JS 脚本应用(.js)。本文以 documentation/js/js_data_types.md 为核心,逐一剖析 mJS 支持的 10 类数据类型,并结合 lib/mjs 下的引擎源码与 applications/system/js_app/examples/apps/Scripts 中的真实示例,讲清每种类型的语义、底层表示与实战用法。读完本文,你将能准确判断在固件脚本中何时用 Object、何时用 Array、如何用 ArrayBuffer/DataView 做二进制数据处理,并理解 foreign 指针类型在 JS 与 C 边界间的桥梁作用。

一、mJS 数据类型总览

原文档给出的 mJS 常用数据类型清单如下:

数据类型说明
string单字节字符序列,不支持 UTF8
number数值
boolean布尔值
foreignC 函数或数据指针
undefined未定义值
null空值
Object带命名字段的数据结构
Array特殊类型对象,所有项都有索引且类型相等
ArrayBuffer原始数据缓冲区
DataView提供访问 ArrayBuffer 内容的接口

从引擎内部视角看,这些类型被划分为"原始类型"与"Object 的类别"两大类。在 lib/mjs/mjs_core_public.h 的enum mjs_type中可以看到引擎对类型的官方分组:

  • 原始类型(Primitive types):MJS_TYPE_UNDEFINEDMJS_TYPE_NULLMJS_TYPE_BOOLEANMJS_TYPE_NUMBERMJS_TYPE_STRINGMJS_TYPE_FOREIGNMJS_TYPE_ARRAY_BUFMJS_TYPE_ARRAY_BUF_VIEW
  • Object 的不同类别:MJS_TYPE_OBJECT_GENERIC(普通对象)、MJS_TYPE_OBJECT_ARRAY(数组)、MJS_TYPE_OBJECT_FUNCTION(函数)

值得注意:ArrayBufferDataView在枚举中归属于原始类型分组,而ArrayFunction属于 Object 类别——这与 ECMAScript 中"函数和数组都是对象"的认知一致,也解释了为何 mJS 中Array的每一项都要求拥有索引、且类型相等。

二、string:单字节字符序列(无 UTF-8)

mJS 的字符串是单字节字符序列,每个字符恰好占 1 个字节,不提供 UTF-8 多字节编码支持。这意味着中文等多字节字符无法被 mJS 字符串直接正确表示,开发脚本时应避免在字符串中嵌入非 ASCII 字符。

引擎内部为字符串实现了精细的分级存储策略。在 lib/mjs/mjs_core_public.h 中定义了多种字符串标签:

  • MJS_TAG_STRING_I:内联字符串,长度 < 5 字节,直接嵌入值中,零额外分配
  • MJS_TAG_STRING_5:内联字符串,长度恰好 5 字节
  • MJS_TAG_STRING_O:自有字符串(owned string),引擎持有其内存所有权
  • MJS_TAG_STRING_F:外来字符串(foreign string),指向外部数据,不拷贝
  • MJS_TAG_STRING_C:字符串分片(chunk)
  • MJS_TAG_STRING_D:字典字符串(dictionary string),用于属性名等可共享的短串

这种"短串内联、长串外存"的设计(对应 lib/mjs/mjs_string.h 中embed_string等实现)是 mJS 面向内存受限嵌入式环境的核心优化:5 字节以内的短字符串不产生堆分配,避免频繁触发垃圾回收。

mJS 为字符串提供了toLowerCasetoUpperCasesliceindexOfcharCodeAt等方法(见 lib/mjs/mjs_string.h),日常脚本处理文本足够使用。

三、number:IEEE 754 双精度浮点与 NaN-packing

mJS 的number遵循 IEEE 754 双精度浮点格式(8 字节):1 位符号位、11 位指数、52 位尾数。与标准 JavaScript 一致,NaNInfinity等特殊值同样存在。

mJS 在底层实现上采用了著名的NaN-packing技术:既然NaN的指数位全为 1,mJS 就借用NaN的载荷区来"打包"所有 JS 值。在 lib/mjs/mjs_core_public.h 中有完整说明:

11111111|1111tttt|vvvvvvvv|vvvvvvvv|vvvvvvvv|vvvvvvvv|vvvvvvvv|vvvvvvvv NaN marker |type| 48-bit placeholder for values: pointers, strings
  • 前 12 位固定为0xfff(NaN 标记)
  • 4 位type标签,标识值的具体类型
  • 低 48 位存放实际载荷(指针、字符串偏移等)

typedef uint64_t mjs_val_t;定义了引擎中所有 JS 值的统一表示(lib/mjs/mjs_core_public.h)。由于 64 位平台上指针实际只有 48 位有效地址空间,指针可以安全塞入载荷区。标签定义见 lib/mjs/mjs_core_public.h,共使用 32 种可能标签中的一部分,例如:

  • MJS_TAG_OBJECTMJS_TAG_FOREIGNMJS_TAG_UNDEFINEDMJS_TAG_BOOLEAN
  • MJS_TAG_ARRAYMJS_TAG_FUNCTIONMJS_TAG_NULL
  • MJS_TAG_ARRAY_BUF(ArrayBuffer)、MJS_TAG_ARRAY_BUF_VIEW(DataView)

C 侧创建与读取数字的 API 为mjs_mk_number()mjs_get_double()mjs_get_int()mjs_get_int32()(见 lib/mjs/mjs_primitive_public.h)。其中mjs_get_int()会丢弃小数部分,而mjs_get_int32()保证返回 32 位有符号整数——在固件脚本中做位运算或与硬件寄存器打交道时,建议使用后者语义一致的写法。

四、boolean、undefined 与 null

这三个是 mJS 的基础标量类型,语义与标准 JavaScript 一致:

  • boolean:只有truefalse两个值。C 侧通过mjs_mk_boolean()创建、mjs_get_bool()读取、mjs_is_boolean()判断(lib/mjs/mjs_primitive_public.h)。
  • undefined:表示"未定义",通常是未初始化变量或函数未返回值。对应宏MJS_UNDEFINED(lib/mjs/mjs_primitive_public.h)。
  • null:表示"空值",对应宏MJS_NULL。注意 mJS 保留了mjs_mk_null()兼容接口,但官方注释已标记其废弃,推荐直接使用MJS_NULL宏(lib/mjs/mjs_primitive_public.h)。

在 ArrayBuffer/DataView 的读写边界上,undefined还被用作"越界哨兵":例如 DataView 按索引取值越界时,mjs_dataview_get()会返回MJS_UNDEFINED(见 lib/mjs/mjs_array_buf.c),这与标准 JS 中越界读取返回undefined的行为保持一致。

五、foreign:C 函数与数据指针的桥接

foreign是 mJS 中最具嵌入式特色的类型:它表示一个C 函数指针或 C 数据指针。其创建 API 有两个:

  • mjs_mk_foreign(mjs, ptr):打包数据指针(lib/mjs/mjs_primitive_public.h)
  • mjs_mk_foreign_func(mjs, fn):打包函数指针(lib/mjs/mjs_primitive_public.h),宏MJS_MK_FN(fn)是其便捷写法

读取侧对应mjs_get_ptr(),判断侧为mjs_is_foreign()(lib/mjs/mjs_primitive_public.h)。

原文档明确指出 foreign 的语义边界:JS 代码不能对 foreign 值做任何有用的操作,只能在属性中持有它并传来传去,它表现得像一个没有属性的密封对象。这正是 foreign 的设计初衷——它纯粹是 C 侧注册的 JS 内置函数(如printdelay等)在引擎内部的载体,JS 脚本一般不会直接接触到 foreign 值。

源码中还给出了一个重要的存储告诫:由于 foreign 依赖 48 位地址空间,在需要存放正好sizeof(void*)字节、且sizeof(void*) >= 8的原始数据时,不要用 foreign,请改用字节数组(ArrayBuffer)(lib/mjs/mjs_primitive_public.h)。这条建议直接指向下一节的主角。

六、Object 与 Array:数据结构的两类载体

  • Object:带命名字段的数据结构,对应MJS_TYPE_OBJECT_GENERIC。脚本中用字面量{a: 1, b: "x"}创建,C 侧用mjs_mk_object()mjs_set()mjs_get()操作。字段名通常走字典字符串(MJS_TAG_STRING_D)路径,重复出现的属性名可共享同一份存储。
  • ArrayMJS_TYPE_OBJECT_ARRAY。原文档强调其特殊性——所有项都有索引,且类型相等。这意味着 mJS 的数组在实现上比标准 JS 数组更严格、也更紧凑,适合存放同构数据序列(例如一组温度读数、一组 RGB 值)。C 侧 API 见 lib/mjs/mjs_array.h 与mjs_array_public.h,支持mjs_array_getmjs_array_push_internalpush)、mjs_array_splicesplice)等常规操作。

需要留意:正因数组要求"元素类型相等",混入不同类型元素的写法在 mJS 中可能触发类型错误,编写脚本时应尽量保证数组元素的同质性。

七、ArrayBuffer:原始二进制缓冲区

ArrayBuffer是 mJS 提供的原始数据缓冲区,对应标签MJS_TAG_ARRAY_BUF(lib/mjs/mjs_core_public.h),类型为MJS_TYPE_ARRAY_BUF。它不与任何具体数据类型绑定,只是若干字节的连续内存。

在引擎内部,所有 ArrayBuffer 的数据存放在一个统一管理的mbuf(动态字节缓冲)中(mjs->array_buffers),每个缓冲区前有一个 varint 编码的长度头。创建与读取的 C API 为mjs_mk_array_buf()mjs_array_buf_get_ptr()(lib/mjs/mjs_array_buf_public.h)。为避免频繁扩容,每次分配还会多预留MJS_ARRAY_BUF_RESERVE(默认 100)字节(lib/mjs/mjs_array_buf.c)。

mJS 为 ArrayBuffer 提供了slice(start, end)方法:校验参数个数(0~2 个)、起始/结束位置合法性后,从源缓冲区切出一段并返回新的 ArrayBuffer(实现见 lib/mjs/mjs_array_buf.c)。仓库自带的示例脚本完整演示了这一用法:

let arr_1 = Uint8Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); print("len =", arr_1.buffer.byteLength); let arr_2 = Uint8Array(arr_1.buffer.slice(2, 6)); print("slice len =", arr_2.buffer.byteLength); for (let i = 0; i < arr_2.buffer.byteLength; i++) { print(arr_2[i]); }

(完整文件见 array_buf_test.js)

八、DataView:类型化视图与类型化数组

DataView(标签MJS_TAG_ARRAY_BUF_VIEW)提供访问 ArrayBuffer 内容的类型化接口,对应原文档所述"provides interface for accessing ArrayBuffer contents"。通过它可以把同一段缓冲区解释为不同宽度的整数序列。

引擎内置了 6 种元素类型,定义在 lib/mjs/mjs_array_buf_public.h 的mjs_dataview_type_t中:

类型元素宽度说明
MJS_DATAVIEW_U81 字节无符号 8 位整数
MJS_DATAVIEW_I81 字节有符号 8 位整数
MJS_DATAVIEW_U162 字节无符号 16 位整数
MJS_DATAVIEW_I162 字节有符号 16 位整数
MJS_DATAVIEW_U324 字节无符号 32 位整数
MJS_DATAVIEW_I324 字节有符号 32 位整数

元素宽度映射在mjs_dataview_get_element_len()中实现(lib/mjs/mjs_array_buf.c)。这 6 种视图在 JS 全局作用域下对应构造器:Uint8ArrayInt8ArrayUint16ArrayInt16ArrayUint32ArrayInt32Array,注册于mjs_init_builtin_array_buf()(lib/mjs/mjs_array_buf.c)。

DataView构造器支持三种入参形式(见 lib/mjs/mjs_array_buf.c):

  1. 传入现有 ArrayBuffer:在已有缓冲区上建立视图(mjs_mk_dataview_from_buf);
  2. 传入数字长度:自动分配等长的新 ArrayBuffer;
  3. 传入普通数组:按元素类型逐个拷贝数组元素到新缓冲区。

读取与写入分别由mjs_dataview_get()/mjs_dataview_set()完成,且二者都会做越界检查——按索引读取越界返回undefined,写入越界则返回MJS_TYPE_ERROR(lib/mjs/mjs_array_buf.c)。视图内部通过_t字段记录元素类型、buffer字段引用底层 ArrayBuffer(lib/mjs/mjs_array_buf.c)。此外,从 ArrayBuffer 建立视图时要求缓冲区长度必须是元素宽度的整数倍,否则报MJS_BAD_ARGS_ERROR

九、在 Flipper 脚本中的实战模式

官方文档在 documentation/js/ReadMe.md 中特别提示:documentation/js目录下的说明为手工维护,可能存在滞后,权威依据是 TypeScript 类型定义(applications/system/js_app/packages/fz-sdk下的.d.ts文件)与applications/system/js_app/examples/apps/Scripts下的示例脚本。结合示例,可以看到 ArrayBuffer/Uint8Array 在固件脚本中的三类典型用途:

1. 外设收发缓冲(i2c/spi)在 i2c.js 与 spi.js 中,读写数据均以Uint8Array(data_buf)形式构造缓冲,注释明确写道"也可以直接传Uint8Array([0x00, 0x00, ...])作为写参数",即字节数组既是写入参数也是读取结果的载体。

2. 串口数据视图(uart)uart_echo.js 中let data_view = Uint8Array(rx_data);将收到的原始字节流包装为类型化视图,再逐元素访问——这正是 DataView 语义(读写 ArrayBuffer 内容的接口)的直接体现。

3. BLE 广播包构造(blebeacon)blebeacon.js 中blebeacon.setData(Uint8Array(packet))Uint8Array打包广播数据,对应类型定义 blebeacon/index.d.ts 中setData(data: Uint8Array)的签名。此外 gui.js 中defaultData: Uint8Array([0x11, 0x22, ...])表明 GUI 组件的字节属性同样接受类型化数组。

4. 存储 API 的缓冲参数在 C 侧模块 js_storage.c 中,参数校验信息显示存储接口的写参数"expected string or ArrayBuffer"——即ArrayBufferstring一样可作为文件写入的数据来源,这说明 ArrayBuffer 已深度融入固件 JS API 的参数体系。

十、总结

mJS 的 10 类数据类型构成了 Flipper 脚本世界的完整类型体系:string(单字节、无 UTF-8)与number(IEEE 754 双精度)处理基础数据,boolean/undefined/null表达逻辑与空值,foreign在 JS 与 C 之间传递指针,Object/Array组织结构化数据(数组要求元素类型相等),而ArrayBuffer+DataView组合则提供了面向外设通信的原始二进制处理能力。理解这套类型系统,尤其是"NaN-packing 统一表示所有值""字符串短串内联""ArrayBuffer 集中缓冲管理"三个底层设计,将帮助你在 Momentum Firmware 上写出更高效、更符合引擎预期的 JS 应用。

进一步阅读:引擎源码位于 lib/mjs,类型化数组完整实现见 mjs_array_buf.c 与 mjs_array_buf_public.h;脚本示例见 examples/apps/Scripts/Examples;权威类型声明见 packages/fz-sdk。

【免费下载链接】Momentum-Firmware🐬 Feature-rich, stable and customizable Flipper Firmware项目地址: https://gitcode.com/GitHub_Trending/mo/Momentum-Firmware

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/16 16:20:28

Django实现视频点播:MTV架构、ORM模型与部署实战

简介&#xff1a;基于Django框架的视频点播网站完整源码包&#xff0c;主要面向计算机、数学、电子信息等专业的学生&#xff0c;适合用作课程设计、期末大作业或毕业设计参考资料。项目实现了视频播放、收藏、后台管理等核心功能&#xff0c;页面与后端逻辑均打包在内&#xf…

作者头像 李华
网站建设 2026/9/16 16:18:33

基于多智能体大语言模型的中文金融分析框架解析

1. 项目背景与核心价值这个名为TradingAgents-CN的开源项目&#xff0c;本质上是一个基于多智能体大语言模型的中文金融分析框架。它最吸引人的地方在于&#xff0c;将前沿的AI技术与传统金融分析进行了深度融合&#xff0c;打造了一个专门面向中文市场的股票研究平台。从技术架…

作者头像 李华
网站建设 2026/9/16 16:14:57

STM32L496低功耗MCU上实现mbedtls TLS 1.2实战指南

简介&#xff1a;本资源是一套面向嵌入式开发者的STM32L4系列低功耗MCU实战工程&#xff0c;聚焦RT-Thread操作系统下基于mbedtls的TLS安全通信实现&#xff0c;适用于物联网终端设备安全接入、工业数据加密传输等典型场景&#xff0c;适合具备RTOS基础与C语言开发经验的中级以…

作者头像 李华
网站建设 2026/9/16 16:14:53

基于STM32的超声波测厚仪:从脉冲回波原理到工程落地

简介&#xff1a;基于STM32单片机设计的超声波测厚仪完整解决方案&#xff0c;面向嵌入式开发者、电子竞赛参赛者及测控类课程设计人员&#xff0c;解决在1.2mm&#xff5e;225mm范围内对物体厚度的非接触式精准测量问题&#xff0c;设计误差控制在(1%H0.1)mm以内。资源共44个文…

作者头像 李华