Ненавязчивое решение горячего обновления Flutter (создание библиотеки времени выполнения)

Ненавязчивое решение горячего обновления Flutter (создание библиотеки времени выполнения)

гид

1 Ненавязчивое решение горячего обновления Flutter (обсуждение)

Flutter_runtime_ide генерирует IDE при запуске

Давненько я не писал статей на эту тему.Основная причина в том, что времени на исследования очень мало каждый день, а во-вторых, я занят изучением того, как сгенерировать runtime-библиотеку. Хотя есть еще много вещей, которые еще не реализованы, позвольте мне сначала синхронизировать примерно завершенную реализацию.

В основном завершите поддержку для следующего поколения библиотеки времени выполнения

  • Доступ к свойствам класса только для чтения
  • Настройки свойства настройки класса
  • вызов метода инициализации класса
  • вызов метода класса
  • Приобретение статических свойств класса
  • Установка статических свойств класса
  • вызов метода класса
  • Приобретение глобальных атрибутов
  • Настройки глобальных свойств
  • глобальный вызов метода
  • Получение базового значения перечисления

Проанализируйте все зависимости текущей библиотеки

паб получить

Чтобы библиотека, которую необходимо проанализировать, сгенерировала все библиотеки, поддерживающие поддержку времени выполнения, необходимо проанализировать все списки зависимых библиотек, что требует от нас 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 , которую я выпустил ранее , но я не использую официальную библиотеку синтаксического анализа.

Создать библиотеку времени выполнения

Мы помещаем проанализированную выше информацию о конфигурации зависимостей для глобального доступа, что нам удобно для последующего поиска и импорта классов связанных типов.

Путь к библиотеке анализа

Мы можем получить конкретный и подробный адрес зависимой библиотеки из поля rootUri в PackageInfo.Каждый адрес является зависимостью соответствующей версии библиотеки.В качестве примера возьмем зависимую библиотеку 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

АнализКонтекстКоллекция

为了可以拿到当前分析库的代码信息,我们需要通过库 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 \${{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 и меньше доступа и вторжений.

Supongo que te gusta

Origin juejin.im/post/7246676109613416503
Recomendado
Clasificación