首页 > 其他分享 >嵌入式使用quickjs

嵌入式使用quickjs

时间:2023-11-24 16:12:09浏览次数:40  
标签:quickjs ctx module JS fib so 嵌入式 使用 js

零、前言

  之前搞过在嵌入式中引入Lua作为脚本,以实现动态执行效果。详见(https://www.cnblogs.com/wunaozai/p/14087370.html)但是众所周知原因,其实Lua远远没有JS好,一方面是目前前端的如日中天,加之前端开源的库很多。很多都可以复用。

  在选型用哪个JS引擎时,参考了网上的资料,最终在JerryScript和QuickJS中间选择,最后选择QuickJS作为本次嵌入到C/C++程序中。主要原因是,虽然QuickJS占用的资源会比JerryScript多一些。但是QuickJS比较新,而且支持ES6以上语法,这样,一些NodeJS的库是可以直接用到这里的。性能相比也跟强大一些。

 

一、安装、编译动态库

这一步,没什么好说的,就是从github上下载代码,然后进行编译,我是用mingw进行编译的,说几个注意点

1. 源码中的Makefile需要修改为window编译,打开文件取消 CONFIG_WIN32=y 注释

2. 把DOCS相关的编译去掉,因为系统没有texi2pdf, makeinfo命令来生成文档

3. 如果在编译过程中出现 -ldl 包未找到, 可以到 https://github.com/pepstack/dlfcn-win32 编译一个 libdl.dll,然后放到编译器的lib目录下

 编译后产生以下几个文件,其中libquickjs是静态链接库,用于链接到c环境实现集成。

qjs.exe就是quickjs的运行环境, qjsc.exe就是可以将js文件编译成c源码,然后把c源码编译成exe。

> qjsc -o hello helloworld.js #通过qjsc编译器,直接将js编译成可执行文件

> qjsc -e -o helloworld.c helloworld.js #将js文件编译转成c文件,上面的编译成可执行文件就是通过该代码实现

> qjsc -c -o helloworld.c helloworld.js #将js文件编译成二进制文件,类似java的字节码文件,然后可以跑到jsc虚拟机中即可,在不方便编译成so动态库的情况下,编译成字节码字节码文件也是一种不错的选择

 

 二、测试验证

普通js测试

1 console.log("Hello World");

加载js模块

fib_module.js

1 export function fib(n)
2 {
3     if (n <= 0)
4         return 0;
5     else if (n == 1)
6         return 1;
7     else
8         return fib(n - 1) + fib(n - 2);
9 }

hello_module.js

1 import { fib } from "./fib_module.js";
2 
3 console.log("Hello World");
4 console.log("fib(10)=", fib(10));

qjs hello_module.js

拓展 Externd

拓展,表示通过其他语言,拓展JS的能力。比如,纯JS文件,JS调用C/C++写的module。下面分为两种情况分别在Linux和Windows下如何加载

加载so模块(Linux/Unix)

加载so模块,需要在Linux环境测试、一般嵌入式设备也是Linux环境

将官方示例中的fib.c 手动编译成so动态库

 1 #include "../quickjs.h"
 2 
 3 #define countof(x) (sizeof(x) / sizeof((x)[0]))
 4 
 5 static int fib(int n)
 6 {
 7     if (n <= 0)
 8         return 0;
 9     else if (n == 1)
10         return 1;
11     else
12         return fib(n - 1) + fib(n - 2);
13 }
14 
15 static JSValue js_fib(JSContext *ctx, JSValueConst this_val,
16                       int argc, JSValueConst *argv)
17 {
18     int n, res;
19     if (JS_ToInt32(ctx, &n, argv[0]))
20         return JS_EXCEPTION;
21     res = fib(n);
22     return JS_NewInt32(ctx, res);
23 }
24 
25 static const JSCFunctionListEntry js_fib_funcs[] = {
26     JS_CFUNC_DEF("fib", 1, js_fib ),
27 };
28 
29 static int js_fib_init(JSContext *ctx, JSModuleDef *m)
30 {
31     return JS_SetModuleExportList(ctx, m, js_fib_funcs,
32                                   countof(js_fib_funcs));
33 }
34 
35 #ifdef JS_SHARED_LIBRARY
36 #define JS_INIT_MODULE js_init_module
37 #else
38 #define JS_INIT_MODULE js_init_module_fib
39 #endif
40 
41 JSModuleDef *JS_INIT_MODULE(JSContext *ctx, const char *module_name)
42 {
43     JSModuleDef *m;
44     m = JS_NewCModule(ctx, module_name, js_fib_init);
45     if (!m)
46         return NULL;
47     JS_AddModuleExportList(ctx, m, js_fib_funcs, countof(js_fib_funcs));
48     return m;
49 }

编译so库命令行

1 gcc fib.c -shared -DJS_SHARED_LIBRARY -o fib.so

如果没有加上 -DJS_SHARED_LIBRARY,那么在import引用so库时,会提示 ReferenceError: could not load module filename 'fib.so': js_init_module not found

js加载so库例子

1 /* example of JS module importing a C module */
2 
3 import { fib } from "./fib.so";
4 
5 console.log("Hello World");
6 console.log("fib(10)=", fib(10));

 

加载so模块(Window)

除了Linux环境,有时候仿真需要在Windows上测试,因此需要能支持加载dll模块才行。 可以参考这篇文章。https://zhuanlan.zhihu.com/p/623863082

主要的修改点如下,可以打开quickjs-libc.c 这个文件,搜索 js_module_loader_so函数,就会发现 在 #if defined(_WIN32) 

1 static JSModuleDef *js_module_loader_so(JSContext *ctx,
2                                         const char *module_name)
3 {
4     JS_ThrowReferenceError(ctx, "shared library modules are not supported yet");
5     return NULL;
6 }

需要将上面的代码改成下面的代码,使其能加载so或者dll

 1 static JSModuleDef *js_module_loader_so(JSContext *ctx,
 2                                         const char *module_name)
 3 {
 4     //实现支持window dll动态库
 5     JSModuleDef *m;
 6     void *hd;
 7     JSInitModuleFunc *init;
 8     char *filename;
 9     
10     if (!strchr(module_name, '/')) {
11         /* must add a '/' so that the DLL is not searched in the
12            system library paths */
13         filename = js_malloc(ctx, strlen(module_name) + 2 + 1);
14         if (!filename)
15             return NULL;
16         strcpy(filename, "./");
17         strcpy(filename + 2, module_name);
18     } else {
19         filename = (char *)module_name;
20     }
21     
22     /* C module */
23     hd = LoadLibrary(filename);
24     if (filename != module_name)
25         js_free(ctx, filename);
26     if (!hd) {
27         JS_ThrowReferenceError(ctx, "could not load module filename '%s' as shared library",
28                                module_name);
29         goto fail;
30     }
31 
32     init = (JSInitModuleFunc*)GetProcAddress(hd, "js_init_module");
33     if (!init) {
34         JS_ThrowReferenceError(ctx, "could not load module filename '%s': js_init_module not found",
35                                module_name);
36         goto fail;
37     }
38 
39     m = init(ctx, module_name);
40     if (!m) {
41         JS_ThrowReferenceError(ctx, "could not load module filename '%s': initialization error",
42                                module_name);
43     fail:
44         if (hd)
45             FreeLibrary(hd);
46         return NULL;
47     }
48     return m;
49 }

原理也比较简单,就是Linux和Windows上的动态库操作函数不一样而已。

 1 主要的区别是以下部分:
 2 
 3 Windows使用LoadLibrary函数加载动态库
 4 Windows使用FreeLibrary释放动态库
 5 Windows使用GetProcAddress查找动态库提供的函数
 6 而对于Linux/macos
 7 
 8 unix-like使用dlopen函数加载动态库
 9 unix-like使用dlclose释放动态库
10 unix-like使用dlsym查找动态库提供的函数。

编译成so库

1 gcc fib.c ..\libquickjs.a -shared -DJS_SHARED_LIBRARY -o fib.so

调用so库

1 D:\test\quickjs\examples>..\qjs.exe test_fib.js
2 Hello World
3 fib(10)= 55

 

嵌入 Embed

嵌入,表示通过在其他编程语言中,嵌入JS的能力,使其能执行JS脚本。比如,在C/C++程序中,能执行JS脚本,并能进行交互。

使用文档真不好找,先要试用默认的std、os库,总是提示不存在。

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <stdlib.h>
 4 #include "quickjs-libc.h"
 5 
 6 int main(int argc, char *argv[])
 7 {
 8   printf("HELLO\n");
 9 
10   JSRuntime *rt = JS_NewRuntime();
11   js_std_init_handlers(rt);
12   JSContext *ctx = JS_NewContext(rt);
13   /* system modules */
14   js_init_module_std(ctx, "std");
15   js_init_module_os(ctx, "os");
16   const char *str =
17       "import * as std from 'std';\n"
18       "import * as os from 'os';\n"
19       "globalThis.std = std;\n"
20       "globalThis.os = os;\n"
21       "var console = {};\n"
22       "console.log = value => std.printf(value);\n";
23   JSValue init_compile =
24       JS_Eval(ctx, str, strlen(str), "<input>", JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY);
25 
26   js_module_set_import_meta(ctx, init_compile, 1, 1);
27   JSValue init_run = JS_EvalFunction(ctx, init_compile);
28   
29   const char *test_val = R"(
30     var console = {};
31     console.log = value => globalThis.std.printf(value + "\n");
32     console.log('ok')
33     for(var key in globalThis){
34       console.log("--->" + key)
35     }
36     for(var key in std){
37       console.log("--->" + key)
38     }
39     for(var key in os){
40       console.log("--->" + key)
41     }
42     globalThis.std.printf('hello_world\n');
43     globalThis.std.printf(globalThis + "\n");
44     var a = false;
45     os.setTimeout(()=>{std.printf('AAB\n')}, 2000)
46     globalThis.std.printf(a + "\n");
47   )";
48   JSValue result = JS_Eval(ctx, test_val, strlen(test_val), "test", JS_EVAL_TYPE_MODULE);
49   printf("loop\n");
50   js_std_loop(ctx);
51 
52   int32_t len;
53   int to_int_32;
54   if ((to_int_32 = JS_ToInt32(ctx, &len, result)) != 0) {
55     js_std_dump_error(ctx);
56     
57   }
58 
59   JS_FreeContext(ctx);
60   JS_FreeRuntime(rt);
61   return 0;
62 }

编译

1 Linux:  gcc test.c -o main  -L . -I . -lm -lquickjs -ldl -lpthread
2 Window: gcc test.c libquickjs.a -o main.exe -I . 

 

参考资料:

  https://github.com/quickjs-zh/QuickJS/

  https://github.com/saghul/txiki.js/

  https://github.com/bellard/quickjs

  https://bellard.org/quickjs/quickjs.html#Quick-start

  https://zhuanlan.zhihu.com/p/379697068

  https://zhuanlan.zhihu.com/p/382296206

  https://fuchsia.googlesource.com/third_party/quickjs/+/refs/heads/main/basic_test.cc

   libdl.dll 库 https://files.cnblogs.com/files/wunaozai/libdl.zip

本文地址:https://www.cnblogs.com/wunaozai/p/17850789.html

系列目录:https://www.cnblogs.com/wunaozai/p/17853962.html

标签:quickjs,ctx,module,JS,fib,so,嵌入式,使用,js
From: https://www.cnblogs.com/wunaozai/p/17850789.html

相关文章

  • quickjs入门学习
    由于最近在学习quickjs,把学习过程中遇到的问题和功能验证的过程都记录下来,这篇是quickjs入门学习的目录导航。  本文地址:https://www.cnblogs.com/wunaozai/p/17853962.html......
  • pyinstaller利用spec文件打包的使用模板
    pyinstaller打包使用pyqt5开发软件,当项目越来越大,引用的资源越来越多时,那么使用pyinstaller进行打包,如果不利用spec文件,是很难满足打包需求的。spec文件,其实你在使用pyinstallermain.py打包时,也是会自动生成的,叫main.spec。不过,如果你想把自己的资源文件一起打进包去,则需......
  • MinIO的简单使用
    MINIO介绍什么是对象存储?以阿里云OSS为例:对象存储服务OSS(ObjectStorageService)是一种海量、安全、低成本、高可靠的云存储服务,适合存放任意类型的文件。容量和处理能力弹性扩展,多种存储类型供选择,全面优化存储成本。优势就在于它可以存储大容量的非结构化数据。缺点:没有选......
  • gitlab runner 使用摘要
    参考文档GitLabRunnerrunstheCI/CDjobsthataredefinedinGitLabGitLabRunnerisopen-sourceandwritteninGo.Itcanrunasasinglebinaryandhasnolanguage-specificrequirements.AfteryouinstallGitLabRunner,youmustcreateandregisterrunne......
  • Gitlab Docker 使用摘要
    官方文档:https://docs.gitlab.com/ee/install/docker.html#expose-gitlab-on-different-ports设置本地目录设置环境变量GITLAB_HOMEexportGITLAB_HOME=/srv/gitlab安装启动脚本start.sh#!/bin/bashexportGITLAB_HOME=/srv/gitlabsudodockerrun--detach......
  • 一文讲明Mybatis 的使用 超详细 【爆肝两万字教程】
    >我|在这里>......
  • 使用CloudStats监控Linux服务器
    CloudStats是一个服务器监控平台,可让您轻松地监控整个服务器基础平台,同时也可以立即采取行动并解决问题。CloudStats监控工具不需要任何特殊技能或知识来执行设置并开始监控您的服务器。因为是国外的服务器,打开有点慢!在服务器( /Debian/Ubuntu/Fedora等)上安装CloudStats......
  • Java中使用try-with-resources
    Java7中引入的对资源 try-with-resources ,声明在 try 块中使用的资源,并保证资源将在该块执行后关闭。声明的资源需要实现自动关闭接口。 1.使用资源try典型的try-catch-finally块:Scannerscanner=null;try{scanner=newScanner(newFile("test.txt"));......
  • 使用golang写一个导出excel的接口
    Craftedby[Genie](https://marketplace.visualstudio.com/items?itemName=genieai.chatgpt-vscode)You使用golang写一个导出excel的接口Genie要创建一个使用Go语言编写的导出Excel文件的接口,我们需要做几个步骤:安装依赖:我们将使用excelize库来创建和操作Excel文件。......
  • How to use 微PE to install WinOS. 怎么使用微PE安装系统.(no USB drive)
    1.Download微PEInstallit.https://www.wepe.com.cn/download.html 2.Rebootyoursystem,Select微PEtostart. 3. OpenCGI备份还原  .Tips.Step3,chooseyoursystem's partition.Don'tmistake.Mindit'ssize.Forexample,my......