kraken
kraken copied to clipboard
[WIP]: refactor: redesign bridge code base
对 bridge 的实现进行重新设计,用更好的方案解决一些固有的顽疾,并有效提升 bridge 的运行性能。
Bridge 目前存在的顽疾:
- 实现逻辑大量依赖 QuickJS API,完全手动管理内存很容易出问题。
- API 的实现依靠手写,大量重复性的体力活太多。
- 核心 DOM 实现逻辑目前依然采用 QuickJS 的数据结构,性能表现不佳。
基于以上问题,采用一些新的设计思路方案:
- [x] 将代码库进行拆分,划分出 core 和 bindings 目录,core 负责内部逻辑的实现,bindings 负责和 QuickJS 相关的交互。
- [x] 使用栈内存来管理引用计数,core 的实现逻辑均将 JSValue 分配在栈上。
- [x] 采用代码生成器生成所有 API 的胶水代码。
- [x] 初始化有关的数据一律静态化,比如 WrapperTypeInfo。对后面多引擎支持做准备。
- [ ] 重新设计并实现 DOM API
- [x] EventTarget
- [x] Node
- [x] ContainerNode
- [x] Element
- [x] Document
- [x] Attr
- [x] CharacterData
- [x] DocumentFragment
- [x] Text
- [x] NodeList
- [x] CSSStyleDeclaration
- [ ] CustomElement
- [ ] CustomEvent
- [ ] DOM Selector
- [x] 常用字符串静态化,在构建阶段预先分配存储。
Design Notes:
实现层与接口之间的分离

TS 类型与 C++ 类型之间的转换
依据 TS 类型的信息,生成对应调用 Converter
的代码。Converter
是一个 C++ Type Trains 类,负责将各种类型 JS 值转换为 C++ 值。
基本的类型映射关系:
JS | C++ |
---|---|
double | TSDouble |
int64 | TSInt64 |
boolean | TSBool |
double? | TSOptional<TSDouble> |
double | null | TSNullable<TSDouble> |
double[] | TSSequence<TSDouble> |
type BlobPart = { name: string, value: double} | TSBlobPart // 自定义类型需要独立实现转换器 |
any | TSAny |
生成器实现范例
template <>
struct Converter<TSDouble> : public ConverterBase<TSDouble> {
static ImplType FromValue(JSContext* ctx, JSValue value) {
assert(!JS_IsException(value));
double v;
JS_ToFloat64(ctx, &v, value);
return v;
}
static JSValue ToValue(JSContext* ctx, double v) { return JS_NewFloat64(ctx, v); }
};
JS API 的装载过程
生成器生成的 API 代码需要通过装载才可以被注入到 JS 的全局环境中,供 JS 访问。
装载会在 Bridge 初始化到时候进行调用。

JS 环境中类构造函数
JS 环境大部分的 API 都是通过构造函数来创建,比如 EventTarget,Element,Node 这些都可以直接全部访问的变量。通过构造函数所创建的对象的原型都指向构造函数。
构造函数都是全局性的,即同一种类型的构造函数,在一个 JSContext 中只会存在一个。同时这些值都将存储在 ExecutionContextData
这样一个全局的对象中。

读取一个方法的过程
JS 采用原型链的方式实现继承,所以访问属性和方法会按照原型链来进行定位。

GC 回收的过程
在 bindings 目录有一个叫做 GarbageCollected
的类,所有继承它的类的内存管理均交给 JS 引擎的 GC 来承担,所以在 C++ 环境中不需要额外对这些类做内存回收操作。
GC 的回收分为两个阶段,
- Trace 阶段,负责追踪到哪些可能需要被回收的对象。
- Dispose 阶段,将需要被回收的对象进行释放,并调用 dispose 方法。

EventTarget

这个pr还有问题吗?看起来做了很不错的修改,改动较大,原始团队评估能合入了么
原始团队很难找到人 review 了,估计要组建新团队之后重新 review