blob: 5cb08f03ff160725060920805cc38d25535d316e [file] [log] [blame]
// Copyright (c) 2017, 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:async_helper/async_helper.dart';
import 'package:compiler/src/commandline_options.dart';
import 'package:compiler/src/common.dart';
import 'package:compiler/src/compiler.dart';
import 'package:compiler/src/diagnostics/diagnostic_listener.dart';
import 'package:compiler/src/elements/elements.dart';
import 'package:compiler/src/elements/entities.dart';
import 'package:compiler/src/kernel/element_map.dart';
import 'package:compiler/src/kernel/kernel_backend_strategy.dart';
import 'package:compiler/src/resolution/access_semantics.dart';
import 'package:compiler/src/resolution/send_structure.dart';
import 'package:compiler/src/tree/nodes.dart' as ast;
import 'package:expect/expect.dart';
import 'package:kernel/ast.dart' as ir;
import '../equivalence/id_equivalence.dart';
import '../equivalence/id_equivalence_helper.dart';
const List<String> dataDirectories = const <String>[
'../closure/data',
'../inference/data',
'../jumps/data',
];
main(List<String> args) {
asyncTest(() async {
for (String path in dataDirectories) {
Directory dataDir = new Directory.fromUri(Platform.script.resolve(path));
await for (FileSystemEntity entity in dataDir.list()) {
if (args.isNotEmpty && !args.contains(entity.uri.pathSegments.last)) {
continue;
}
print('Checking ${entity.uri}');
String annotatedCode =
await new File.fromUri(entity.uri).readAsString();
IdData data1 = await computeData(
annotatedCode, computeAstMemberData, compileFromSource,
options: [Flags.disableTypeInference, stopAfterTypeInference]);
IdData data2 = await computeData(
annotatedCode, computeIrMemberData, compileFromDill,
options: [Flags.disableTypeInference, stopAfterTypeInference]);
data1.actualMap.forEach((Id id, ActualData actualData1) {
IdValue value1 = actualData1.value;
IdValue value2 = data2.actualMap[id]?.value;
if (value1 != value2) {
reportHere(data1.compiler.reporter, actualData1.sourceSpan,
'$id: from source:${value1},from dill:${value2}');
print('--annotations diff----------------------------------------');
print(data1.computeDiffCodeFor(data2));
print('----------------------------------------------------------');
}
Expect.equals(value1, value2, 'Value mismatch for $id');
});
data2.actualMap.forEach((Id id, ActualData actualData2) {
IdValue value2 = actualData2.value;
IdValue value1 = data1.actualMap[id]?.value;
if (value1 != value2) {
reportHere(data2.compiler.reporter, actualData2.sourceSpan,
'$id: from source:${value1},from dill:${value2}');
print('--annotations diff----------------------------------------');
print(data1.computeDiffCodeFor(data2));
print('----------------------------------------------------------');
}
Expect.equals(value1, value2, 'Value mismatch for $id');
});
}
}
});
}
/// Compute a descriptive mapping of the [Id]s in [_member] as a
/// [MemberElement].
///
/// Fills [actualMap] with the data and [sourceSpanMap] with the source spans
/// for the data origin.
void computeAstMemberData(
Compiler compiler, MemberEntity _member, Map<Id, ActualData> actualMap,
{bool verbose: false}) {
MemberElement member = _member;
ResolvedAst resolvedAst = member.resolvedAst;
new ResolvedAstComputer(compiler.reporter, actualMap, resolvedAst).run();
}
/// Mixin used for computing a descriptive mapping of the [Id]s in a member.
class ComputerMixin {
String computeMemberName(String className, String memberName) {
if (className != null) {
return 'member:$className.$memberName';
}
return 'member:$memberName';
}
String computeLocalName(String localName) {
return 'local:$localName';
}
String computeGetName(String propertyName) {
return 'get:$propertyName';
}
String computeInvokeName(String propertyName) {
return 'invoke:$propertyName';
}
String computeSetName(String propertyName) {
return 'set:$propertyName';
}
String get loopName => 'loop';
String get gotoName => 'goto';
String get switchName => 'switch';
String get switchCaseName => 'case';
String get labelName => 'label';
}
/// AST visitor for computing a descriptive mapping of the [Id]s in a member.
class ResolvedAstComputer extends AstDataExtractor with ComputerMixin {
ResolvedAstComputer(DiagnosticReporter reporter,
Map<Id, ActualData> actualMap, ResolvedAst resolvedAst)
: super(reporter, actualMap, resolvedAst);
@override
String computeNodeValue(Id id, ast.Node node, AstElement element) {
if (element != null && element.isLocal) {
return computeLocalName(element.name);
}
if (node is ast.Loop) {
return loopName;
} else if (node is ast.GotoStatement) {
return gotoName;
} else if (node is ast.SwitchStatement) {
return switchName;
} else if (node is ast.SwitchCase) {
return switchCaseName;
} else if (node is ast.LabeledStatement) {
return labelName;
}
dynamic sendStructure;
if (node is ast.Send) {
sendStructure = elements.getSendStructure(node);
if (sendStructure == null) return null;
String getDynamicName() {
switch (sendStructure.semantics.kind) {
case AccessKind.PARAMETER:
case AccessKind.FINAL_PARAMETER:
case AccessKind.LOCAL_VARIABLE:
case AccessKind.FINAL_LOCAL_VARIABLE:
case AccessKind.LOCAL_FUNCTION:
return sendStructure.semantics.element.name;
case AccessKind.THIS_PROPERTY:
case AccessKind.DYNAMIC_PROPERTY:
DynamicAccess access = sendStructure.semantics;
return access.name.text;
default:
return null;
}
}
switch (sendStructure.kind) {
case SendStructureKind.GET:
String dynamicName = getDynamicName();
if (dynamicName != null) return computeGetName(dynamicName);
break;
case SendStructureKind.BINARY:
return computeInvokeName(sendStructure.operator.selectorName);
case SendStructureKind.EQUALS:
return computeInvokeName('==');
case SendStructureKind.NOT_EQUALS:
return computeInvokeName('==');
case SendStructureKind.INVOKE:
String dynamicName = getDynamicName();
if (dynamicName != null) return computeInvokeName(dynamicName);
break;
case SendStructureKind.SET:
String dynamicName = getDynamicName();
if (dynamicName != null) return computeSetName(dynamicName);
break;
case SendStructureKind.PREFIX:
case SendStructureKind.POSTFIX:
case SendStructureKind.COMPOUND:
String dynamicName = getDynamicName();
if (dynamicName != null) {
if (id.kind == IdKind.update) {
return computeSetName(dynamicName);
} else if (id.kind == IdKind.invoke) {
return computeInvokeName(
sendStructure.operator.binaryOperator.name);
} else {
return computeGetName(dynamicName);
}
}
break;
default:
}
}
if (sendStructure != null) {
return '<unknown:$node (${node.runtimeType}) $sendStructure>';
}
return '<unknown:$node (${node.runtimeType})>';
}
@override
String computeElementValue(Id id, AstElement element) {
return computeMemberName(element.enclosingClass?.name, element.name);
}
}
/// Compute a descriptive mapping of the [Id]s in [member] as a kernel based
/// member.
///
/// Fills [actualMap] with the data and [sourceSpanMap] with the source spans
/// for the data origin.
void computeIrMemberData(
Compiler compiler, MemberEntity member, Map<Id, ActualData> actualMap,
{bool verbose: false}) {
KernelBackendStrategy backendStrategy = compiler.backendStrategy;
KernelToElementMapForBuilding elementMap = backendStrategy.elementMap;
MemberDefinition definition = elementMap.getMemberDefinition(member);
assert(
definition.kind == MemberKind.regular ||
definition.kind == MemberKind.constructor,
failedAt(member, "Unexpected member definition $definition"));
new IrComputer(compiler.reporter, actualMap).run(definition.node);
}
/// IR visitor for computing a descriptive mapping of the [Id]s in a member.
class IrComputer extends IrDataExtractor with ComputerMixin {
IrComputer(DiagnosticReporter reporter, Map<Id, ActualData> actualMap)
: super(reporter, actualMap);
@override
String computeNodeValue(Id id, ir.TreeNode node) {
if (node is ir.VariableDeclaration) {
return computeLocalName(node.name);
} else if (node is ir.FunctionDeclaration) {
return computeLocalName(node.variable.name);
} else if (node is ir.FunctionExpression) {
return computeLocalName('');
} else if (node is ir.MethodInvocation) {
return computeInvokeName(node.name.name);
} else if (node is ir.PropertyGet) {
return computeGetName(node.name.name);
} else if (node is ir.PropertySet) {
return computeSetName(node.name.name);
} else if (node is ir.VariableGet) {
return computeGetName(node.variable.name);
} else if (node is ir.VariableSet) {
return computeSetName(node.variable.name);
} else if (node is ir.DoStatement) {
return loopName;
} else if (node is ir.ForStatement) {
return loopName;
} else if (node is ir.ForInStatement) {
return loopName;
} else if (node is ir.WhileStatement) {
return loopName;
} else if (node is ir.BreakStatement) {
return gotoName;
} else if (node is ir.ContinueSwitchStatement) {
return gotoName;
} else if (node is ir.SwitchStatement) {
return switchName;
} else if (node is ir.SwitchCase) {
return switchCaseName;
} else if (node is ir.LabeledStatement) {
return labelName;
}
return '<unknown:$node (${node.runtimeType})>';
}
@override
String computeMemberValue(Id id, ir.Member member) {
return computeMemberName(member.enclosingClass?.name, member.name.name);
}
}