Flutter 热更新无侵入计划(生成运转时库)

导读

1 Flutter 热更新无侵入计划(探讨)

flutter_runtime_ide 运转时生成IDE

已经很久没有写相关的文章了,首要在于每天研究时间很短,二是忙着研究怎样生成运转时库。尽管现在还有许多没有完成,我现在先同步一个大概的完结完成吧。

基本完结关于下面的运转库生成的支撑

  • 类只读特点的获取
  • 类设置特点的设置
  • 类初始化办法调用
  • 类办法的调用
  • 类静态特点的获取
  • 类静态特点的设置
  • 类办法的调用
  • 大局特点的获取
  • 大局特点的设置
  • 大局办法的调用
  • 根底枚举值的获取

剖析当时库的一切依靠

pub get

关于需求剖析的库中将一切库生成支撑运转时支撑的库,需求剖析出来一切的依靠库列表,这就需求咱们提早履行pub get拉取一切的依靠库。

读取 package_config.json 里边的信息

~/.dart_tool/package_config.json这个文件能够获取到最新的依靠的信息,首要包括如下的信息。

- configVersion 当时装备版别号
- generated 装备生成的详细时间
- generator 装备生成者
- generatorVersion 生成者的对应 Dart 版别
- packages 依靠库列表
	- name 库称号
	- rootUri 库对应的本地地址
	- packageUri 库源文件对应的途径
	- languageVersion 当时库要求的 Dart 版别

将装备的 JSON 信息转换成目标

import 'package:darty_json_safe/darty_json_safe.dart';
class PackageConfig {
  late int configVersion;
  late String generated;
  late String generator;
  late String generatorVersion;
  late List<PackageInfo> packages;
  PackageConfig.fromJson(Map<String, dynamic> json) {
    final jsonValue = JSON(json);
    configVersion = jsonValue["configVersion"].intValue;
    generated = jsonValue["generated"].stringValue;
    generator = jsonValue["generator"].stringValue;
    generatorVersion = jsonValue["generatorVersion"].stringValue;
    packages = jsonValue["packages"]
        .listValue
        .map((e) => PackageInfo.fromJson(e))
        .toList();
  }
}
class PackageInfo {
  late String name;
  late String rootUri;
  late String packageUri;
  late String languageVersion;
  PackageInfo.fromJson(Map<String, dynamic> json) {
    final jsonValue = JSON(json);
    name = jsonValue["name"].stringValue;
    rootUri = jsonValue["rootUri"].stringValue;
    packageUri = jsonValue["packageUri"].stringValue;
    languageVersion = jsonValue["languageVersion"].stringValue;
  }
}

这儿我用到自己之前发布的一个库darty_json_safe来解析就没用到官方解析的库了。

生成运转库

咱们将上面剖析出来的依靠装备信息放在能够大局访问,用于后续方便咱们查找将关联类型的类进行引进。

剖析库的途径

咱们从 PackageInfo 中字段 rootUri 能够拿到依靠库详细的详细的地址,每个地址都是一个对应版别库的依靠,比方咱们拿着 yaml 这个依靠库为比方。

file:///Users/king/.pub-cache/hosted/pub.flutter-io.cn/yaml-3.1.2

关于上面的地址,咱们不能直接拿来进行运用,咱们需求去掉最开端的字符串file://

运转库生成地址

咱们将生成的运转库存放在本机电脑 $HOME/.runtime 目录,这个 $HOME 参数咱们能够引证 process_run 这个库经过下面的办法能够获取到

platformEnvironment["HOME"] // 获取本机  HOME 目录

剖析代码

详细剖析的代码能够参阅下面的文件

ConverRuntimePackage

AnalysisContextCollection

为了能够拿到当时剖析库的代码信息,咱们需求经过库 analyzer 中 AnalysisContextCollection 类来剖析代码信息。

AnalysisContextCollection(
  sdkPath: getDartPath(),
  includedPaths: [join(packagePath, "lib")],
);

第一个参数是本地 Dart 库的地址,不设置剖析就会报错。

第二个参数是剖析源代码的文件夹或者详细文件途径

Dart 库的地址

咱们能够经过下面的代码进行获取

String getDartPath() {
  // 获取当时  Dart  命令的详细途径
  String dartCommandPath = whichSync("dart") ?? "";
  // 将获取的  Dart  途径获取到当时文件夹 之后增加  cache/dart-sdk
  return join(dirname(dartCommandPath), "cache", "dart-sdk");
}

剖析当时库的 pubspec 信息

能够引进 pubspec_parse 这个库来剖析 pubspec.yaml 的信息。

final sourceFile = join(packagePath, "pubspec.yaml");
pubspec = Pubspec.parse(await File(sourceFile).readAsString());

剖析代码文件

咱们从供给的库途径获取到目录下面一切的源文件,咱们默认为一切的的依靠库的源文件都在 lib 这个目录下。

// 获取到当时需求剖析目录下面一切的子元素
List<FileSystemEntity> entitys = await Directory(dir).list(recursive: true).toList();

经过 AnalysisContext 目标拿到对应代码文件的剖析结果

AnalysisContext context = analysisContextCollection.contextFor(path);
SomeResolvedLibraryResult result = await context.currentSession.getResolvedLibrary(path);

SomeResolvedLibraryResult 存在有多个子类,咱们暂时只剖析 ResolvedLibraryResult 这个子类的内容。

剖析 Class

获取当时代码文件一切能够被揭露访问的类

final classes = result.element.units[0].classes.where((element) {
  return !element.name.isPrivate;
});
bool get isPrivate => startsWith("_");

尽管 result.element.units 是一个数组 但是现在还没有遇到存在两个的,所以就暂时用一个座位剖析研究

每一个类剖析出来都是一个 ClassElement 目标,咱们能够从这儿边找到需求的信息。

- String name // 获取类称号
- List<FieldElement> fields // 获取类的特点
	- PropertyAccessorElement getter // 获取只读的特点
	- PropertyAccessorElement setter // 获取能够设置的特点
- List<MethodElement> methods // 获取类的办法
- List<ConstructorElement> constructors // 获取类的结构办法
- Bool isAbstract // 是否是笼统类
剖析 PropertyAccessorElement
- String name // 特点称号
- String isStatic // 是否是静态特点

关于特点的称号咱们要处理自身就带有 $和=号的特别字符

关于 $ 咱们能够增加 \进行搬运

关于尾部带有 = 的咱们需求将 = 号进行移除

剖析 MethodElement
- String name // 获取办法称号
- List<ParameterElement> parameters // 获取办法参数列表
剖析 ConstructorElement
- String name // 结构办法称号可能为空
- List<ParameterElement> parameters // 结构的参数列表
剖析 ParameterElement
- String name // 参数称号
- bool isNamed // 是否是姓名参数
- bool hasDefaultValue // 是否有默认值
- String defaultValueCode // 默认值
剖析 FunctionElement
- String name // 大局办法称号
- List<ParameterElement> parameters // 大局办法参数
剖析 EnumElement
- String name // 枚举称号
- List<FieldElementImpl> constructors // 枚举值
	- String name 枚举值称号

生成代码

咱们已经经过剖析代码拿到了基本的信息,剩下的咱们经过获取的值经过 Mustache 模版语法来完成咱们的生成代码。

关于 Mustache 咱们能够经过 mustache_template 这个库来供给支撑。

flutter_runtime

flutter_runtime是供给运转时根底才能的,后续会依据需求调整。

abstract class FlutterRuntime<T> {
  // 当时运转类的实例
  final T runtime;
  FlutterRuntime(this.runtime);
  // 获取当时类揭露只读的特点
  dynamic getField(String fieldName);
  // 设置类揭露的设置特点
  void setField(String fieldName, dynamic value);
  // 履行当时类的揭露办法
  dynamic call(String methodName, [Map args = const {}]);
  // 依据类名创立当时类实例
  dynamic createInstance(
    String packageName,
    String libraryPath,
    String className, [
    Map args = const {},
  ]) {
    return null;
  }
}

咱们生成的每一个运转时目标都要基于这个笼统类来生成。

pubspec.yaml

关于生成的运转时库,咱们需求创立一个 pubspec.yaml

name: {{pubName}}_runtime
environment:
  sdk: '>=2.18.0 <3.0.0'
dependencies:
  flutter_runtime:
    path: {{{flutterRuntimePath}}}
  {{pubName}}:
    path: {{{pubPath}}}
  darty_json_safe: ^1.0.1
  • pubName

    源包名

  • flutterRuntimePath

    flutter_runtime 依靠的地址

  • pubPath

    源包依靠的地址

生成运转时代码文件
// ignore_for_file: implementation_imports, unused_import
import 'dart:async';
import 'package:flutter_runtime/flutter_runtime.dart';
import 'package:darty_json_safe/darty_json_safe.dart';
# 依据供给的依靠途径生成需求导入的依靠
{{#paths}}
import '{{{sourcePath}}}';
{{/paths}}
# 生成运转时  Class
{{#classes}}
{{>classMustache}}
{{/classes}}
生成运转时 Class
class \${{className}}\$ extends FlutterRuntime<{{className}}>{
# 生成必要的运转时结构器
\${{className}}\$(super.runtime);
# 生成只读特点的运转时办法
{{>getFieldMustache}}
# 生成设置特点的运转时办法
{{>setFieldMustache}}
# 生成办法运转时调用
{{>methodMustache}}
# 生成结构办法的运转时调用
{{>constructorMustache}}
}
只读特点运转时办法
@override
dynamic getField(String fieldName) {
	# 便利只读特点的列表
  {{#getFields}}
  	# 是静态的特点
    {{#isStatic}}
    	if (fieldName == "{{fieldName}}") return {{className}}.{{fieldName}};
    {{/isStatic}}
    # 不是静态特点
    {{^isStatic}}
   	 if (fieldName == "{{fieldName}}") return runtime.{{fieldName}};
    {{/isStatic}}
  {{/getFields}}
}
设置特点的运转时办法
@override
void setField(String fieldName, dynamic value) {
	# 设置特点的列表
  {{#setFields}}
    {{#isStatic}}
      if (fieldName == "{{fieldName}}") {{className}}.{{fieldName}} = value;
    {{/isStatic}}
    {{^isStatic}}
      if (fieldName == "{{fieldName}}") runtime.{{fieldName}} = value;
    {{/isStatic}}
  {{/setFields}}
}
办法的运转时办法
@override
dynamic call(String methodName,[Map args = const {}]) {
	# 类中的办法列表
  {{#methods}}
  {{>functionMustache}}
  {{/methods}}
}
# functionMustache
# 是否是自定义调用代码 针对一些[]这种数组和字典的办法
{{#isCustomCall}}
  if (methodName == '{{methodName}}') return {{{customCallCode}}};
{{/isCustomCall}}
{{^isCustomCall}}
  if (methodName == '{{methodName}}') return runtime.{{methodName}}(
  	# 办法的参数列表
    {{#parameters}}
    	# 是称号参数
      {{#isNamed}}
        {{parameterName}}:{{>createInstanceMustache}}{{>defaultValueMustache}},
      {{/isNamed}}
      # 不是称号参数
      {{^isNamed}}
        {{>createInstanceMustache}}{{>defaultValueMustache}},
      {{/isNamed}}
    {{/parameters}}
  );
{{/isCustomCall}}
结构办法的运转时办法
{{className}}? createRuntimeInstance(String constructorName,[Map args = const {},]) {
	# 是否是笼统办法 笼统办法不支撑结构函数
  {{^isAbstract}}
  	# 结构办法列表
    {{#constructors}}
      if (constructorName == "{{constructorName}}")
        return {{className}}{{#isName}}.{{constructorName}}{{/isName}}(
          {{#parameters}}
            {{#isNamed}}
              {{parameterName}}:{{>createInstanceMustache}}{{>defaultValueMustache}},
            {{/isNamed}}
            {{^isNamed}}
              {{>createInstanceMustache}}{{>defaultValueMustache}},
            {{/isNamed}}
          {{/parameters}}
        );
    {{/constructors}}
  {{/isAbstract}}
  return null;
} 
大局调用的运转时
// ignore_for_file: implementation_imports, unused_import
import 'package:flutter_runtime/flutter_runtime.dart';
import 'package:darty_json_safe/darty_json_safe.dart';
{{#paths}}
import '{{{sourcePath}}}';
{{/paths}}
# 由于大局没有一个对应类 所以就直接用  dynamic  后面能够经过 null  进行创立
class \$GlobalRuntime\$ extends FlutterRuntime<dynamic> {
 \$GlobalRuntime\$(super.runtime);
	# 大局的办法 直接能够调用 不需求 runtime
  dynamic call(String methodName, [Map args = const {}]) {
    {{#functions}}
      if (methodName == '{{methodName}}') return {{methodName}}(
    {{#parameters}}
      {{#isNamed}}
        {{parameterName}}:{{>createInstanceMustache}}{{>defaultValueMustache}},
      {{/isNamed}}
      {{^isNamed}}
        {{>createInstanceMustache}}{{>defaultValueMustache}},
      {{/isNamed}}
    {{/parameters}}
    );
    {{/functions}}
  }
  @override
  getField(String fieldName) {
    {{#getFields}}
        if (fieldName == '{{fieldName}}') return {{fieldValue}};
    {{/getFields}}
  }
  @override
  void setField(String fieldName, value) {
    {{#setFields}}
      if (fieldName == "{{fieldName}}") {{fieldName}} = value;
    {{/setFields}}
  }
  dynamic getEnumValue(String enumName, String constructorName) {
    {{#enums}}
      if (enumName == "{{enumName}}"){
        {{#constructors}}
          if (constructorName == '{{constructorName}}') {
            return {{enumName}}.{{constructorName}};
          }
        {{/constructors}}
      }
    {{/enums}}
    return null;  
  }
} 

一些技能点

怎样让生成的代码不报错?
  • 让生成的类所属的文件作为依靠引进
  • 让默认值关联的目标所属的文件作为依靠引进
关于 [] 和 == 好办法的处理
String? get customCallCode {
    if (name == '[]=' && parameters.length == 2) {
      return '''runtime[args['${parameters[0].name}']] = args['${parameters[1].name}']''';
    } else if (name == '==' && parameters.length == 1) {
      return '''runtime == args['${parameters[0].name}']''';
    } else {
      return null;
    }
  }

如果判断办法称号为 [] 或者 == 就生成自定义的调用代码生成。

获取默认值对应目标所在的库文件途径
String? get defaultValueImportPath {
  if (!hasDefaultValue || this is! DefaultParameterElementImpl) return null;
  DefaultParameterElementImpl parameter = this as DefaultParameterElementImpl;
  final constantInitializer = parameter.constantInitializer;
  if (constantInitializer == null ||
      constantInitializer is! PrefixedIdentifier) return null;
  return constantInitializer.staticElement?.librarySource?.importPath;
}
extension SourceImport on Source {
  String? get importPath {
    final packages =
        Get.find<HomeController>().packageConfig.value?.packages ?? [];
    List<PackageInfo> infos = packages.where((element) {
      return fullName.startsWith(element.rootUri.replaceFirst("file://", ""));
    }).toList();
    if (infos.isEmpty) return null;
    PackageInfo info = infos[0];
    final path = fullName.split("/lib/").last;
    return 'package:${info.name}/$path';
  }
}

运转库生成比方

普通类文件

// ignore_for_file: implementation_imports, unused_import
import 'dart:async';
import 'package:flutter_runtime/flutter_runtime.dart';
import 'package:darty_json_safe/darty_json_safe.dart';
import 'package:yaml/src/event.dart';
class $Event$ extends FlutterRuntime<Event> {
  $Event$(super.runtime);
  @override
  dynamic getField(String fieldName) {
    if (fieldName == "type") return runtime.type;
    if (fieldName == "span") return runtime.span;
  }
  @override
  void setField(String fieldName, dynamic value) {}
  @override
  dynamic call(String methodName, [Map args = const {}]) {
    if (methodName == 'toString') return runtime.toString();
  }
  Event? createRuntimeInstance(
    String constructorName, [
    Map args = const {},
  ]) {
    if (constructorName == "")
      return Event(
        args['type'],
        args['span'],
      );
    return null;
  }
}
class $DocumentStartEvent$ extends FlutterRuntime<DocumentStartEvent> {
  $DocumentStartEvent$(super.runtime);
  @override
  dynamic getField(String fieldName) {
    if (fieldName == "span") return runtime.span;
    if (fieldName == "versionDirective") return runtime.versionDirective;
    if (fieldName == "tagDirectives") return runtime.tagDirectives;
    if (fieldName == "isImplicit") return runtime.isImplicit;
    if (fieldName == "type") return runtime.type;
  }
  @override
  void setField(String fieldName, dynamic value) {}
  @override
  dynamic call(String methodName, [Map args = const {}]) {
    if (methodName == 'toString') return runtime.toString();
  }
  DocumentStartEvent? createRuntimeInstance(
    String constructorName, [
    Map args = const {},
  ]) {
    if (constructorName == "")
      return DocumentStartEvent(
        args['span'],
        versionDirective: args['versionDirective'],
        tagDirectives: args['tagDirectives'],
        isImplicit: args['isImplicit'] ?? true,
      );
    return null;
  }
}
class $DocumentEndEvent$ extends FlutterRuntime<DocumentEndEvent> {
  $DocumentEndEvent$(super.runtime);
  @override
  dynamic getField(String fieldName) {
    if (fieldName == "span") return runtime.span;
    if (fieldName == "isImplicit") return runtime.isImplicit;
    if (fieldName == "type") return runtime.type;
  }
  @override
  void setField(String fieldName, dynamic value) {}
  @override
  dynamic call(String methodName, [Map args = const {}]) {
    if (methodName == 'toString') return runtime.toString();
  }
  DocumentEndEvent? createRuntimeInstance(
    String constructorName, [
    Map args = const {},
  ]) {
    if (constructorName == "")
      return DocumentEndEvent(
        args['span'],
        isImplicit: args['isImplicit'] ?? true,
      );
    return null;
  }
}
class $AliasEvent$ extends FlutterRuntime<AliasEvent> {
  $AliasEvent$(super.runtime);
  @override
  dynamic getField(String fieldName) {
    if (fieldName == "span") return runtime.span;
    if (fieldName == "name") return runtime.name;
    if (fieldName == "type") return runtime.type;
  }
  @override
  void setField(String fieldName, dynamic value) {}
  @override
  dynamic call(String methodName, [Map args = const {}]) {
    if (methodName == 'toString') return runtime.toString();
  }
  AliasEvent? createRuntimeInstance(
    String constructorName, [
    Map args = const {},
  ]) {
    if (constructorName == "")
      return AliasEvent(
        args['span'],
        args['name'],
      );
    return null;
  }
}
class $ScalarEvent$ extends FlutterRuntime<ScalarEvent> {
  $ScalarEvent$(super.runtime);
  @override
  dynamic getField(String fieldName) {
    if (fieldName == "span") return runtime.span;
    if (fieldName == "anchor") return runtime.anchor;
    if (fieldName == "tag") return runtime.tag;
    if (fieldName == "value") return runtime.value;
    if (fieldName == "style") return runtime.style;
    if (fieldName == "type") return runtime.type;
  }
  @override
  void setField(String fieldName, dynamic value) {}
  @override
  dynamic call(String methodName, [Map args = const {}]) {
    if (methodName == 'toString') return runtime.toString();
  }
  ScalarEvent? createRuntimeInstance(
    String constructorName, [
    Map args = const {},
  ]) {
    if (constructorName == "")
      return ScalarEvent(
        args['span'],
        args['value'],
        args['style'],
        anchor: args['anchor'],
        tag: args['tag'],
      );
    return null;
  }
}
class $SequenceStartEvent$ extends FlutterRuntime<SequenceStartEvent> {
  $SequenceStartEvent$(super.runtime);
  @override
  dynamic getField(String fieldName) {
    if (fieldName == "span") return runtime.span;
    if (fieldName == "anchor") return runtime.anchor;
    if (fieldName == "tag") return runtime.tag;
    if (fieldName == "style") return runtime.style;
    if (fieldName == "type") return runtime.type;
  }
  @override
  void setField(String fieldName, dynamic value) {}
  @override
  dynamic call(String methodName, [Map args = const {}]) {}
  SequenceStartEvent? createRuntimeInstance(
    String constructorName, [
    Map args = const {},
  ]) {
    if (constructorName == "")
      return SequenceStartEvent(
        args['span'],
        args['style'],
        anchor: args['anchor'],
        tag: args['tag'],
      );
    return null;
  }
}
class $MappingStartEvent$ extends FlutterRuntime<MappingStartEvent> {
  $MappingStartEvent$(super.runtime);
  @override
  dynamic getField(String fieldName) {
    if (fieldName == "span") return runtime.span;
    if (fieldName == "anchor") return runtime.anchor;
    if (fieldName == "tag") return runtime.tag;
    if (fieldName == "style") return runtime.style;
    if (fieldName == "type") return runtime.type;
  }
  @override
  void setField(String fieldName, dynamic value) {}
  @override
  dynamic call(String methodName, [Map args = const {}]) {}
  MappingStartEvent? createRuntimeInstance(
    String constructorName, [
    Map args = const {},
  ]) {
    if (constructorName == "")
      return MappingStartEvent(
        args['span'],
        args['style'],
        anchor: args['anchor'],
        tag: args['tag'],
      );
    return null;
  }
}

大局运转时

// ignore_for_file: implementation_imports, unused_import
import 'package:flutter_runtime/flutter_runtime.dart';
import 'package:darty_json_safe/darty_json_safe.dart';
import 'package:yaml/src/utils.dart';
import 'package:yaml/src/charcodes.dart';
import 'package:yaml/yaml.dart';
import 'package:yaml/src/equality.dart';
import 'package:yaml/src/yaml_node.dart';
import 'package:yaml/src/event.dart';
import 'package:yaml/src/token.dart';
class $GlobalRuntime$ extends FlutterRuntime<dynamic> {
  $GlobalRuntime$(super.runtime);
  dynamic call(String methodName, [Map args = const {}]) {
    if (methodName == 'loadYaml')
      return loadYaml(
        args['yaml'],
        sourceUrl: args['sourceUrl'],
        recover: args['recover'] ?? false,
        errorListener: args['errorListener'],
      );
    if (methodName == 'loadYamlNode')
      return loadYamlNode(
        args['yaml'],
        sourceUrl: args['sourceUrl'],
        recover: args['recover'] ?? false,
        errorListener: args['errorListener'],
      );
    if (methodName == 'loadYamlDocument')
      return loadYamlDocument(
        args['yaml'],
        sourceUrl: args['sourceUrl'],
        recover: args['recover'] ?? false,
        errorListener: args['errorListener'],
      );
    if (methodName == 'loadYamlStream')
      return loadYamlStream(
        args['yaml'],
        sourceUrl: args['sourceUrl'],
      );
    if (methodName == 'loadYamlDocuments')
      return loadYamlDocuments(
        args['yaml'],
        sourceUrl: args['sourceUrl'],
      );
    if (methodName == 'warn')
      return warn(
        args['message'],
        args['span'],
      );
    if (methodName == 'deepEqualsMap') return deepEqualsMap();
    if (methodName == 'deepEquals')
      return deepEquals(
        args['obj1'],
        args['obj2'],
      );
    if (methodName == 'deepHashCode')
      return deepHashCode(
        args['obj'],
      );
    if (methodName == 'setSpan')
      return setSpan(
        args['node'],
        args['span'],
      );
  }
  @override
  getField(String fieldName) {
    if (fieldName == 'yamlWarningCallback') return yamlWarningCallback;
    if (fieldName == '\$plus') return $plus;
    if (fieldName == '\$minus') return $minus;
    if (fieldName == '\$dot') return $dot;
    if (fieldName == '\$0') return $0;
    if (fieldName == '\$9') return $9;
    if (fieldName == '\$F') return $F;
    if (fieldName == '\$N') return $N;
    if (fieldName == '\$T') return $T;
    if (fieldName == '\$f') return $f;
    if (fieldName == '\$n') return $n;
    if (fieldName == '\$o') return $o;
    if (fieldName == '\$t') return $t;
    if (fieldName == '\$x') return $x;
    if (fieldName == '\$tilde') return $tilde;
  }
  @override
  void setField(String fieldName, value) {
    if (fieldName == "yamlWarningCallback") yamlWarningCallback = value;
  }
  dynamic getEnumValue(String enumName, String constructorName) {
    if (enumName == "EventType") {
      if (constructorName == 'streamStart') {
        return EventType.streamStart;
      }
      if (constructorName == 'streamEnd') {
        return EventType.streamEnd;
      }
      if (constructorName == 'documentStart') {
        return EventType.documentStart;
      }
      if (constructorName == 'documentEnd') {
        return EventType.documentEnd;
      }
      if (constructorName == 'alias') {
        return EventType.alias;
      }
      if (constructorName == 'scalar') {
        return EventType.scalar;
      }
      if (constructorName == 'sequenceStart') {
        return EventType.sequenceStart;
      }
      if (constructorName == 'sequenceEnd') {
        return EventType.sequenceEnd;
      }
      if (constructorName == 'mappingStart') {
        return EventType.mappingStart;
      }
      if (constructorName == 'mappingEnd') {
        return EventType.mappingEnd;
      }
    }
    if (enumName == "TokenType") {
      if (constructorName == 'streamStart') {
        return TokenType.streamStart;
      }
      if (constructorName == 'streamEnd') {
        return TokenType.streamEnd;
      }
      if (constructorName == 'versionDirective') {
        return TokenType.versionDirective;
      }
      if (constructorName == 'tagDirective') {
        return TokenType.tagDirective;
      }
      if (constructorName == 'documentStart') {
        return TokenType.documentStart;
      }
      if (constructorName == 'documentEnd') {
        return TokenType.documentEnd;
      }
      if (constructorName == 'blockSequenceStart') {
        return TokenType.blockSequenceStart;
      }
      if (constructorName == 'blockMappingStart') {
        return TokenType.blockMappingStart;
      }
      if (constructorName == 'blockEnd') {
        return TokenType.blockEnd;
      }
      if (constructorName == 'flowSequenceStart') {
        return TokenType.flowSequenceStart;
      }
      if (constructorName == 'flowSequenceEnd') {
        return TokenType.flowSequenceEnd;
      }
      if (constructorName == 'flowMappingStart') {
        return TokenType.flowMappingStart;
      }
      if (constructorName == 'flowMappingEnd') {
        return TokenType.flowMappingEnd;
      }
      if (constructorName == 'blockEntry') {
        return TokenType.blockEntry;
      }
      if (constructorName == 'flowEntry') {
        return TokenType.flowEntry;
      }
      if (constructorName == 'key') {
        return TokenType.key;
      }
      if (constructorName == 'value') {
        return TokenType.value;
      }
      if (constructorName == 'alias') {
        return TokenType.alias;
      }
      if (constructorName == 'anchor') {
        return TokenType.anchor;
      }
      if (constructorName == 'tag') {
        return TokenType.tag;
      }
      if (constructorName == 'scalar') {
        return TokenType.scalar;
      }
    }
    return null;
  }
}

备注

现在生成运转时的才能还很弱,比方后续将生成目标整理成注册表。还有其他类型的生成支撑,还有将代码转换为能够动态运转的 JSON 文件来运转,包括后续解析运转库来支撑闭包,if/for 等。

我十分希望有感兴趣的一起参加这个社区,让 Flutter 动态化的完成计划更多挑选,让接入和侵入更低。