|  | // Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file | 
|  | // for details. All rights reserved. Use of this source code is governed by a | 
|  | // BSD-style license that can be found in the LICENSE file. | 
|  |  | 
|  | import 'dart:io'; | 
|  | import 'package:expect/async_helper.dart'; | 
|  | import 'package:compiler/src/closure.dart'; | 
|  | import 'package:compiler/src/common.dart'; | 
|  | import 'package:compiler/src/compiler.dart'; | 
|  | import 'package:compiler/src/elements/entities.dart'; | 
|  | import 'package:compiler/src/js_model/element_map.dart'; | 
|  | import 'package:compiler/src/js_model/js_strategy.dart'; | 
|  | import 'package:compiler/src/js_model/js_world.dart'; | 
|  | import 'package:kernel/ast.dart' as ir; | 
|  | import '../equivalence/id_equivalence.dart'; | 
|  | import '../equivalence/id_equivalence_helper.dart'; | 
|  |  | 
|  | const List<String> skip = []; | 
|  |  | 
|  | main(List<String> args) { | 
|  | runTests(args); | 
|  | } | 
|  |  | 
|  | runTests(List<String> args, [int? shardIndex]) { | 
|  | runTestsCommon( | 
|  | args, | 
|  | shardIndex: shardIndex, | 
|  | shards: 2, | 
|  | directory: 'data', | 
|  | skip: skip, | 
|  | options: const [], | 
|  | ); | 
|  | } | 
|  |  | 
|  | runTestsCommon( | 
|  | List<String> args, { | 
|  | int? shardIndex, | 
|  | required int shards, | 
|  | required String directory, | 
|  | required List<String> options, | 
|  | required List<String> skip, | 
|  | }) { | 
|  | asyncTest(() async { | 
|  | Directory dataDir = Directory.fromUri(Platform.script.resolve(directory)); | 
|  | await checkTests( | 
|  | dataDir, | 
|  | const CodegenDataComputer(), | 
|  | forUserLibrariesOnly: true, | 
|  | args: args, | 
|  | options: options, | 
|  | testedConfigs: allInternalConfigs + [canaryConfig], | 
|  | skip: skip, | 
|  | shardIndex: shardIndex ?? 0, | 
|  | shards: shardIndex == null ? 1 : shards, | 
|  | ); | 
|  | }); | 
|  | } | 
|  |  | 
|  | class CodegenDataComputer extends DataComputer<String> { | 
|  | const CodegenDataComputer(); | 
|  |  | 
|  | /// Compute generated code for [member]. | 
|  | /// | 
|  | /// Fills [actualMap] with the data. | 
|  | @override | 
|  | void computeMemberData( | 
|  | Compiler compiler, | 
|  | MemberEntity member, | 
|  | Map<Id, ActualData<String>> actualMap, { | 
|  | bool verbose = false, | 
|  | }) { | 
|  | JClosedWorld closedWorld = compiler.backendClosedWorldForTesting!; | 
|  | JsToElementMap elementMap = closedWorld.elementMap; | 
|  | MemberDefinition definition = elementMap.getMemberDefinition(member); | 
|  | CodegenIrComputer( | 
|  | compiler.reporter, | 
|  | actualMap, | 
|  | elementMap, | 
|  | member, | 
|  | compiler.backendStrategy, | 
|  | closedWorld.closureDataLookup, | 
|  | ).run(definition.node); | 
|  | } | 
|  |  | 
|  | @override | 
|  | DataInterpreter<String> get dataValidator => const CodeDataInterpreter(); | 
|  | } | 
|  |  | 
|  | /// AST visitor for computing codegen data for a member. | 
|  | class CodegenIrComputer extends IrDataExtractor<String> { | 
|  | final JsBackendStrategy _backendStrategy; | 
|  | final JsToElementMap _elementMap; | 
|  | final ClosureData _closureDataLookup; | 
|  |  | 
|  | CodegenIrComputer( | 
|  | DiagnosticReporter reporter, | 
|  | Map<Id, ActualData<String>> actualMap, | 
|  | this._elementMap, | 
|  | MemberEntity member, | 
|  | this._backendStrategy, | 
|  | this._closureDataLookup, | 
|  | ) : super(reporter, actualMap); | 
|  |  | 
|  | String? getMemberValue(MemberEntity member) { | 
|  | if (member is FunctionEntity) { | 
|  | return _backendStrategy.getGeneratedCodeForTesting(member); | 
|  | } | 
|  | return null; | 
|  | } | 
|  |  | 
|  | @override | 
|  | String? computeMemberValue(Id id, ir.Member node) { | 
|  | return getMemberValue(_elementMap.getMember(node)); | 
|  | } | 
|  |  | 
|  | @override | 
|  | String? computeNodeValue(Id id, ir.TreeNode node) { | 
|  | if (node is ir.FunctionExpression || node is ir.FunctionDeclaration) { | 
|  | ClosureRepresentationInfo info = _closureDataLookup.getClosureInfo( | 
|  | node as ir.LocalFunction, | 
|  | ); | 
|  | return getMemberValue(info.callMethod!); | 
|  | } | 
|  | return null; | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Default data interpreter for string data representing compiled JavaScript | 
|  | /// code. | 
|  | /// | 
|  | /// The data annotation reader strips out newlines and indentation so the | 
|  | /// comparison needs to compensate. | 
|  | /// | 
|  | /// The special data annotation `ignore` always passes, so we don't have to | 
|  | /// track uninteresting code like a 'main' program. | 
|  | class CodeDataInterpreter implements DataInterpreter<String> { | 
|  | const CodeDataInterpreter(); | 
|  |  | 
|  | String _clean(String code) => code.replaceAll(_re, ''); | 
|  | static final RegExp _re = RegExp(r'[\n\r]\s*'); | 
|  |  | 
|  | @override | 
|  | String? isAsExpected(String? actualData, String? expectedData) { | 
|  | actualData ??= ''; | 
|  | expectedData ??= ''; | 
|  | if (expectedData == 'ignore') return null; | 
|  | if (_clean(actualData) != _clean(expectedData)) { | 
|  | return 'Expected $expectedData, found $actualData'; | 
|  | } | 
|  | return null; | 
|  | } | 
|  |  | 
|  | @override | 
|  | bool isEmpty(String actualData) { | 
|  | return _clean(actualData) == ''; | 
|  | } | 
|  |  | 
|  | @override | 
|  | String getText(String actualData, [String? indentation]) { | 
|  | return actualData; | 
|  | } | 
|  | } |