Android 接入 QuickJS(二)- 源码阅读

引言

我们接入 QuickJS 引擎的目的是为了使用引擎能力,那么应该从哪里获取 QuickJS 支持那些能力呢,比较好的一个来源就是 QuickJS 源码。

相关文章

Android 接入 QuickJS(一)

源码阅读

为了对 QuickJS 引擎有稍微深入的了解,我们需要阅读源码以加深对源码的理解,并且通过源码的阅读找寻当下我们使用引擎可能会用到的 API。

目前我个人是通过几个维度来阅读和分析源码,它们分别是头文件阅读与分析 / 源码 Test Code 阅读 / 引擎核心实现源码阅读。经过这三种维度的源码阅读后会对整体的引擎代码有一个初步了解。有了这些基础后就可以粗略选择出来可能要使用的 API 接口。

头文件

由于 QuickJS 是用 C 语言写的,我们在使用的时候可以优先查看代码通过头文件暴露了那些方法。

这里我以比较重要的 quickjs.h 为例,分析 QuickJS 引擎都提供了那些能力。

从上至下阅读,首先可以看到 QuickJS 对各种 reference 的 tag 定义,如 JS_TAG_INT = 0 / JS_TAG_BOOL = 1 等。

继续往下可以看到 QuickJS 对核心结构体的定义,如 JSValue / JSValueUnion 等。

然后在这个头文件里声明了一些常用的方法,如 #define JS_VALUE_GET_TAG(v) 获取 JSValue 的 tag 等。

最后是一些方法的定义,如 JSRuntime *JS_NewRuntime(void); 产生 JSRuntime 的结构等。

有了这个整体头文件的分析,可以大致对 QuickJS 引擎有一个初步的了解。经过这一宏观的了解后就可以根据 QuickJS 源码中提供的测试代码进行阅读来进一步的熟悉 QuickJS 的架构和基本用法。

Example Code

hello.c

通过编译 QuickJS 的源码会生成一个 test 文件 hello.c 通过对这个文件的阅读,我们可以了解最典型的 QuickJS 引擎用法。

c 复制代码
int main(int argc, char **argv)
{
  JSRuntime *rt;
  JSContext *ctx;
  rt = JS_NewRuntime();
  js_std_set_worker_new_context_func(JS_NewCustomContext);
  js_std_init_handlers(rt);
  ctx = JS_NewCustomContext(rt);
  js_std_add_helpers(ctx, argc, argv);
  js_std_eval_binary(ctx, qjsc_hello, qjsc_hello_size, 0);
  js_std_loop(ctx);
  JS_FreeContext(ctx);
  JS_FreeRuntime(rt);
  return 0;
}

通过上述代码可以分析出,在使用 QuickJS 引擎时,首先要创建一个 JSRuntime 环境,然后通过这个 Runtime 去生成对应的 JSContext 后续的执行则依赖这个 JSContext 去使用。

由于 QuickJS 引擎初衷有一部分是为嵌入式设备使用,因此从 js_std_eval_binary(ctx, qjsc_hello, qjsc_hello_size, 0); 这个方法里也可以看出 QuickJS 是支持直接通过二进制字节码运行脚本代码的。

这个 hello.c 文件驱动的方法都是通过调用 quickjs-libc.h 实现的。通过研究这个文件的实现类 quickjs-libc.c 可以看到大部分的典型用法。

如 js_std_eval_binary 方法中最终是调用 JS_EvalFunction 执行输入的字节码二进制文件,在 js_std_add_helpers 方法中完成对应方法的注入等。

point.c

这个 example 主要是讲解如何自定义自己的 JS Class 以及在 JS 脚本中使用,通过自定义实现,可以在 JS 脚本中使用我们使用 C 语言定义的结构体等。

关于本 example 的使用会在接下来的文章(API 详解)中详细解析,同时也会在该文中解析各个 API 的使用场景和注意事项。

Source Code

经过上述两种方式的阅读后,可以再通过阅读真实的 QuickJS 源码加深对引擎的理解。

阅读源码时,有时候会因为源码的体积或者架构比较复杂感到无从下手,根据我个人的经验,最好的阅读方式是带着问题去阅读和学习更加快速。

现在就以 JS_EvalFunction 为例,看下 QuickJS 引擎是如何执行 Fucntion 的实例的。

阅读代码可以发现这个方法内部调用了 JS_EvalFunctionInternal 进行下一步的执行,其中多添加了一个参数是 ctx->global_obj ,在这个方法的内部首先判断这个输入的 fun_obj 是 JS_TAG_FUNCTION_BYTECODE 还是 JS_TAG_MODULE 类型,如果是 JS_TAG_FUNCTION_BYTECODE 则可以正常执行 JS_CallFree 并且拿到结果。如果是 JS_TAG_MODULE 则会首先拿到对应的 PTR 然后对这个 PTR 进行一系列的操作,最终完成这个 module 的加载。

小结

本篇文章主要从三个维度介绍了如何阅读 QuickJS 的源码,希望通过这些方式可以加深大家对 QuickJS 的了解。

接下来将根据实际代码分析如何使用 QuickJS 引擎在自己的工程中实战。

相关推荐
服装学院的IT男11 分钟前
【Android 13源码分析】Activity生命周期之onCreate,onStart,onResume-2
android
Arms20614 分钟前
android 全面屏最底部栏沉浸式
android
家有狸花16 分钟前
VSCODE驯服日记(三):配置C++环境
c++·ide·vscode
服装学院的IT男16 分钟前
【Android 源码分析】Activity生命周期之onStop-1
android
dengqingrui12338 分钟前
【树形DP】AT_dp_p Independent Set 题解
c++·学习·算法·深度优先·图论·dp
C++忠实粉丝39 分钟前
前缀和(8)_矩阵区域和
数据结构·c++·线性代数·算法·矩阵
ZZZ_O^O1 小时前
二分查找算法——寻找旋转排序数组中的最小值&点名
数据结构·c++·学习·算法·二叉树
ChinaDragonDreamer3 小时前
Kotlin:2.0.20 的新特性
android·开发语言·kotlin
小飞猪Jay3 小时前
C++面试速通宝典——13
jvm·c++·面试
rjszcb4 小时前
一文说完c++全部基础知识,IO流(二)
c++