mirror of
https://github.com/dart-lang/sdk
synced 2024-10-06 15:50:01 +00:00
[pkg/vm] Consolidate kernel compilation options to an object
TEST=ci Change-Id: Ic9a8801bff140b1393decde61917e8c61ab1945d Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/370721 Commit-Queue: Alexander Markov <alexmarkov@google.com> Reviewed-by: Slava Egorov <vegorov@google.com>
This commit is contained in:
parent
31a4bb7f60
commit
6ab01427bd
|
@ -658,22 +658,25 @@ class FrontendCompiler implements CompilerInterface {
|
|||
];
|
||||
}
|
||||
results = await _runWithPrintRedirection(() => compileToKernel(
|
||||
_mainSource, compilerOptions,
|
||||
additionalSources: _additionalSources,
|
||||
nativeAssets: _nativeAssets,
|
||||
includePlatform: options['link-platform'],
|
||||
deleteToStringPackageUris: options['delete-tostring-package-uri'],
|
||||
keepClassNamesImplementing: options['keep-class-names-implementing'],
|
||||
aot: options['aot'],
|
||||
targetOS: options['target-os'],
|
||||
useGlobalTypeFlowAnalysis: options['tfa'],
|
||||
useRapidTypeAnalysis: options['rta'],
|
||||
environmentDefines: environmentDefines,
|
||||
enableAsserts: options['enable-asserts'],
|
||||
useProtobufTreeShakerV2: options['protobuf-tree-shaker-v2'],
|
||||
minimalKernel: options['minimal-kernel'],
|
||||
treeShakeWriteOnlyFields: options['tree-shake-write-only-fields'],
|
||||
fromDillFile: options['from-dill']));
|
||||
new KernelCompilationArguments(
|
||||
source: _mainSource,
|
||||
options: compilerOptions,
|
||||
additionalSources: _additionalSources,
|
||||
nativeAssets: _nativeAssets,
|
||||
includePlatform: options['link-platform'],
|
||||
deleteToStringPackageUris: options['delete-tostring-package-uri'],
|
||||
keepClassNamesImplementing:
|
||||
options['keep-class-names-implementing'],
|
||||
aot: options['aot'],
|
||||
targetOS: options['target-os'],
|
||||
useGlobalTypeFlowAnalysis: options['tfa'],
|
||||
useRapidTypeAnalysis: options['rta'],
|
||||
environmentDefines: environmentDefines,
|
||||
enableAsserts: options['enable-asserts'],
|
||||
useProtobufTreeShakerV2: options['protobuf-tree-shaker-v2'],
|
||||
minimalKernel: options['minimal-kernel'],
|
||||
treeShakeWriteOnlyFields: options['tree-shake-write-only-fields'],
|
||||
fromDillFile: options['from-dill'])));
|
||||
}
|
||||
if (results!.component != null) {
|
||||
transformer?.transform(results.component!);
|
||||
|
@ -776,13 +779,11 @@ class FrontendCompiler implements CompilerInterface {
|
|||
return;
|
||||
}
|
||||
|
||||
final KernelCompilationResults results =
|
||||
await _runWithPrintRedirection(() => compileToKernel(
|
||||
null,
|
||||
_compilerOptions,
|
||||
final KernelCompilationResults results = await _runWithPrintRedirection(
|
||||
() => compileToKernel(new KernelCompilationArguments(
|
||||
options: _compilerOptions,
|
||||
nativeAssets: _nativeAssets,
|
||||
environmentDefines: {},
|
||||
));
|
||||
)));
|
||||
_nativeAssetsLibrary = results.nativeAssetsLibrary;
|
||||
}
|
||||
|
||||
|
|
|
@ -129,11 +129,10 @@ class _Client {
|
|||
final String uriStr = arguments.required<String>('uri');
|
||||
|
||||
final vm.KernelCompilationResults compilationResults =
|
||||
await vm.compileToKernel(
|
||||
Uri.parse(uriStr),
|
||||
compilerOptions,
|
||||
environmentDefines: {},
|
||||
);
|
||||
await vm.compileToKernel(new vm.KernelCompilationArguments(
|
||||
source: Uri.parse(uriStr),
|
||||
options: compilerOptions,
|
||||
));
|
||||
|
||||
return _serializeComponentWithoutPlatform(
|
||||
compilationResults.component!,
|
||||
|
|
|
@ -33,7 +33,7 @@ import 'package:kernel/core_types.dart' show CoreTypes;
|
|||
import 'package:kernel/kernel.dart';
|
||||
import 'package:kernel/target/targets.dart' show TargetFlags, getTarget;
|
||||
import 'package:vm/kernel_front_end.dart'
|
||||
show runGlobalTransformations, ErrorDetector;
|
||||
show runGlobalTransformations, ErrorDetector, KernelCompilationArguments;
|
||||
import 'package:vm/modular/target/install.dart' show installAdditionalTargets;
|
||||
import 'package:vm/transformations/type_flow/transformer.dart' as globalTypeFlow
|
||||
show transformComponent;
|
||||
|
@ -116,18 +116,15 @@ Future main(List<String> args) async {
|
|||
|
||||
// The [component] is treeshaken and has TFA annotations. Write output.
|
||||
if (argResults['aot']) {
|
||||
const bool useGlobalTypeFlowAnalysis = true;
|
||||
const bool enableAsserts = false;
|
||||
const bool useProtobufAwareTreeShakerV2 = true;
|
||||
final nopErrorDetector = ErrorDetector();
|
||||
runGlobalTransformations(
|
||||
target,
|
||||
component,
|
||||
useGlobalTypeFlowAnalysis,
|
||||
enableAsserts,
|
||||
useProtobufAwareTreeShakerV2,
|
||||
nopErrorDetector,
|
||||
);
|
||||
target,
|
||||
component,
|
||||
nopErrorDetector,
|
||||
KernelCompilationArguments(
|
||||
useGlobalTypeFlowAnalysis: true,
|
||||
enableAsserts: false,
|
||||
useProtobufTreeShakerV2: true));
|
||||
} else {
|
||||
globalTypeFlow.transformComponent(target, CoreTypes(component), component,
|
||||
treeShakeProtobufs: true, treeShakeSignatures: false);
|
||||
|
|
|
@ -334,7 +334,9 @@ Future<int> runCompiler(ArgResults options, String usage) async {
|
|||
return badUsageExitCode;
|
||||
}
|
||||
|
||||
final results = await compileToKernel(mainUri, compilerOptions,
|
||||
final results = await compileToKernel(KernelCompilationArguments(
|
||||
source: mainUri,
|
||||
options: compilerOptions,
|
||||
additionalSources: additionalSources,
|
||||
nativeAssets: nativeAssetsUri,
|
||||
resourcesFile: resourcesFileUri,
|
||||
|
@ -351,7 +353,7 @@ Future<int> runCompiler(ArgResults options, String usage) async {
|
|||
minimalKernel: minimalKernel,
|
||||
treeShakeWriteOnlyFields: treeShakeWriteOnlyFields,
|
||||
targetOS: targetOS,
|
||||
fromDillFile: fromDillFile);
|
||||
fromDillFile: fromDillFile));
|
||||
|
||||
errorPrinter.printCompilationMessages();
|
||||
|
||||
|
@ -440,33 +442,61 @@ class KernelCompilationResults {
|
|||
});
|
||||
}
|
||||
|
||||
// Arguments for [compileToKernel].
|
||||
class KernelCompilationArguments {
|
||||
final Uri? source;
|
||||
final CompilerOptions? options;
|
||||
final List<Uri> additionalSources;
|
||||
final Uri? nativeAssets;
|
||||
final Uri? resourcesFile;
|
||||
final bool includePlatform;
|
||||
final List<String> deleteToStringPackageUris;
|
||||
final List<String> keepClassNamesImplementing;
|
||||
final bool aot;
|
||||
final Uri? dynamicInterface;
|
||||
final Map<String, String> environmentDefines; // Should be mutable.
|
||||
final bool enableAsserts;
|
||||
final bool useGlobalTypeFlowAnalysis;
|
||||
final bool useRapidTypeAnalysis;
|
||||
final bool treeShakeWriteOnlyFields;
|
||||
final bool useProtobufTreeShakerV2;
|
||||
final bool minimalKernel;
|
||||
final String? targetOS;
|
||||
final String? fromDillFile;
|
||||
|
||||
KernelCompilationArguments({
|
||||
this.source,
|
||||
this.options,
|
||||
this.additionalSources = const <Uri>[],
|
||||
this.nativeAssets,
|
||||
this.resourcesFile,
|
||||
this.includePlatform = false,
|
||||
this.deleteToStringPackageUris = const <String>[],
|
||||
this.keepClassNamesImplementing = const <String>[],
|
||||
this.aot = false,
|
||||
this.dynamicInterface,
|
||||
Map<String, String>? environmentDefines,
|
||||
this.enableAsserts = true,
|
||||
this.useGlobalTypeFlowAnalysis = false,
|
||||
this.useRapidTypeAnalysis = true,
|
||||
this.treeShakeWriteOnlyFields = false,
|
||||
this.useProtobufTreeShakerV2 = false,
|
||||
this.minimalKernel = false,
|
||||
this.targetOS,
|
||||
this.fromDillFile,
|
||||
}) : environmentDefines = environmentDefines ?? {};
|
||||
}
|
||||
|
||||
/// Generates a kernel representation of the program whose main library is in
|
||||
/// the given [source]. Intended for whole program (non-modular) compilation.
|
||||
/// the given [args.source]. Intended for whole program (non-modular) compilation.
|
||||
///
|
||||
/// VM-specific replacement of [kernelForProgram].
|
||||
///
|
||||
/// Either [source], or [nativeAssets], or both must be non-null.
|
||||
/// Either [arg.source], or [args.nativeAssets], or both must be non-null.
|
||||
Future<KernelCompilationResults> compileToKernel(
|
||||
Uri? source,
|
||||
CompilerOptions options, {
|
||||
List<Uri> additionalSources = const <Uri>[],
|
||||
Uri? nativeAssets,
|
||||
Uri? resourcesFile,
|
||||
bool includePlatform = false,
|
||||
List<String> deleteToStringPackageUris = const <String>[],
|
||||
List<String> keepClassNamesImplementing = const <String>[],
|
||||
Uri? dynamicInterface,
|
||||
bool aot = false,
|
||||
bool useGlobalTypeFlowAnalysis = false,
|
||||
bool useRapidTypeAnalysis = true,
|
||||
required Map<String, String> environmentDefines,
|
||||
bool enableAsserts = true,
|
||||
bool useProtobufTreeShakerV2 = false,
|
||||
bool minimalKernel = false,
|
||||
bool treeShakeWriteOnlyFields = false,
|
||||
String? targetOS = null,
|
||||
String? fromDillFile = null,
|
||||
}) async {
|
||||
KernelCompilationArguments args) async {
|
||||
final options = args.options!;
|
||||
|
||||
// Replace error handler to detect if there are compilation errors.
|
||||
final errorDetector =
|
||||
new ErrorDetector(previousErrorHandler: options.onDiagnostic);
|
||||
|
@ -474,8 +504,8 @@ Future<KernelCompilationResults> compileToKernel(
|
|||
|
||||
final nativeAssetsLibrary =
|
||||
await NativeAssetsSynthesizer.synthesizeLibraryFromYamlFile(
|
||||
nativeAssets, errorDetector);
|
||||
if (source == null) {
|
||||
args.nativeAssets, errorDetector);
|
||||
if (args.source == null) {
|
||||
return KernelCompilationResults.named(
|
||||
nativeAssetsLibrary: nativeAssetsLibrary,
|
||||
);
|
||||
|
@ -483,15 +513,16 @@ Future<KernelCompilationResults> compileToKernel(
|
|||
|
||||
final target = options.target!;
|
||||
options.environmentDefines =
|
||||
target.updateEnvironmentDefines(environmentDefines);
|
||||
target.updateEnvironmentDefines(args.environmentDefines);
|
||||
|
||||
CompilerResult? compilerResult;
|
||||
final fromDillFile = args.fromDillFile;
|
||||
if (fromDillFile != null) {
|
||||
compilerResult =
|
||||
await loadKernel(options.fileSystem, resolveInputUri(fromDillFile));
|
||||
} else {
|
||||
compilerResult = await kernelForProgram(source, options,
|
||||
additionalSources: additionalSources);
|
||||
compilerResult = await kernelForProgram(args.source!, options,
|
||||
additionalSources: args.additionalSources);
|
||||
}
|
||||
final Component? component = compilerResult?.component;
|
||||
|
||||
|
@ -501,27 +532,18 @@ Future<KernelCompilationResults> compileToKernel(
|
|||
|
||||
Set<Library> loadedLibraries = createLoadedLibrariesSet(
|
||||
compilerResult?.loadedComponents, compilerResult?.sdkComponent,
|
||||
includePlatform: includePlatform);
|
||||
includePlatform: args.includePlatform);
|
||||
|
||||
if (deleteToStringPackageUris.isNotEmpty && component != null) {
|
||||
if (args.deleteToStringPackageUris.isNotEmpty && component != null) {
|
||||
to_string_transformer.transformComponent(
|
||||
component, deleteToStringPackageUris);
|
||||
component, args.deleteToStringPackageUris);
|
||||
}
|
||||
|
||||
// Run global transformations only if component is correct.
|
||||
if ((aot || minimalKernel) && component != null) {
|
||||
await runGlobalTransformations(target, component, useGlobalTypeFlowAnalysis,
|
||||
enableAsserts, useProtobufTreeShakerV2, errorDetector,
|
||||
environmentDefines: options.environmentDefines,
|
||||
targetOS: targetOS,
|
||||
minimalKernel: minimalKernel,
|
||||
treeShakeWriteOnlyFields: treeShakeWriteOnlyFields,
|
||||
useRapidTypeAnalysis: useRapidTypeAnalysis,
|
||||
keepClassNamesImplementing: keepClassNamesImplementing,
|
||||
dynamicInterface: dynamicInterface,
|
||||
resourcesFile: resourcesFile);
|
||||
if ((args.aot || args.minimalKernel) && component != null) {
|
||||
await runGlobalTransformations(target, component, errorDetector, args);
|
||||
|
||||
if (minimalKernel) {
|
||||
if (args.minimalKernel) {
|
||||
// compiledSources is component.uriToSource.keys.
|
||||
// Make a copy of compiledSources to detach it from
|
||||
// component.uriToSource which is cleared below.
|
||||
|
@ -570,26 +592,14 @@ Set<Library> createLoadedLibrariesSet(
|
|||
return loadedLibraries;
|
||||
}
|
||||
|
||||
Future runGlobalTransformations(
|
||||
Target target,
|
||||
Component component,
|
||||
bool useGlobalTypeFlowAnalysis,
|
||||
bool enableAsserts,
|
||||
bool useProtobufTreeShakerV2,
|
||||
ErrorDetector errorDetector,
|
||||
{bool minimalKernel = false,
|
||||
bool treeShakeWriteOnlyFields = false,
|
||||
bool useRapidTypeAnalysis = true,
|
||||
Map<String, String>? environmentDefines,
|
||||
List<String>? keepClassNamesImplementing,
|
||||
Uri? dynamicInterface,
|
||||
String? targetOS,
|
||||
Uri? resourcesFile}) async {
|
||||
Future runGlobalTransformations(Target target, Component component,
|
||||
ErrorDetector errorDetector, KernelCompilationArguments args) async {
|
||||
assert(!target.flags.supportMirrors);
|
||||
if (errorDetector.hasCompilationErrors) return;
|
||||
|
||||
final coreTypes = new CoreTypes(component);
|
||||
|
||||
final dynamicInterface = args.dynamicInterface;
|
||||
if (dynamicInterface != null) {
|
||||
dynamic_interface_annotator.annotateComponent(
|
||||
File(dynamicInterface.toFilePath()).readAsStringSync(),
|
||||
|
@ -608,21 +618,22 @@ Future runGlobalTransformations(
|
|||
|
||||
// Perform unreachable code elimination, which should be performed before
|
||||
// type flow analysis so TFA won't take unreachable code into account.
|
||||
final targetOS = args.targetOS;
|
||||
final os = targetOS != null ? TargetOS.fromString(targetOS)! : null;
|
||||
final evaluator = vm_constant_evaluator.VMConstantEvaluator.create(
|
||||
target, component, os,
|
||||
enableAsserts: enableAsserts,
|
||||
environmentDefines: environmentDefines,
|
||||
enableAsserts: args.enableAsserts,
|
||||
environmentDefines: args.environmentDefines,
|
||||
coreTypes: coreTypes);
|
||||
unreachable_code_elimination.transformComponent(
|
||||
target, component, evaluator, enableAsserts);
|
||||
target, component, evaluator, args.enableAsserts);
|
||||
|
||||
if (useGlobalTypeFlowAnalysis) {
|
||||
if (args.useGlobalTypeFlowAnalysis) {
|
||||
globalTypeFlow.transformComponent(target, coreTypes, component,
|
||||
treeShakeSignatures: !minimalKernel,
|
||||
treeShakeWriteOnlyFields: treeShakeWriteOnlyFields,
|
||||
treeShakeProtobufs: useProtobufTreeShakerV2,
|
||||
useRapidTypeAnalysis: useRapidTypeAnalysis);
|
||||
treeShakeSignatures: !args.minimalKernel,
|
||||
treeShakeWriteOnlyFields: args.treeShakeWriteOnlyFields,
|
||||
treeShakeProtobufs: args.useProtobufTreeShakerV2,
|
||||
useRapidTypeAnalysis: args.useRapidTypeAnalysis);
|
||||
} else {
|
||||
devirtualization.transformComponent(coreTypes, component);
|
||||
no_dynamic_invocations_annotator.transformComponent(component);
|
||||
|
@ -638,10 +649,11 @@ Future runGlobalTransformations(
|
|||
// We don't know yet whether gen_snapshot will want to do obfuscation, but if
|
||||
// it does it will need the obfuscation prohibitions.
|
||||
obfuscationProhibitions.transformComponent(
|
||||
component, coreTypes, target, hierarchy, keepClassNamesImplementing);
|
||||
component, coreTypes, target, hierarchy, args.keepClassNamesImplementing);
|
||||
|
||||
deferred_loading.transformComponent(component, coreTypes, target);
|
||||
|
||||
final resourcesFile = args.resourcesFile;
|
||||
if (resourcesFile != null) {
|
||||
resource_identifier.transformComponent(component, resourcesFile);
|
||||
}
|
||||
|
|
|
@ -55,16 +55,14 @@ main() async {
|
|||
verifyComponent(
|
||||
vmTarget, VerificationStage.afterModularTransformations, component);
|
||||
|
||||
const useGlobalTypeFlowAnalysis = true;
|
||||
const enableAsserts = false;
|
||||
const useProtobufTreeShakerV2 = false;
|
||||
await runGlobalTransformations(
|
||||
vmTarget,
|
||||
component,
|
||||
useGlobalTypeFlowAnalysis,
|
||||
enableAsserts,
|
||||
useProtobufTreeShakerV2,
|
||||
ErrorDetector());
|
||||
ErrorDetector(),
|
||||
KernelCompilationArguments(
|
||||
useGlobalTypeFlowAnalysis: true,
|
||||
enableAsserts: false,
|
||||
useProtobufTreeShakerV2: false));
|
||||
|
||||
// Verify after running global transformations.
|
||||
verifyComponent(
|
||||
|
|
|
@ -12,7 +12,7 @@ import 'package:kernel/target/targets.dart';
|
|||
import 'package:kernel/verifier.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:vm/kernel_front_end.dart'
|
||||
show runGlobalTransformations, ErrorDetector;
|
||||
show runGlobalTransformations, ErrorDetector, KernelCompilationArguments;
|
||||
import 'package:vm/modular/target/vm.dart' show VmTarget;
|
||||
import 'package:vm/modular/transformations/ffi/native.dart'
|
||||
show transformLibraries;
|
||||
|
@ -57,19 +57,17 @@ runTestCaseAot(Uri source) async {
|
|||
Component component = await compileTestCaseToKernelProgram(source,
|
||||
target: target, experimentalFlags: []);
|
||||
|
||||
const bool useGlobalTypeFlowAnalysis = true;
|
||||
const bool enableAsserts = false;
|
||||
const bool useProtobufAwareTreeShakerV2 = true;
|
||||
final nopErrorDetector = ErrorDetector();
|
||||
runGlobalTransformations(
|
||||
target,
|
||||
component,
|
||||
useGlobalTypeFlowAnalysis,
|
||||
enableAsserts,
|
||||
useProtobufAwareTreeShakerV2,
|
||||
nopErrorDetector,
|
||||
treeShakeWriteOnlyFields: true,
|
||||
);
|
||||
target,
|
||||
component,
|
||||
nopErrorDetector,
|
||||
KernelCompilationArguments(
|
||||
useGlobalTypeFlowAnalysis: true,
|
||||
enableAsserts: false,
|
||||
useProtobufTreeShakerV2: true,
|
||||
treeShakeWriteOnlyFields: true,
|
||||
));
|
||||
|
||||
verifyComponent(
|
||||
target, VerificationStage.afterGlobalTransformations, component);
|
||||
|
|
|
@ -14,7 +14,7 @@ import 'package:path/path.dart' as path;
|
|||
import 'package:test/test.dart';
|
||||
|
||||
import 'package:vm/kernel_front_end.dart'
|
||||
show runGlobalTransformations, ErrorDetector;
|
||||
show runGlobalTransformations, ErrorDetector, KernelCompilationArguments;
|
||||
import 'package:vm/modular/target/vm.dart' show VmTarget;
|
||||
import 'package:vm/transformations/type_flow/transformer.dart' as globalTypeFlow
|
||||
show transformComponent;
|
||||
|
@ -79,18 +79,16 @@ Future<void> compileAOT(Uri source) async {
|
|||
// Imitate the global transformations as run by the protobuf-aware tree shaker
|
||||
// in AOT mode.
|
||||
// Copied verbatim from pkg/vm/bin/protobuf_aware_treeshaker.dart.
|
||||
const bool useGlobalTypeFlowAnalysis = true;
|
||||
const bool enableAsserts = false;
|
||||
const bool useProtobufAwareTreeShakerV2 = true;
|
||||
final nopErrorDetector = ErrorDetector();
|
||||
runGlobalTransformations(
|
||||
target,
|
||||
component,
|
||||
useGlobalTypeFlowAnalysis,
|
||||
enableAsserts,
|
||||
useProtobufAwareTreeShakerV2,
|
||||
nopErrorDetector,
|
||||
);
|
||||
target,
|
||||
component,
|
||||
nopErrorDetector,
|
||||
KernelCompilationArguments(
|
||||
useGlobalTypeFlowAnalysis: true,
|
||||
enableAsserts: false,
|
||||
useProtobufTreeShakerV2: true,
|
||||
));
|
||||
}
|
||||
|
||||
main() {
|
||||
|
|
|
@ -10,7 +10,7 @@ import 'package:kernel/target/targets.dart';
|
|||
import 'package:kernel/verifier.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:vm/kernel_front_end.dart'
|
||||
show runGlobalTransformations, ErrorDetector;
|
||||
show runGlobalTransformations, ErrorDetector, KernelCompilationArguments;
|
||||
import 'package:vm/modular/target/vm.dart' show VmTarget;
|
||||
|
||||
import '../common_test_utils.dart';
|
||||
|
@ -33,9 +33,6 @@ void runTestCaseAot(Uri source, bool throws) async {
|
|||
}
|
||||
}
|
||||
|
||||
const bool useGlobalTypeFlowAnalysis = true;
|
||||
const bool enableAsserts = false;
|
||||
const bool useProtobufAwareTreeShakerV2 = true;
|
||||
final nopErrorDetector = ErrorDetector();
|
||||
|
||||
var tempDir = Directory.systemTemp.createTempSync().path;
|
||||
|
@ -44,15 +41,16 @@ void runTestCaseAot(Uri source, bool throws) async {
|
|||
path: path.join(tempDir, 'resources.json'),
|
||||
);
|
||||
runGlobalTransformations(
|
||||
target,
|
||||
component,
|
||||
useGlobalTypeFlowAnalysis,
|
||||
enableAsserts,
|
||||
useProtobufAwareTreeShakerV2,
|
||||
nopErrorDetector,
|
||||
treeShakeWriteOnlyFields: true,
|
||||
resourcesFile: resourcesFile,
|
||||
);
|
||||
target,
|
||||
component,
|
||||
nopErrorDetector,
|
||||
KernelCompilationArguments(
|
||||
useGlobalTypeFlowAnalysis: true,
|
||||
enableAsserts: false,
|
||||
useProtobufTreeShakerV2: true,
|
||||
treeShakeWriteOnlyFields: true,
|
||||
resourcesFile: resourcesFile,
|
||||
));
|
||||
|
||||
verifyComponent(
|
||||
target,
|
||||
|
|
Loading…
Reference in a new issue