Fluttify编译器原理介绍

系列文章:前端

(一)Flutter插件开发必备 原生SDK->Dart接口生成引擎Fluttify介绍java

(二)如何利用Fluttify开发一个新的Flutter插件android

(三)Fluttify输出Flutter插件工程详解ios

(四)Fluttify编译器原理介绍算法

Fluttify网站:fluttify.comjson

前言

普通开发Flutter插件的方式既繁琐又容易出错,由于须要在dart和原生之间传递大量的数据,在这个过程当中须要手写大量模板代码。前段时间饿了么团队发布了一个插件dna,这个插件提供了一个通用的channel在dart和原生之间传递数据,避免了手写原生代码,过程当中使用反射来调用对应原生代码。和dna不一样的是Fluttify提供了一个更静态的方案,即从原生出发,生成对应的dart绑定。后端

正如Fluttify的定位“编译器”所示,Fluttify的总体实现分红前端和后端,链接前端和后端的是一个表明SDK的中间表示。api

前端负责借助antlr从jar/aarframework中解析出中间表示(目前使用的是json格式),后端则消费这个中间表示,把其转换成dart/java/objc代码。缓存

解析器ANTLR

引用自Wiki:bash

ANTLR(全名:ANother Tool for Language Recognition)是基于LL(*)算法实现的语法解析器生成器(parser generator),用Java语言编写,使用自上而下(top-down)的递归降低LL剖析器方法。由旧金山大学的Terence Parr博士等人于1989年开始发展。

ANTLR自己是Java实现的,向ANTLR输入一个语法规则文件,可以生成对应语言源文件的解析代码,目前支持输出Java, C#, Python2|3, JavaScript, Go, C++, Swift代码,也就是说你能够用这些语言的代码解析任何语法规则对应的源代码。

好比说如今有一份A语言的语法文件A.g4,把这个文件做为参数传入ANTLR,ANTLR能够为你生成一份Swift代码,这份Swift代码能够遍历A语言的源代码,你能够解析出A语言代码里任何你感兴趣的部分。

咱们这里使用ANTLR默认的输出语言Java。

从远程依赖获取源代码

从maven坐标到jar

Fluttify支持从maven坐标直接生成插件工程,其中的难点即是怎么把maven坐标下载成真实的SDK,我找了不少maven相关的rest api服务,可是要么是少字段,要么是速度很慢,再要么就是商业接口要付费。

幸运的是Fluttify是基于gradle实现的,一顿google后,发现gradle api能够指定maven坐标直接下载artifact,后来才发现其实这跟在build.gradle里添加依赖是同样的。只不过平时都是写在build.gradle里,换成gradle api就懵逼了。

project.repositories.run {
    maven { it.url = URI("http://maven.aliyun.com/nexus/content/groups/public/") }
    jcenter()
    mavenCentral()
}
val config = project.configurations.create("targetJar")
val dep = project.dependencies.create(ext.android.remote.run { "$org:$name:$version" })
config.dependencies.add(dep)
config.files // 调用这句后,若是本地没有缓存,gradle就会去下载
复制代码

从cocoapods到framework

从cocoapods获取到源代码的方法就更trick一点,一开始也是各类找有没有开放的rest api,不少地方说cocoapods官方有开放api,可是试了以后都不能用。后来只能想一些偏门的方法,好比说直接读取cocoapods的本地索引。

cocoapods在用户目录下会有一个~/.cocoapods/repos/master/Specs文件夹,一开始看见这个文件夹下的内容很容易会被劝退,由于它是这样的:

cocoapods

这些16进制数字文件夹会有三层,到第4层就是实际的pod了,每一个pod下面会有全部版本的podspec.json,剩下的工做就是解析这个json,获取到里面的下载连接,下载压缩包便可。

编译器前端

生成中间表示第一步须要拿到源代码,android端采用反编译jar的方式获取到源代码,ios端则直接拿到objc的头文件直接解析便可。

反编译使用的是intellij使用的Fernflower反编译器,反编译结果效果不错,目前没有碰到大问题。

第二步就是遍历源代码,这是整个编译器中最困(bu)难(dong)的部分。因为对objc语言的不熟悉,不少objc的语言元素的叫法分不清哪一个是哪一个,各类specifier,并且不少语法元素能够递归嵌套,很难从语法文件想象出本来的源代码的样貌。

个中细节再也不赘述,最终编译器会把SDK分解为7个Java类,分别是SDKTypeConstructorFieldMethodParameterVariable

一个SDK会被一个SDK类表示,而后SDK对象会被序列化,并写入一个文件中,供后端使用。

一个中间表示的部份内容:

{
  "version": "0.0.1",
  "platform": "Android",
  "libs": [
    {
      "name": "com",
      "types": [
        {
          "platform": "Android",
          "name": "com.autonavi.ae.gmap.maploader.Pools$Pool",
          "genericTypes": [
            "com.autonavi.ae.gmap.maploader.T"
          ],
          "typeType": "Interface",
          "isPublic": true,
          "isAbstract": true,
          "isInnerType": true,
          "isStaticType": true,
          "isJsonable": false,
          "superClass": "",
          "interfaces": [],
          "constructors": [],
          "fields": [],
          "methods": [
            {
              "exactName": "acquire",
              "returnType": "com.autonavi.ae.gmap.maploader.T",
              "name": "acquire",
              "formalParams": [],
              "isStatic": false,
              "isAbstract": true,
              "isPublic": true,
              "className": "com.autonavi.ae.gmap.maploader.Pools$Pool",
              "platform": "Android",
              "isDeprecated": false,
              "isFunction": false,
              "isGenericMethod": false
            },
            ...
复制代码

编译器后端

有了中间表示后,其实编译器后端的工做就相对轻松了。精力消耗都在摸索模板内容中。主要的工做就是怎么把(好比)Method对象转换为Dart/Java/Objc对应的代码。

因为Java,Objc和Dart之间的语法并不能一一对应,因此在编写模板的过程当中也遇到很多问题。

好比说高德地图的MAMapView设置delegate,因为delegate是弱引用,因此任何新建立的MAMapViewDelegate对象赋值给delegate都会被当即回收,由于引用计数没有增长,因此delegate必须赋值为self,一开始找不到合适的对象来当这个self,整个插件里只有主Plugin类和PlatformViewFactory类两种类型的对象,因此只能让PlatformViewFactory类来承当这个self,也能让delegate和PlatformViewFactory的生命周期保持一致。

后记

所谓编译不过就是把一段文本转化成另外一段文本,创造Fluttify的过程当中,对于Fluttify究竟是一个什么东西的见解也一直在转变,这都源于个人知识匮乏,一开始以为它是一个生成器,因此定位成一个所谓的引擎,后来xster大佬在Fluttify输出Flutter插件工程详解 下向我推荐了他们Flutter官方搞的一个相似的东西dartle,我才意识到Fluttify实际上是一个编译器,和大多数编译器同样,它有前端和后端,只不过它的目标代码再也不是二进制而是可读的代码,甚至理论上借助中间表示,也能够为React Native这样的技术生成插件。

相关文章
相关标签/搜索