1. QuickJS 快速入门 (QuickJS QuickStart)

1.1. 简介

QuickJS是一个小型的可嵌入Javascript引擎。它支持ES2020规范,包括模块、异步生成器和代理。它还支持数学扩展,比如大整数(BigInt)、大浮点数(BigFloat)和操作符重载。

1.2. 安装

  • Linux 直接下载 源码
make && make install
  • MacOS X 下 makefile 有 Bug ,可以直接使用 homebrew 安装
brew install quickjs
  • 执行 qjs 验证安装成功

1.3. 简单使用

1.3.1. 控制台执行

qjs 进入quickjs环境,-h 获取帮助,-q 退出环境。
直接执行js:

console.log(new Date())

输出:Wed Aug 14 2019 23:51:43 GMT+0800
   undefined

(function(){ return 1+1;})()

输出:2

1.3.2. js脚本执行

新建一个js脚本,名为hello.js,内容为console.log('hello world !'), 在js目录下执行

qjs hello.js

输出:hello world !

1.3.3. 直接编译成二进制文件执行

quickjs.hquickjs-libc.hlibquickjs.a拷贝到js文件同目录下。

qjsc -o hello hello.js
ls
./hello

输出:hello world !
编译出来的可执行文件的大小只有569K(2019-9-18版本为900K),没有任何外部依赖,非常适合嵌入式设备使用。

1.4. 全局对象

  • scriptArgs 输入的命令行参数,第一个参数为脚本的名称。
  • print(...args)console.log(...args)打印由空格和尾随换行符分隔的参数。

新建js脚本globle_obj.js

(function(){if(typeof scriptArgs != 'undefined'){print(scriptArgs);console.log(scriptArgs[1]);}
})()
qjs globle_obj.js -a 123 1234

输出:
globle_obj.js,-a,123,1234
-a

1.5. std 模块

std模块为quickjs-libc提供包装器stdlib.hstdio.h和其他一些实用程序。

1.5.1. 常见的导出函数

  • exit(n) 退出进程。
  • evalScript(str) 将字符串str以脚本方式运行(全局eval)。
  • loadScript(filename) 将文件filename以脚本方式运行(全局eval)。
  • Error(errno)std.Error构造函数。错误实例包含字段errno(错误代码)和messagestd.Error.strerror(errno)的结果)。
    构造函数包含以下字段:EINVALEIOEACCESEEXISTENOSPCENOSYSEBUSYENOENTEPERMEPIPE 常见错误的整数值 (可以定义附加错误代码)。
  • strerror(errno) 返回描述错误的字符串errno
  • open(filename, flags) 打开一个文件(libc的包装器fopen())。在I/O错误时抛出 std.Error
  • tmpfile() 打开一个临时文件。在I/O错误时抛出std.Error
  • puts(str) 相当于std.out.puts(str)
  • printf(fmt, ...args) 相当于std.out.printf(fmt, ...args)sprintf(fmt, ...args) 相当于libc的sprintf()。
  • inouterr 包装libc文件的stdinstdoutstderr
  • SEEK_SETSEEK_CURSEEK_ENDseek() 的常量。
  • global 引用全局对象。
  • gc() 手动调用循环删除算法。循环移除算法在需要时自动启动,因此该功能在特定内存限制或测试时非常有用。
  • getenv(name) 返回环境变量的值 name ,或未定义时返回 undefined .

1.5.2. FILE 原型

  • close() 关闭文件。
  • puts(str) 使用UTF-8编码输出字符串。
  • printf(fmt, ...args) 格式化printf,与libc printf格式相同。
  • flush() 刷新缓冲的文件。
  • seek(offset, whence) 寻找特定文件位置 (从哪里std.SEEK_*)。在I/O错误时抛出 std.Error
  • tell() 返回当前文件位置。
  • eof() 如果文件结束,则返回true。
  • fileno() 返回关联的OS句柄。
  • read(buffer, position, length)(封装 libc fread)。
  • write(buffer, position, length) (封装 libc fread)。
  • getline() 返回文件中的下一行,假设为UTF-8编码,不包括尾随换行符。
  • getByte() 返回文件中的下一个字节。
  • putByte(c) 将一个字节写入文件。

1.5.3. std代码示例

创建文件std_m.js

import * as std from 'std';
var file = std.open('std_open_file.js','w');
file.puts('var file = std.open(\"std_open_file.txt\",\"w\");\n');
file.puts('file.puts(\'std_open_file line1\\n\');\n');
file.puts('file.puts(\'std_open_file line2\\n\');\n');
file.puts('file.close();\n');
file.close();
std.loadScript('std_open_file.js');
var rdfile = std.open("std_open_file.txt","r");
do{console.log(rdfile.getline());
}while(!rdfile.eof());
rdfile.close();

执行qjs std_m.js ,目录下会生成2个新文件std_open_file.js std_open_file.txt
控制台输出:
std_open_file line1
std_open_file line2
null

1.6. os 模块

os 模块提供操作系统特定功能:底层文件访问、信号、计时器、异步 I/O。
如果是OK,OS函数通常返回0,或者OS返回特定的错误代码。

可用导出函数:

  • open(filename, flags, mode = 0o666) 打开一个文件。如果错误,返回句柄或<0。 O_RDONLYO_WRONLYO_RDWRO_APPENDO_CREATO_EXCLO_TRUNC POSIX打开标志。O_TEXT
    (Windows特定)。以文本模式打开文件。默认为二进制模式。
  • close(fd) 关闭文件句柄fd
  • seek(fd, offset, whence) 寻找文件。使用 std.SEEK_*whence
  • read(fd, buffer, offset, length) 读取指定长度的字节码。返回读取成功的字节码长度,失败返回 <0;
  • write(fd, buffer, offset, length) 写入字节码,失败返回<0。
  • isatty(fd) fd 是一个TTY (终端)句柄返true
  • ttyGetWinSize(fd) 返回TTY大小 [width, height] 或者如果不可用返回 null
  • ttySetRaw(fd) 在原始模式下设置TTY。
  • remove(filename) 删除文件。如果正常则返回0,如果错误则返回<0。
  • rename(oldname, newname) 重命名文件。如果正常则返回0,如果错误则返回<0。
  • setReadHandler(fd, func) 将读处理程序添加到文件句柄fdfd每次有数据待增加处理时调用func 。支持每个文件句柄的单个读处理程序。使用 func = null 来删除句柄。
  • setWriteHandler(fd, func) 将写处理程序添加到文件句柄fdfd每次有数据待写入处理时调用func . 支持每个文件句柄一个写处理程序。使用 `func = null来删除句柄。
  • signal(signal, func) 当信号 signal 发生时调用 func 。 每个信号编号只支持一个处理程序。使用 null 设定的默认处理或 undefined 忽略的信号。
    SIGINT
    SIGABRT
    SIGFPE
    SIGILL
    SIGSEGV
    SIGTERM
    POSIX 信号编号。
  • setTimeout(func, delay)delay 毫秒之后调用函数 func 。返回计时器的句柄。
  • clearTimer(handle)取消计时器。
  • platform 返回表示该平台的字符串: "linux", "darwin", "win32" or "js"
import * as os from 'os';
os.remove('hello');
os.remove('std_open_file.js');
os.remove('std_open_file.txt');

删除生成的测试文件

1.7. 自定义C模块

ES6模块完全支持。默认名称解析规则如下:

  • 模块名称带有前导.或…是相对于当前模块的路径
  • 模块名称没有前导.或…是系统模块,例如std或os
  • 模块名称以.so结尾,是使用QuickJS C API的原生模块

使用js文件模块和系统模块,参照引用原生js模块和上面的例子即可,这里就不多赘述。
这里着重讲解如何编写自己的原生C模块,并且以导入so文件的方式在js代码中使用。

1.7.1. js数据类型在C中的定义

typedef union JSValueUnion {int32_t int32;          //整数值double float64;         //double值void *ptr;              //QuickJS引用类型的指针
} JSValueUnion;             //存放于同一地址,且互斥typedef struct JSValue {JSValueUnion u;         //存放真实数值或着其指针int64_t tag;            //JSValue类型的标示符(如 undefined 其 tag == JS_TAG_UNDEFINED)
} JSValue;

此结构定义在 quickjs.h 中。

1.7.2. c模块编写

流程如下:

  1. 自定义原生C函数
  2. 定义 QuickJS C 函数
  3. 定义API的函数入口名称及列表
  4. 定义初始化回调方法,将函数入口列表在模块中暴露
  5. 定义初始化模块方法,由系统自动调用,且函数名称不可更改

创建编写c_test_m.c文件:

// #include "quickjs.h"
// #include "stdio.h"
// #include "stdlib.h"
// #include "string.h"// #define JS_INIT_MODULE js_init_module
//#define countof(x) (sizeof(x) / sizeof((x)[0]))/* 自定义原生C函数 */
static double test_add(int a, double b)
{return a + b;
}static char *test_add_str(const char *a, double b)
{/* 要有足够的空间来容纳要拼接的字符串,否则可能会造成缓冲溢出的错误情况 */char instr[64];sprintf(instr, "%.2f", b);char *dest = malloc(128);memset(dest, 0, 128);strcpy(dest, a);char *retdest = strcat(dest, instr);return dest;
}/* 定义 QuickJS C 函数 *ctx     : 运行时上下文this_val : this对象argc     : 入参个数*argv    : 入参列表
*/
static JSValue js_test_add(JSContext *ctx, JSValueConst this_val,int argc, JSValueConst *argv)
{int a;double b;if (JS_ToInt32(ctx, &a, argv[0]))return JS_EXCEPTION;if (JS_ToFloat64(ctx, &b, argv[1]))return JS_EXCEPTION;printf("argc = %d \n", argc);printf("a = %d \n", a);printf("b = %lf \n", b);printf("argv[1].u.float64 = %lf \n", argv[1].u.float64);return JS_NewFloat64(ctx, test_add(a, b));
}static JSValue js_test_add_str(JSContext *ctx, JSValueConst this_val,int argc, JSValueConst *argv)
{if (!JS_IsString(argv[0])){return JS_EXCEPTION;}double d;if (JS_ToFloat64(ctx, &d, argv[1]))return JS_EXCEPTION;const char *jscstr = JS_ToCString(ctx, argv[0]);printf("JS_ToCString(ctx, argv[0]) = %s \n", jscstr);printf("argv[1].u.float64 = %lf \n", argv[1].u.float64);char *jsret = test_add_str(jscstr, d);return JS_NewString(ctx, jsret);
}/* 定义API的函数入口名称及列表 */
static const JSCFunctionListEntry js_test_funcs[] = {/* JS_CFUNC_DEF(函数入口名称,入参个数,QuickJS C 函数) */JS_CFUNC_DEF("testAdd", 2, js_test_add),JS_CFUNC_DEF("testAddStr", 2, js_test_add_str),
};/* 定义初始化回调方法(由系统调用,入参格式固定),将函数入口列表 在模块中暴露 */
static int js_test_init(JSContext *ctx, JSModuleDef *m)
{return JS_SetModuleExportList(ctx, m, js_test_funcs,countof(js_test_funcs));
}/* 定义初始化模块方法,由系统自动调用,且函数名称不可更改 */
JSModuleDef *JS_INIT_MODULE(JSContext *ctx, const char *module_name)
{JSModuleDef *m;m = JS_NewCModule(ctx, module_name, js_test_init);if (!m)return NULL;JS_AddModuleExportList(ctx, m, js_test_funcs, countof(js_test_funcs));return m;
}

quickjs.hquickjs-libc.hlibquickjs.a拷贝到当前工程目录下。
执行命令

gcc c_test_m.c libquickjs.a  -fPIC -shared -o libtest.so

生成libtest.so文件。

1.7.3. 使用.so模块

创建js文件 c_test_m.js

import { testAdd , testAddStr} from 'libtest.so'
console.log('\n')
console.log(`testAdd: ${testAdd(1, 0.5)}`)
console.log('\n')
console.log(`testAddStr: ${testAddStr('Pi equal to about ', 3.14159)}`)
console.log('\n')
qjs c_test_m.js

输出:
argc = 2
a = 1
b = 0.500000
argv[1].u.float64 = 0.500000
testAdd: 1.5

JS_ToCString(ctx, argv[0]) = Pi equal to about
argv[1].u.float64 = 3.141590
testAddStr: Pi equal to about 3.14

项目地址

QuickJS 快速入门 (QuickJS QuickStart)相关推荐

  1. Shiro第一个程序:官方快速入门程序Qucickstart详解教程

    目录 一.下载解压 二.第一个Shiro程序 1. 导入依赖 2. 配置shiro配置文件 3. Quickstart.java 4. 启动测试 三.shiro.ini分析 四.Quickstart. ...

  2. apache2.4.9 开启path_info访问_【第一篇】ASP.NET MVC快速入门之数据库操作(MVC5+EF6)...

    新建项目 打开VS2015,找到菜单项[文件->新建->项目],打开向导对话框: 注意我们的选择项: 运行平台:.NET FrameWork 4.5 项目模板:ASP.NET Web Ap ...

  3. 关卡设计快速入门_7. 自己来!

    通过使用这个引导的一些方法,尝试做以下这些事情: 将关卡的光照改为月光,晚间场景. 在第一个房间边上添加另一个房间. 在新添的房间里,试着抬高它并用梯子连接. 添加一些灌木,一个沙发,一些书架和一扇门 ...

  4. Gradle用户指南(章9:Groovy快速入门)

    Gradle用户指南(章9:Groovy快速入门) 你可以使用groovy插件来构建groovy项目.这个插件继承了java插件的功能,且扩展了groovy编译.你的项目可以包含groovy代码.ja ...

  5. 【转】Robot Framework 快速入门

    目录 介绍 概述 安装 运行demo 介绍样例应用程序 测试用例 第一个测试用例 高级别测试用例 数据驱动测试用例 关键词keywords 内置关键词 库关键词 用户定义关键词 变量 定义变量 使用变 ...

  6. ADO.NET Entity Framework Beta2(五)/快速入门(实体框架)

    This quickstart illustrates a series of tasks that support the topics in Getting Started with the En ...

  7. 2017 Vue.js 2快速入门指南

    注意,据部分读者反映本文水多,怕湿身者勿进.后续推荐详解 Vue & Vuex 实践 2017 Vue.js 2快速入门指南翻译自Vue.js 2 Quickstart Tutorial 20 ...

  8. 教你从0到1搭建秒杀系统-Canal快速入门(番外篇)

    Canal用途很广,并且上手非常简单,小伙伴们在平时完成公司的需求时,很有可能会用到.本篇介绍一下数据库中间件Canal的使用. 很多时候为了缩短调用延时,我们会对部分接口数据加入了缓存.一旦这些数据 ...

  9. Quartz快速入门

    Quartz快速入门 1.下载地址http://www.quartz-scheduler.org/downloads/ 写此文章时的最新版是2.2.1. 2.解压缩将基本包quartz-2.2.1.j ...

最新文章

  1. CVPR 2021 | 中科大联合快手,提出人脸伪造检测新方法
  2. Android之解决在scrollview中嵌套ListView切换界面时scrollview整体向下滑动
  3. iebook 发布到网站 独家秘诀
  4. mysql-5.7.17-winx64的安装配置
  5. java核心技术----访问权限
  6. 数据结构二之线段树Ⅰ——Count Color,Hotel,Transformation,Tree Generator™
  7. 通用Makefile实现
  8. discuz中又拍云在ie8,chrome22下不能上传的问题
  9. jdk1.8 base64注意事项
  10. 1.5编程基础之循环控制 03 均值 python
  11. 远程桌面超出最大连接数问题
  12. shop--7.店铺编辑和列表--店铺列表展示 前端
  13. ODBC和JDBC是做什么的?为初学者理解概念问题
  14. 【转】tensorflow中的batch_norm以及tf.control_dependencies和tf.GraphKeys.UPDATE_OPS的探究
  15. MySQL 的慢 SQL 怎么优化?
  16. J-Link驱动下载(含历史版本)
  17. 计算机制图作品答辩,工程制图(第一章)答辩.ppt
  18. python 两点曲线_ECC椭圆曲线加密算法:ECDH 和 ECDSA
  19. python ImportError: No module named spiders
  20. flume中HDFS IO error

热门文章

  1. 上海的“户口”和“居住证”到底有哪些不同?
  2. js文件流,导出txt
  3. 计算机信息学院活动简报,青海师范大学计算机学院“善行一百”活动简报
  4. 职场员工有没有潜力,看这一个能力就够了
  5. fmcw matlab仿真,基于SIMULINK的FMCW雷达测距功能仿真.pdf
  6. 全新第八代智能英特尔® 酷睿™ 处理器经全面优化
  7. i7 10510U性能怎么样?相当于台式机什么水平
  8. 浅析MOS管开关速度影响因素-KIA MOS管
  9. 算法设计与分析实验报告验优参考
  10. android下查看内存阀值限制