1. QuickJS 快速入门 (QuickJS QuickStart)

1.1. 简介

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

1.2. 安装

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和其他一些实用程序。

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。

代码示例:

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. springboot笔记02——快速入门quickstart

    前言 学习一个新的框架,往往会用一个quickstart快速入门,这次就写一下springboot的quickstart程序. 开发环境 JDK 1.8 Springboot 2.1.6 Maven ...

  2. 【番外篇】ASP.NET MVC快速入门之免费jQuery控件库(MVC5+EF6)

    目录 [第一篇]ASP.NET MVC快速入门之数据库操作(MVC5+EF6) [第二篇]ASP.NET MVC快速入门之数据注解(MVC5+EF6) [第三篇]ASP.NET MVC快速入门之安全策 ...

  3. 【第一篇】ASP.NET MVC快速入门之数据库操作(MVC5+EF6)

    目录 [第一篇]ASP.NET MVC快速入门之数据库操作(MVC5+EF6) [第二篇]ASP.NET MVC快速入门之数据注解(MVC5+EF6) [第三篇]ASP.NET MVC快速入门之安全策 ...

  4. Spring IoC反转控制的快速入门

    * 下载Spring最新开发包 * 复制Spring开发jar包到工程 * 理解IoC反转控制和DI依赖注入 * 编写Spring核心配置文件 * 在程序中读取Spring配置文件,通过Spring框 ...

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

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

  6. TestDriven.NET – 快速入门

    TestDriven.NET – 快速入门[译文] 介绍 这部分将提供一个快速的入门向导,在vs.NET的任何一个版本上面使用TestDriven.NET TDD(测试驱动开发)在你写你的代码之前,写 ...

  7. SuperSocket快速入门(三):实现你的AppServer和AppSession

    什么是AppSession? AppSession 代表一个和客户端的逻辑连接,基于连接的操作应该定义于在该类之中.你可以用该类的实例发送数据到客户端,接收客户端发送的数据或者关闭连接.同时可以保存客 ...

  8. SuperSocket快速入门(二):启动程序以及相关的配置

    如何快速启动第一个程序 既然是快速入门,所以,对于太深奥的知识点将不做讲解,会在后续的高级应用章节中,会对SS进行拆解.所有的实例90%都是来自SS的实例,外加本人的注释进行讲解. 一般应用而言,你只 ...

  9. 【转】Robot Framework 快速入门

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

随机推荐

  1. ROS Software update

    http://packages.ros.org/ros/ubuntu

  2. SQLite带参数处理方法

    /// <summary> /// 执行语法[新增 修改 删除] /// </summary> /// <param name="sqlText"&g ...

  3. 修改excel图表中的“系列一”

    修改excel图表中的"系列一" 觉得有用的话,欢迎一起讨论相互学习~Follow Me 参考文献 https://zhidao.baidu.com/question/153915 ...

  4. Spring 定时任务 注解版

    Task类: ManageSql.Java对应代码: package com.axb.cheney.task; import java.sql.ResultSet; import java.sql.S ...

  5. 使用SampleRateConverter对音频采样率进行转换

    java sound resource SampleRateconverter.java(接近于官方源码) 输入目标采样率,输入文件,输出文件.食用方便;p 比如 SampleRateConverte ...

  6. LODOP粒度TableRowThickNess合并行测试

    之前的博文:LODOP打印table不切行TableRowThickNess. ,中是没有合并行等的表格,通过设置增大分页粒度,会找附近的表格线,然后根据表格线分页,避免了切行.如果有比较复杂的表格, ...

  7. python:动态参数*args

    动态参数 顾名思义,动态参数就是传入的参数的个数是动态的,可以是1个.2个到任意个,还可以是0个.在不需要的时候,你完全可以忽略动态函数,不用给它传递任何值. Python的动态参数有两种,分别是*a ...

  8. Kafka运维大全来了!优化、监控、故障处理

    Kafka运维大全来了!优化.监控.故障处理……   Kafka概念 Kafka是分布式发布-订阅消息系统.它最初由LinkedIn公司开发,之后成为Apache项目的一部分.Kafka是一个分布式的 ...

  9. 安装rpy2 报错<cdef source string>:23:5: before: blah1 解决办法就是直接下载一个rpy2的轮子

    win7上安装rpy2, python环境是3.6.1. 使用pip install rpy2直接安装rpy2,对应的版本时rpy2 3.0.5 报如下错误: ERROR: Complete outp ...

  10. 乐字节Java继承|方法重写、super和final关键字

    大家好,乐字节的小乐又来了,上一篇是:乐字节Java|JavaBean.继承与权限修饰,也是属于Java继承的,今天继续Java继承. 一. 方法的重写 父类不满足子类的要求,按需改写.注意 方法签名 ...