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這樣的技術生成插件。

相關文章
相關標籤/搜索