Flutter语法检测及原理剖析-FAIR语法检测实践
前言
Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面。
Fair是58技术开源的一个Flutter动态化的框架,能够实现UI和逻辑的动态化。
开发者在使用Fair开发过程中存在一些痛点,比如可能会出现使用语法糖不正确或者存在不支持的语法糖问题,所以我们需要一个配套插件去提示用户使用Fair语法糖。
一、Flutter语法检测机制
在IDE中,Flutter语法检测机制是依赖Dart/Flutter插件实现的,即我们在开发Flutter前需要下载的Dart/Flutter插件。我们需要通过插件去提供Flutter的开发环境,同时插件也能提供语法检测功能。而插件其中的一个核心功能就是Analysis Server。
Analysis Server是什么?
Analysis Server是Dart SDK提供的一个Dart/Flutter语法分析服务,主要功能包括语法静态分析、代码提示、代码补全等。我们常用的Dart/Flutter IDE如intellij、Android Studio和VS Code都是通过安装Dart插件实现Dart开发环境的配置。
Dart/Flutter插件语法检测的工作流程
以Android Studio为例(因为对应插件的语言是Java,比较好阅读和理解),语法检测核心是Analysis Server,每当用户代码有改动的时候,就会通过Socket的方式同步给Analysis Server,当Analysis Server分析结束后也会将分析结果返回来,Dart插件则就是根据Analysis Server返的事件类型去做不同的处理,最终将结果刷新在用户IDE界面上。流程图如下:
Dart插件中Analysis Server的启动流程
当用户配置完Dart SDK路径后,插件会获取到配置路径,在插件启动的时候会启动一个进程去执行Dart SDK里的dart可执行文件,同时也会获取到Dart SDK目录下的"/bin/snapshots/analysis_server.dart.snapshot"文件,并将其路径作为vmArguments。我们可以看一小段代码:
//获取配置的SDK路径
mySdkHome = sdk.getHomePath();//找到dart可执行文件的路径
final String runtimePath = FileUtil.toSystemDependentName(DartSdkUtil.getDartExePath(sdk));//找到analysis_server.dart.snapshot文件路径
String analysisServerPath = FileUtil.toSystemDependentName(mySdkHome + "/bin/snapshots/analysis_server.dart.snapshot");//拼凑vmArguments
String firstArgument = useDartLangServerCall ? "language-server" : analysisServerPath;//创建Socket
myServerSocket =new StdioServerSocket(runtimePath, StringUtil.split(vmArgsRaw, " "), firstArgument, StringUtil.split(serverArgsRaw, " "), debugStream);//创建AnalysisServer实现类
final RemoteAnalysisServerImpl startedServer = new RemoteAnalysisServerImpl(myServerSocket);//这里的具体实现其实就是socket.start(),将socket启动起来
startedServer.start();
Dart插件与Analysis Server交互
Dart插件与Analysis Server交互类型主要分为两种,一种是id,一种是event。每次Dart插件给Analysis Server同步数据时都会生成一个uniqueId并缓存在HashMap,每次Analysis Server返数据的时候也会带上event或id,优先处理event类型数据。
Dart插件同步给Analysis Server数据示例:
request: {"id": String"method": "analysis.setAnalysisRoots""params": {"included": List<FilePath>"excluded": List<FilePath>"packageRoots": optional Map<FilePath, FilePath>}
}
上述说到Dart插件每次给Analysis Server同步数据都会生成一个uniqueId,这里的生成规则就是通过具备原子属性的AtomicInteger的getAndIncrement()方法每同步一次数据+1。Dart插件对应Analysis Server每种事件实现一个Consumer或Processor(如果是event类型数据则是Processor,result事件类型的话就是Consumer)。
语法检测插件的挂载流程
插件是有一个挂载过程的,插件的挂载流程如下图所示:
小结一下:
- YamlParaser解析analysis_options.yaml文件中analyzer#plugins节点,获取到一个pluginNameList
- 根据pluginNameList遍历通过Plugin Locator获取到每个plugin的analyzer_plugin目录路径,即pluginPath
- 将pluginPath目录路径文件copy到系统的.dartServer/plugin_manager/unique Directory目录下,其中unique Directory是通过对pluginPath进行md5操作生成的串
- 执行pluginPath下/bin/plugin.dart的main方法
二、Fair语法检测插件的实现原理
当我们掌握了前面的前置知识,再去开发语法插件就简单许多了。
Fair语法检测插件的实现机制
Dart插件是基于Analysis Server实现了Dart语法的检测,Fair语法检测插件则是对Dart插件语法检测功能的补充和扩展,实现对Fair语法糖的检测,Fair语法插件本质页是一个Dart语法检测插件,可以通过配置pubspec.yaml和analysis_options.yaml来使用。
语法插件的开发流程
主要可以分为以下几步:
- 首先创建一个类去继承ServerPlugin,并实现抽象属性/方法。
- 然后跟lib同级创建tools/analyzer_plugin目录,内容如图所示:
- 最后在plugin.dart文件中注册步骤1中实现的ServerPlugin。
//示例
void main(List<String> args, SendPort sendPort) {ServerPluginStarter(FairPlugin(PhysicalResourceProvider.INSTANCE)).start(sendPort);
}
核心实现
Fair语法检测插件的核心就是抽象语法树遍历,通过对抽象语法树的遍历,获取到每个子节点,然后插入自定义语法检测逻辑。
1.创建AnalysisDriver
createAnalysisDriver()是ServerPlugin的一个抽象方法,开发者实现这个方法创建一个AnalysisDriver,然后我们可以通过AnalysisDriver的results数据流去监听AnalysisResult的返回,其中这个AnalysisResult就是Analysis Server对文件的分析结果。示例代码如下所示:
@override
AnalysisDriverGeneric createAnalysisDriver(plugin.ContextRoot contextRoot) {//获取contextRootfinal contextRoots =ContextLocator(resourceProvider: resourceProvider).locateRoots(...);//获取ContextBuilderfinal contextBuilder =ContextBuilderImpl(resourceProvider: resourceProvider);//获取Analysis Contextfinal context = contextBuilder.createContext(contextRoot: contextRoots.first, byteStore: byteStore);//获取Analysis Driverfinal dartDriver = context.driver;//监听分析结果dartDriver.results.listen((analysisResult) {_processResult(...);});return dartDriver;
}
2.感知代码变化
ServerPlugin提供contentChanged方法,我们只需要收到回调的时候将内容有改变的文件路径添加到AnalysisDriver即可
@override
void contentChanged(String path) {super.driverForPath(path)?.addFile(path);
}
3.利用AOP思想对AnalysisResult处理
Dart的抽象语法树遍历是通过访问者模式实现的,我们可以在我们想要处理的节点插入一个自定义的Visitor对其及其子节点实现访问。其中我们可以从AnalysisResult拿到编译单元(CompilationUnit),每个编译单元就是一棵抽象语法树,我们可以通过其accept()方法插入一个Visitor。
4.@FairPatch注解识别实现逻辑
我们了解到Dart抽象语法树是通过访问者模式实现的,就能够很简单地去实现自定义的逻辑了。我们要实现@FairPatch注解的识别,我们只需要在遍历Annotation节点时,判断一下annotation name是我们想要的FairPatch即可。示例代码如下:
class _FairVisitor extends RecursiveAstVisitor<void> {@overridevoid visitAnnotation(Annotation node) {node.visitChildren(this);if (node.name.name == \'FairPatch\') {//...}}
}
5.if语法检测
因为if语法检测是需要前置条件的,首先得是用@FairPatch标注的类,其次还需要是在build()方法下的if才进行检测。
- 首先是build()方法检测,只需要在method deceleration的时候进行一下方法过滤即可。
@override
void visitMethodDeclaration(MethodDeclaration node) {if (node.name.name == \'build\') {//...}
}
- 其次是if语法检测,稍微复杂一点,if分为IfStatement和IfElement,IfStatement指代的是外层的if-else语句,IfElement指代的是嵌套在其他语法里的if-else语句,如果想要全覆盖if语法,则需要两者都实现。
上述两张图分别对应IfStatement和IfElement具体含义。
6.Fair的Sugar.if语法糖分类实现
在Fair中常用的Sugar.if相关语法糖有Sugar.ifEqual、Sugar.ifEqualBool和Sugar.ifRange
- Sugar.ifRange
static K ifRange<T, K>(T actual,List<T> expect, {required K trueValue,required K falseValue,}) =>expect.contains(actual) ? trueValue : falseValue;
- Sugar.ifEqual
static K ifEqual<T, K>(T actual,T expect, {required K trueValue,required K falseValue,}) =>expect == actual ? trueValue : falseValue;
- Sugar.ifEqualBool
static K ifEqualBool<T, K>(bool state, {required K trueValue,required K falseValue,}) =>state ? trueValue : falseValue;
使用Fair语法糖能够加快Fair的编译,所以我们都更推荐能使用语法糖就使用语法糖,我们要实现对语法糖分类,其实就是对if的condition进行区分,其中ifRange稍微复杂一点,则我们再讲下如何实现,其他也同理:
//ifRange有个前置条件就是如参expect是一个List,所以再检测的时候只需要判断用户在调用list.contains(xx)即可//先判断用户是在调用contains()方法@overridevoid visitMethodDeclaration(MethodDeclaration node) {if (node.name.name == \'contains\') {//...}}//其次再判断调用contains方法的对象是List@overridevoid visitMethodInvocation(MethodInvocation node) {super.visitMethodInvocation(node);//判断staticType即可_result = node.target?.staticType?.isDartCoreList ?? false;if (_result) {_target = node.target;_actual = node.argumentList.arguments.first;}}
三、Fair Plugin实现效果展示
- Android Studio实现对build()方法下if语法块的检测效果
- build方法下if的代码检测,及提示引导信息
- 点击more action 或者 AS代码提示快捷键
- 根据提示点击替换
相关推荐
- Flutter动态化框架Fair文档上线&开源倒计时
- Flutter动态化框架Fair的设计与思考
- Fair 2.0 逻辑动态化开源了!
- Fair逻辑动态化架构设计与实现
- Fair逻辑动态化通信实现
- Fair下发产物-布局DSL生成原理
- Fair 逻辑语法糖设计与实现
- Fair热更新设计与实现
- Fair在安居拍房App中的实践
- Flutter 动态化项目评测
- Fair 在 58 同城拍客 App 中的实践
- Flutter + Dart三端一体化动态化平台实践
- 超级全面的Flutter性能优化实践
支持我们
欢迎大家使用 Fair,也欢迎大家为我们点亮star
Github地址:https://fair.58.com
Fair官网:https://fair.58.com
欢迎贡献
通过Issue提交问题,贡献代码请提交Pull Request,管理员将对代码进行审核。
对Fair感兴趣的小伙伴,可以加入交流群。
微信 |
---|
![]() |
微信入群:请先添加58技术小秘书为好友,备注fair,小秘书邀请进群。
Flutter语法检测及原理剖析-FAIR语法检测实践相关推荐
- matlab霍夫变换检测直线原理,霍夫变换(霍夫变换检测直线原理)
OpenCv里面有个概率霍夫变换,但是不知道原理是什么,请各位大侠指教!.. 霍夫变换就是利用参数空间中的(ρ,θ)来表示一条直线,其中ρ是原点到直线的垂直距离,θ是原点到直线的一条垂线段与θ的夹角. ...
- webshell检测方式深度剖析 --- 行业内的实践方案
概述 关于webshell检测我们已经讲了很多理论的东西,最近在网络上搜到了一篇阿里的主机层入侵检测团队在XCON(安全焦点安全信息技术峰会)上的一篇演讲PPT,名字为<云安全环境下恶意脚本检测 ...
- java实现html语法检查函数_Html或JS语法检测之JSLint工具
1.介绍 JSLint是一个JavaScript验证工具(非开源), 可以扫描JavaScript源代码来查找问题.如果JSLint发现一 个问题,JSLint就会显示描述这个问题的消息,并指出错误在 ...
- 【编译原理】LR语法分析器的设计与实现
LR语法分析器的设计与实现 本文为当时编译原理实验作业,要求用设计的思想完成,小题大做,仅供参考 文章目录 LR语法分析器的设计与实现 实验要求 实现功能 输入输出 样例 一.LR语法分析器问题定义 ...
- 编译原理实验-LL1语法分析器(自动生成First集、Follow集求法)java实现
编译原理实验-LL1语法分析器(自动生成First.Follow)java 博主在做实验时,参考众多他人代码,发现bug众多,在@moni_mm代码基础上,与伙伴把能看到的BUG都做出修正,同时增添了 ...
- 编译原理之简单语法分析器(c语言)
语法分析是编译过程的核心部分,其基本任务是根据语言的语法规则进行语法分析,如果不存在语法错误即给出正确的语法结果,并为语义分析和代码生成做准备. 语法分析器的两种方式 语法分析器的任务主要是确定是否可 ...
- 门禁系统中人脸检测技术的原理剖析和使用教程
引言 人脸检测 API 是一种基于深度学习技术的图像处理API,可以快速地检测出一张图片中的人脸,并返回人脸的位置和关键点坐标,在人脸识别系统.人脸情绪识别等多种场景下都有极大的应用. 本文将从人脸检 ...
- SLR语法分析器-编译原理
语法分析器 完整代码及图形化界面演示详见github:编译器前端 JavaFx项目,运行Main可看到图形界面,若要测试请按照文法,举几个栗子: 数据结构定义 单词和单词token表 package ...
- react16常见api以及原理剖析
Vue 与 React 两个框架的粗略区别对比 Vue 的优势包括: 模板和渲染函数的弹性选择 简单的语法及项目创建 更快的渲染速度和更小的体积 React 的优势包括: 更适用于大型应用和更好的可测 ...
最新文章
- 2012-12-21
- 开发人员学Linux(3):CentOS7中安装JDK8和Tomcat8
- 吴恩达《Machine Learning》精炼笔记 11:推荐系统
- Linux X Window 与命令行的切换
- Openfire使用上的一些技巧
- spring4.x aop拦截spring mvc controller
- HDFS概述(5)————HDFS HA
- 服务器经常崩溃??让我们来看看简单的内存知识:C语言——内存管理
- 看门狗性能软件测试,《看门狗:军团》PC版性能测试 不建议光追,优化极差
- 跟着内核学框架-从misc子系统到3+2+1设备识别驱动框架
- 抗锯齿_电竞屏+AI抗锯齿,雷神911 Pro游戏本给你更好游戏体验
- VMware vSphere第三方免费工具介绍之一:RVTools
- 平面设计从事什么工作
- office安装错误“错误25004,您输入的产品密钥无法在此计算机上使用,-----------”
- 【科普】准大一新生如何挑选笔记本电脑
- 高分系列(GF1-GF7)卫星介绍
- Lifeline功能介绍03——课堂信息的查询
- airtest获取设备号和获取设备宽度、高度、绝对坐标 相对坐标、滑动屏幕
- Java获取Excel中链接的文件
- android分享截屏到微信,Android 微信分享长图 ScrollView 生成长截图 View变bitmap