惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

SecWiki News
SecWiki News
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
V
Visual Studio Blog
博客园 - 叶小钗
S
SegmentFault 最新的问题
IT之家
IT之家
大猫的无限游戏
大猫的无限游戏
博客园_首页
Apple Machine Learning Research
Apple Machine Learning Research
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
月光博客
月光博客
酷 壳 – CoolShell
酷 壳 – CoolShell
腾讯CDC
D
Darknet – Hacking Tools, Hacker News & Cyber Security
V
V2EX
阮一峰的网络日志
阮一峰的网络日志
L
Lohrmann on Cybersecurity
量子位
C
Cyber Attacks, Cyber Crime and Cyber Security
T
Tor Project blog
J
Java Code Geeks
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
博客园 - 三生石上(FineUI控件)
Attack and Defense Labs
Attack and Defense Labs
AI
AI
The Cloudflare Blog
T
Tailwind CSS Blog
S
Schneier on Security
爱范儿
爱范儿
PCI Perspectives
PCI Perspectives
Stack Overflow Blog
Stack Overflow Blog
S
Secure Thoughts
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
T
The Exploit Database - CXSecurity.com
博客园 - 【当耐特】
V2EX - 技术
V2EX - 技术
S
Securelist
P
Proofpoint News Feed
T
Threat Research - Cisco Blogs
Help Net Security
Help Net Security
C
Cisco Blogs
N
News and Events Feed by Topic
人人都是产品经理
人人都是产品经理
B
Blog RSS Feed
K
Kaspersky official blog
T
The Blog of Author Tim Ferriss
G
Google Developers Blog
S
Security Affairs
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Simon Willison's Weblog
Simon Willison's Weblog

yulingtianxia's blog

Flutter 官方终于出手了,DartNative 将何去何从? CPP-Summit 2020 实现 Native 异步回调 Flutter 如何实现 Flutter 同步调用 Native API DartNative 内存自动管理 告别 Flutter Channel,调用 Native API 仅需一行代码! BlockHook and Memory Safety Passing Out Parameter in DartNative 在 Flutter 中玩转 Objective-C Block DartNative Struct DartNative Memory Management: C++ Non-Object DartNative Memory Management: NSObject 谈谈 dart_native 混合编程引擎的设计 用 Dart 来写 Objective-C 代码 App 二进制文件重排已经被玩坏了 BlockHook with Invocation(2) BlockHook with Invocation(1) BlockHook with Private Data BlockHook with Revocation
如何实现一行命令自动生成 Flutter 插件
杨萧玉 · 2020-07-25 · via yulingtianxia's blog

文章目录

  1. 1. 为何要做代码生成工具
  2. 2. 技术方案
  3. 3. 遇到的坑
    1. 3.1. 词法分析失败
    2. 3.2. 语法特性映射
    3. 3.3. 批量处理
    4. 3.4. 多种使用场景
  4. 4. 总结
  5. 5. Reference

在上一篇文章《告别 Flutter Channel,调用 Native API 仅需一行代码!》 发出后,收到了很多关注。仔细想想,其实不是仅仅只需一行代码的,还需要敲一行 codegen 命令来生成 Dart 代码。这回就简单讲下自动生成代码这块的设计和实现原理。

为何要做代码生成工具

一开始技术架构的搭建自底向上的。当我做出 Flutter 与 Native 之间的高性能通道后,自然而然地去想要提升易用性,降低开发者的使用门槛。当你从使用者角度去审视自己的产品时,就会自顶向下去设计一些 Feature 去满足目标用户的诉求。

最终我决定开发一款命令行工具。它可以解析 Native 代码中的 API,生成对应的 Dart 代码,再进而支持生成 Flutter Plugin/Package 工程。生成的 Flutter 工程会通过 pub 依赖 DartNative:

工具需要满足以下需求:

  1. 兼容性:能够支持 Native 多种语言代码的转换,如 Objective-C,Java,Swift,Kotlin 等
  2. 易用性:开发者能够很方便地安装和使用工具

技术方案

为了满足兼容性,在实现『解析代码生成 AST』的技术方案上,是基于第三方开源框架 antlr 来实现。命令行工具使用 NodeJS 实现,开发者可以通过 npm 很方便地安装工具。

如果基于 Clang 来实现的话,性能和稳定性都有保证,但是需要 link 到所需的代码或 framework。且 Clang 对多语言的支持比较局限,也不方便提供给开发者去安装。

关于 antlr 的使用,这里不再赘述,可以直接查看官方文档。(毕竟作者是靠卖书赚钱的)。这里需要将源语言的 grammer 生成 JS 版本的 Runtime 文件,在遍历 AST 的 callback 中收集所需的元数据,转换成为生成 Dart 自定义格式的 AST。最后遍历 Dart AST,生成 Dart 代码。

这里的方案概括为如下步骤:

  1. 查找出包含客户端 API 的 Native 代码文件,如 .h.java 文件
  2. 通过 antlr parser 生成 Native 语言的 AST
  3. 将 Native AST 转换成 Dart 语言所需的 AST
  4. 通过 Dart AST 生成 dart 代码

遇到的坑

词法分析失败

官方提供的 Objective-C 的 grammer 有很多问题,生成的 lexer 还是 parser 都会在词法分析阶段就抛异常。这里就比较坑了,需要修改 lexer 和 parser 的 g4 文件。可能是 grammer 太久没更新了,很多分词阶段就抛异常了,比如 @import 都不支持,真是一言难尽。而且这种只针对单个文件的词法分析程序,很难像 Clang 那样可以 link 其他文件做到的严谨性。经过一系列的修复工作,已经可以 parse iOS Foundation 库的所有头文件。

语法特性映射

这一步发生在 AST 的 transform。虽说不同语言之间有很多语法设计都是想通的,但是依然会有一些难以映射的语法特性。比如 Java 的重载方法,OC 奇葩的方法名。转换成 Dart 的方法命名应该遵循哪方的语言规范?OC 的 Protocol 在 Dart 中如何表示?Dart 类的静态方法不会被继承,那么 Protocol 中的类方法怎么办?Dart 的 enum 不支持自定义 int 值,来自 OC 的 enum 如何转换成 Dart?区分何时生成 Dart 的 importexport?。。。

类似这些操蛋的问题数不胜数。。。需要不断细化,思考,打磨。。。

批量处理

当要处理的文件有很多时,一个一个地串行处理显然会让开发者等得不耐烦。NodeJS 本来不是为 CPU 密集型操作服务的,但是在 v10.5.0 引入 worker_threads 后,实现了真·多线程,解决了 CPU 密集型操作的痛点。

在处理一些稍大点的文件时,需要注意上调 NodeJS VM 老生代内存的阈值。

多种使用场景

考虑到不同的使用场景,codegen 所生成的策略也会不一样:

  1. 将 App 的 Native 代码转为 Dart 代码,直接在 Flutter 中使用
  2. 将 Native 系统库转成 Flutter Package
  3. 将 Native 第三方库转成 Flutter Plugin

这三种场景的共同点:如果 Native 代码依赖了其他库,也需要向 pubspec.yaml 中插入这个依赖库的 Dart 版本。

第 2 和 3 个场景需要用 flutter create 命令生成新的 Flutter 工程,并将生成的 Dart 代码挪到工程里。
更进一步是将生成代码所需的 Native 文件也挪到 Flutter 工程里,并更新 podspec 和 gradle (PS:待实现)。

总结

在做 codegen 之前,我一直觉得有 dart_native 超级通道就足够了。毕竟是自己开发的轮子自己熟悉,认为手写一层 Dart Wrapper 也没啥麻烦的,其实并不然。当给未接触过的开发者使用后,的确是有上手门槛和开发量的。在调研的过程中也发现 bang 神的 JSPatch 也有个 Converter 工具用来把 OC 代码转为 JS 代码,同样用的也是 antlr 解析 AST,在此也十分感谢 JSPatch 提供的思路。超级通道加工具辅助方可实现了运行性能和开发效率的双提升

Reference

antlr
grammers-v4
JSPatch Convertor 实现原理详解