JS(JavaScript)是一种轻量级的面向对象的编程语言,目前使用非常广泛,既能在浏览器中使用,也能在服务器端作为后台(借助node.s),并且由于其是解释型语言,不需要提前编译,所以它就有了一个特别大的优势,它支持热更新(可以实现极其轻量级的OTA),所以这些年在嵌入式Linux中,有很多场景也开始引入了JS。不过JS也有缺点,因为是解释性语言,所以执行效率相比二进制编译语言,比如C,相对低。本文主要介绍借助QuickJS来体验一下再C程序代码中如何调用JS代码。
QuickJS是一种在嵌入式平台上应用比较广泛的JS引擎。
wget https://bellard.org/quickjs/quickjs-2024-01-13.tar.xz
tar -xJf quickjs-2024-01-13.tar.xz
sudo make
......
sudo make install
安装成功后,会在/usr/local/lib/quickjs 目录下安装libquickjs.a 文件,在/usr/local/include/quickjs下包含其头文件。
// calculator.js 文件内容
function calculate(a, b, operation) {
switch(operation) {
case '+': return a + b;
case '-': return a - b;
case '*': return a * b;
case '/': return a / b || 'Error: Division by zero'; // 注意,这里做了除数为零的判断
default: return 'Error: Invalid operation';
}
}
test.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <quickjs/quickjs-libc.h>
int main(int argc, char *argv[])
{
JSRuntime* rt;
JSContext* ctx;
JSValue global_obj, script, result;
// 初始化QuickJS运行时和上下文
rt = JS_NewRuntime();
ctx = JS_NewContext(rt);
// 注册全局对象
global_obj = JS_GetGlobalObject(ctx);
// 读取并执行JavaScript脚本文件
FILE *fp = fopen("calculator.js", "r");
if (!fp) {
fprintf(stderr, "Failed to open script file: calculator.js\n");
exit(EXIT_FAILURE);
}
fseek(fp, 0, SEEK_END);
long script_size = ftell(fp);
fseek(fp, 0, SEEK_SET);
char *script_text = malloc(script_size + 1);
fread(script_text, script_size, 1, fp);
script_text[script_size] = '\0'; // 添加结束符
fclose(fp);
script = JS_Eval(ctx, script_text, script_size, "calculator.js", JS_EVAL_TYPE_GLOBAL);
if (JS_IsException(script)) {
fprintf(stderr, "Error evaluating JavaScript function from calculator.js: %s\n", JS_ToCString(ctx, script));
goto fail;
}
// 创建JavaScript函数参数
//double num1 = 10, num2 = 2;
double num1 = atof(argv[1]);
double num2 = atof(argv[3]);
///const char* op = "+"; // 这里可以更换为加减乘除符号
const char* op = argv[2];
JSValue arg1 = JS_NewFloat64(ctx, num1);
JSValue arg2 = JS_NewFloat64(ctx, num2);
JSValue operation = JS_NewString(ctx, op);
// 创建JavaScript函数调用
JSValue calculator_fn;
calculator_fn = JS_GetPropertyStr(ctx, global_obj, "calculate"); // 获取calculate函数
if (JS_IsFunction(ctx, calculator_fn)) {
JSValue argv[] = {arg1, arg2, operation};
result = JS_Call(ctx, calculator_fn, JS_UNDEFINED, 3, argv); // 调用函数
if (JS_IsException(result)) {
printf("Error executing JavaScript function\n");
} else {
double calculated_result;
if (JS_ToFloat64(ctx, &calculated_result, result) < 0) {
printf("Conversion to float64 failed\n");
} else {
printf("Result: %.2f\n", calculated_result);
}
}
} else {
printf("calculate function not found or is not a function\n");
}
// 清理
fail:
JS_FreeValue(ctx, result);
JS_FreeValue(ctx, calculator_fn);
JS_FreeValue(ctx, operation);
JS_FreeValue(ctx, arg2);
JS_FreeValue(ctx, arg1);
JS_FreeValue(ctx, global_obj);
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
free(script_text);
return 0;
}
编译构建
gcc -o test test.c -L/usr/local/lib/quickjs -lquickjs -lpthread -lm
特别注意:因为quickJS引用了数学相关的C库,所以我们需要添加-lm,否则会宝很多错。
demo执行结果:
/opt/test/quickJs-test$ ./test 12 + 34
Result: 46.00
本文介绍了在ubuntu平台上,使用C代码调用JS脚本的demo流程,对于嵌入式工程师来说,js提供了一种新的思路,我们不一定非要OTA来升级程序,也可以通过JS的热更新机制来实现程序的升级,当然,这个前提是JS能够满足使用场景,比如执行效率,内存等。