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 引擎在自己的工程中实战。

相关推荐
成都大菠萝1 小时前
Android Car CarProperty 车辆信号链路
android
敲代码的鱼1 小时前
PDF 预览与签名批注写回 支持安卓 iOS 鸿蒙 UTS插件
android·前端·ios
时光足迹3 小时前
uni-app 视频通话实战:康复师与患者视频问诊的 6 个致命 Bug 与解决方案
android·ios·uni-app
Coffeeee7 小时前
闲聊几句,Android老哥们,你们多久没做技改需求了
android·程序员·代码规范
萝卜er8 小时前
Fragment 生命周期与状态恢复-《Android深水区(四)》
android
萝卜er8 小时前
Intent 显式、隐式与 PendingIntent-《Android深水区(五)》
android
用户805533698039 小时前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
Kapaseker10 小时前
一文吃透 Kotlin 集合操作符
android·kotlin
三少爷的鞋11 小时前
Main-safe:现代Android 架构真正的分水岭
android
沐怡旸20 小时前
深入解析 Android Performance Analyzer (APA) 底层架构与技术原理
android