| Index: tests/compiler/dart2js/equivalence/id_equivalence_helper.dart
|
| diff --git a/tests/compiler/dart2js/equivalence/id_equivalence_helper.dart b/tests/compiler/dart2js/equivalence/id_equivalence_helper.dart
|
| index ab3b9a7f2cf596432565097b70262ec4dba134ae..81de9b38b7ebf97de87c33d1ba05e930a137e674 100644
|
| --- a/tests/compiler/dart2js/equivalence/id_equivalence_helper.dart
|
| +++ b/tests/compiler/dart2js/equivalence/id_equivalence_helper.dart
|
| @@ -2,35 +2,73 @@
|
| // 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:async';
|
| +import 'package:compiler/src/commandline_options.dart';
|
| import 'package:compiler/src/common.dart';
|
| import 'package:compiler/src/common_elements.dart';
|
| import 'package:compiler/src/compiler.dart';
|
| import 'package:compiler/src/elements/elements.dart';
|
| import 'package:compiler/src/elements/entities.dart';
|
| import 'package:compiler/src/resolution/tree_elements.dart';
|
| -import 'package:compiler/src/tree/nodes.dart';
|
| +import 'package:compiler/src/tree/nodes.dart' as ast;
|
| import 'package:expect/expect.dart';
|
| +import 'package:kernel/ast.dart' as ir;
|
|
|
| import '../annotated_code_helper.dart';
|
| import '../memory_compiler.dart';
|
| import '../equivalence/id_equivalence.dart';
|
| +import '../kernel/compiler_helper.dart';
|
|
|
| -typedef void CheckMemberFunction(
|
| - Compiler compiler, Map<Id, String> expectedMap, MemberEntity member);
|
| +/// Function that compiles [code] with [options] and returns the [Compiler] object.
|
| +typedef Future<Compiler> CompileFunction(
|
| + AnnotatedCode code, Uri mainUri, List<String> options);
|
|
|
| -/// Compiles the [annotatedCode] with the provided [options] and calls
|
| -/// [checkMember] for each member in the code providing the map from [Id] to
|
| -/// annotation. Any [Id] left in the map will be reported as missing.
|
| -checkCode(String annotatedCode, CheckMemberFunction checkMember,
|
| +/// Function that computes a data mapping for [member].
|
| +///
|
| +/// Fills [actualMap] with the data and [sourceSpanMap] with the source spans
|
| +/// for the data origin.
|
| +typedef void ComputeMemberDataFunction(Compiler compiler, MemberEntity member,
|
| + Map<Id, String> actualMap, Map<Id, SourceSpan> sourceSpanMap);
|
| +
|
| +/// Compile [code] from .dart sources.
|
| +Future<Compiler> compileFromSource(
|
| + AnnotatedCode code, Uri mainUri, List<String> options) async {
|
| + Compiler compiler = compilerFor(
|
| + memorySourceFiles: {'main.dart': code.sourceCode}, options: options);
|
| + compiler.stopAfterTypeInference = true;
|
| + await compiler.run(mainUri);
|
| + return compiler;
|
| +}
|
| +
|
| +/// Compile [code] from .dill sources.
|
| +Future<Compiler> compileFromDill(
|
| + AnnotatedCode code, Uri mainUri, List<String> options) async {
|
| + Compiler compiler = await compileWithDill(
|
| + mainUri,
|
| + {'main.dart': code.sourceCode},
|
| + [Flags.disableTypeInference]..addAll(options),
|
| + beforeRun: (Compiler compiler) {
|
| + compiler.stopAfterTypeInference = true;
|
| + });
|
| + return compiler;
|
| +}
|
| +
|
| +/// Compute expected and actual data for all members defined in [annotatedCode].
|
| +///
|
| +/// Actual data is computed using [computeMemberData] and [code] is compiled
|
| +/// using [compileFunction].
|
| +Future<IdData> computeData(
|
| + String annotatedCode,
|
| + ComputeMemberDataFunction computeMemberData,
|
| + CompileFunction compileFunction,
|
| {List<String> options: const <String>[]}) async {
|
| AnnotatedCode code =
|
| new AnnotatedCode.fromText(annotatedCode, commentStart, commentEnd);
|
| Map<Id, String> expectedMap = computeExpectedMap(code);
|
| - Compiler compiler = compilerFor(
|
| - memorySourceFiles: {'main.dart': code.sourceCode}, options: options);
|
| - compiler.stopAfterTypeInference = true;
|
| + Map<Id, String> actualMap = <Id, String>{};
|
| + Map<Id, SourceSpan> sourceSpanMap = <Id, SourceSpan>{};
|
| Uri mainUri = Uri.parse('memory:main.dart');
|
| - await compiler.run(mainUri);
|
| + Compiler compiler = await compileFunction(code, mainUri, options);
|
| ElementEnvironment elementEnvironment =
|
| compiler.backendClosedWorldForTesting.elementEnvironment;
|
| LibraryEntity mainLibrary = elementEnvironment.mainLibrary;
|
| @@ -38,22 +76,62 @@ checkCode(String annotatedCode, CheckMemberFunction checkMember,
|
| elementEnvironment.forEachClassMember(cls,
|
| (ClassEntity declarer, MemberEntity member) {
|
| if (cls == declarer) {
|
| - checkMember(compiler, expectedMap, member);
|
| + computeMemberData(compiler, member, actualMap, sourceSpanMap);
|
| }
|
| });
|
| });
|
| elementEnvironment.forEachLibraryMember(mainLibrary, (MemberEntity member) {
|
| - checkMember(compiler, expectedMap, member);
|
| + computeMemberData(compiler, member, actualMap, sourceSpanMap);
|
| });
|
| - expectedMap.forEach((Id id, String expected) {
|
| + return new IdData(compiler, elementEnvironment, mainUri, expectedMap,
|
| + actualMap, sourceSpanMap);
|
| +}
|
| +
|
| +/// Data collected by [computeData].
|
| +class IdData {
|
| + final Compiler compiler;
|
| + final ElementEnvironment elementEnvironment;
|
| + final Uri mainUri;
|
| + final Map<Id, String> expectedMap;
|
| + final Map<Id, String> actualMap;
|
| + final Map<Id, SourceSpan> sourceSpanMap;
|
| +
|
| + IdData(this.compiler, this.elementEnvironment, this.mainUri, this.expectedMap,
|
| + this.actualMap, this.sourceSpanMap);
|
| +}
|
| +
|
| +/// Compiles the [annotatedCode] with the provided [options] and calls
|
| +/// [computeMemberData] for each member. The result is checked against the
|
| +/// expected data derived from [annotatedCode].
|
| +Future checkCode(
|
| + String annotatedCode,
|
| + ComputeMemberDataFunction computeMemberData,
|
| + CompileFunction compileFunction,
|
| + {List<String> options: const <String>[]}) async {
|
| + IdData data = await computeData(
|
| + annotatedCode, computeMemberData, compileFunction,
|
| + options: options);
|
| +
|
| + data.actualMap.forEach((Id id, String actual) {
|
| + String expected = data.expectedMap.remove(id);
|
| + if (actual != expected) {
|
| + reportHere(data.compiler.reporter, data.sourceSpanMap[id],
|
| + 'expected:${expected},actual:${actual}');
|
| + }
|
| + Expect.equals(expected, actual);
|
| + });
|
| +
|
| + data.expectedMap.forEach((Id id, String expected) {
|
| reportHere(
|
| - compiler.reporter,
|
| - computeSpannable(elementEnvironment, mainUri, id),
|
| + data.compiler.reporter,
|
| + computeSpannable(data.elementEnvironment, data.mainUri, id),
|
| 'expected:${expected},actual:null');
|
| });
|
| - Expect.isTrue(expectedMap.isEmpty, "Ids not found: $expectedMap.");
|
| + Expect.isTrue(
|
| + data.expectedMap.isEmpty, "Ids not found: ${data.expectedMap}.");
|
| }
|
|
|
| +/// Compute a [Spannable] from an [id] in the library [mainUri].
|
| Spannable computeSpannable(
|
| ElementEnvironment elementEnvironment, Uri mainUri, Id id) {
|
| if (id is NodeId) {
|
| @@ -71,6 +149,7 @@ Spannable computeSpannable(
|
| throw new UnsupportedError('Unsupported id $id.');
|
| }
|
|
|
| +/// Compute the expectancy map from [code].
|
| Map<Id, String> computeExpectedMap(AnnotatedCode code) {
|
| Map<Id, String> map = <Id, String>{};
|
| for (Annotation annotation in code.annotations) {
|
| @@ -90,87 +169,156 @@ Map<Id, String> computeExpectedMap(AnnotatedCode code) {
|
| return map;
|
| }
|
|
|
| -abstract class AbstractResolvedAstChecker extends Visitor
|
| - with AstEnumeratorMixin {
|
| +/// Mixin used for computing [Id] data.
|
| +abstract class ComputerMixin {
|
| + Map<Id, String> get actualMap;
|
| + Map<Id, SourceSpan> get sourceSpanMap;
|
| +
|
| + void registerValue(SourceSpan sourceSpan, Id id, String value) {
|
| + if (id != null && value != null) {
|
| + sourceSpanMap[id] = sourceSpan;
|
| + actualMap[id] = value;
|
| + }
|
| + }
|
| +}
|
| +
|
| +/// Abstract AST visitor for computing [Id] data.
|
| +abstract class AbstractResolvedAstComputer extends ast.Visitor
|
| + with AstEnumeratorMixin, ComputerMixin {
|
| final DiagnosticReporter reporter;
|
| - final Map<Id, String> expectedMap;
|
| + final Map<Id, String> actualMap;
|
| + final Map<Id, SourceSpan> sourceSpanMap;
|
| final ResolvedAst resolvedAst;
|
|
|
| - AbstractResolvedAstChecker(this.reporter, this.expectedMap, this.resolvedAst);
|
| + AbstractResolvedAstComputer(
|
| + this.reporter, this.actualMap, this.sourceSpanMap, this.resolvedAst);
|
|
|
| TreeElements get elements => resolvedAst.elements;
|
|
|
| - void check() {
|
| - resolvedAst.node.accept(this);
|
| - }
|
| -
|
| - visitNode(Node node) {
|
| - node.visitChildren(this);
|
| - }
|
| -
|
| - void checkElement(AstElement element) {
|
| + void computeForElement(AstElement element) {
|
| ElementId id = computeElementId(element);
|
| - String expected = annotationForId(id);
|
| + if (id == null) return;
|
| String value = computeElementValue(element);
|
| - checkValue(element, expected, value);
|
| + registerValue(element.sourcePosition, id, value);
|
| + }
|
| +
|
| + void computeForNode(ast.Node node, AstElement element) {
|
| + NodeId id = computeNodeId(node, element);
|
| + if (id == null) return;
|
| + String value = computeNodeValue(node, element);
|
| + SourceSpan sourceSpan = new SourceSpan(resolvedAst.sourceUri,
|
| + node.getBeginToken().charOffset, node.getEndToken().charEnd);
|
| + registerValue(sourceSpan, id, value);
|
| }
|
|
|
| String computeElementValue(AstElement element);
|
|
|
| - String annotationForId(Id id) {
|
| - if (id == null) return null;
|
| - return expectedMap.remove(id);
|
| - }
|
| + String computeNodeValue(ast.Node node, AstElement element);
|
|
|
| - void checkValue(Spannable spannable, String expected, String value) {
|
| - if (value != null || expected != null) {
|
| - if (value != expected) {
|
| - reportHere(reporter, spannable, 'expected:${expected},actual:${value}');
|
| - }
|
| - Expect.equals(expected, value);
|
| - }
|
| + void run() {
|
| + resolvedAst.node.accept(this);
|
| }
|
|
|
| - void checkNode(Node node, AstElement element) {
|
| - NodeId id = computeNodeId(node, element);
|
| - String expected = annotationForId(id);
|
| - String value = computeNodeValue(node, element);
|
| - checkValue(node, expected, value);
|
| + visitNode(ast.Node node) {
|
| + node.visitChildren(this);
|
| }
|
|
|
| - String computeNodeValue(Node node, [AstElement element]);
|
| -
|
| - visitVariableDefinitions(VariableDefinitions node) {
|
| - for (Node child in node.definitions) {
|
| + visitVariableDefinitions(ast.VariableDefinitions node) {
|
| + for (ast.Node child in node.definitions) {
|
| AstElement element = elements[child];
|
| if (element == null) {
|
| reportHere(reporter, child, 'No element for variable.');
|
| } else if (!element.isLocal) {
|
| - checkElement(element);
|
| + computeForElement(element);
|
| } else {
|
| - checkNode(child, element);
|
| + computeForNode(child, element);
|
| }
|
| }
|
| visitNode(node);
|
| }
|
|
|
| - visitFunctionExpression(FunctionExpression node) {
|
| + visitFunctionExpression(ast.FunctionExpression node) {
|
| AstElement element = elements.getFunctionDefinition(node);
|
| if (!element.isLocal) {
|
| - checkElement(element);
|
| + computeForElement(element);
|
| } else {
|
| - checkNode(node, element);
|
| + computeForNode(node, element);
|
| }
|
| visitNode(node);
|
| }
|
|
|
| - visitSend(Send node) {
|
| - checkNode(node, null);
|
| + visitSend(ast.Send node) {
|
| + computeForNode(node, null);
|
| visitNode(node);
|
| }
|
|
|
| - visitSendSet(SendSet node) {
|
| - checkNode(node, null);
|
| + visitSendSet(ast.SendSet node) {
|
| + computeForNode(node, null);
|
| visitNode(node);
|
| }
|
| }
|
| +
|
| +/// Abstract IR visitor for computing [Id] data.
|
| +abstract class AbstractIrComputer extends ir.Visitor
|
| + with IrEnumeratorMixin, ComputerMixin {
|
| + final Map<Id, String> actualMap;
|
| + final Map<Id, SourceSpan> sourceSpanMap;
|
| +
|
| + AbstractIrComputer(this.actualMap, this.sourceSpanMap);
|
| +
|
| + void computeForMember(ir.Member member) {
|
| + ElementId id = computeElementId(member);
|
| + if (id == null) return;
|
| + String value = computeMemberValue(member);
|
| + registerValue(computeSpannable(member), id, value);
|
| + }
|
| +
|
| + void computeForNode(ir.TreeNode node) {
|
| + NodeId id = computeNodeId(node);
|
| + if (id == null) return;
|
| + String value = computeNodeValue(node);
|
| + registerValue(computeSpannable(node), id, value);
|
| + }
|
| +
|
| + Spannable computeSpannable(ir.TreeNode node) {
|
| + return new SourceSpan(
|
| + Uri.parse(node.location.file), node.fileOffset, node.fileOffset + 1);
|
| + }
|
| +
|
| + String computeMemberValue(ir.Member member);
|
| +
|
| + String computeNodeValue(ir.TreeNode node);
|
| +
|
| + void run(ir.Node root) {
|
| + root.accept(this);
|
| + }
|
| +
|
| + defaultNode(ir.Node node) {
|
| + node.visitChildren(this);
|
| + }
|
| +
|
| + defaultMember(ir.Member node) {
|
| + computeForMember(node);
|
| + super.defaultMember(node);
|
| + }
|
| +
|
| + visitMethodInvocation(ir.MethodInvocation node) {
|
| + computeForNode(node);
|
| + super.visitMethodInvocation(node);
|
| + }
|
| +
|
| + visitPropertyGet(ir.PropertyGet node) {
|
| + computeForNode(node);
|
| + super.visitPropertyGet(node);
|
| + }
|
| +
|
| + visitVariableDeclaration(ir.VariableDeclaration node) {
|
| + computeForNode(node);
|
| + super.visitVariableDeclaration(node);
|
| + }
|
| +
|
| + visitFunctionDeclaration(ir.FunctionDeclaration node) {
|
| + computeForNode(node);
|
| + super.visitFunctionDeclaration(node);
|
| + }
|
| +}
|
|
|