Index: tests/compiler/dart2js/jumps/jump_test.dart |
diff --git a/tests/compiler/dart2js/jumps/jump_test.dart b/tests/compiler/dart2js/jumps/jump_test.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..12b7f09f1127c6a8227ee3b45bd9eb6d8f1ff912 |
--- /dev/null |
+++ b/tests/compiler/dart2js/jumps/jump_test.dart |
@@ -0,0 +1,202 @@ |
+// 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/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/elements/jumps.dart'; |
+import 'package:compiler/src/js_model/locals.dart'; |
+import 'package:compiler/src/kernel/element_map.dart'; |
+import 'package:compiler/src/kernel/kernel_backend_strategy.dart'; |
+import 'package:compiler/src/tree/nodes.dart' as ast; |
+import '../equivalence/id_equivalence.dart'; |
+import '../equivalence/id_equivalence_helper.dart'; |
+import 'package:kernel/ast.dart' as ir; |
+ |
+main() { |
+ asyncTest(() async { |
+ Directory dataDir = new Directory.fromUri(Platform.script.resolve('data')); |
+ await for (FileSystemEntity entity in dataDir.list()) { |
+ print('Checking ${entity.uri}'); |
+ String annotatedCode = await new File.fromUri(entity.uri).readAsString(); |
+ await checkCode(annotatedCode, computeJumpsData, compileFromSource); |
+ await checkCode(annotatedCode, computeKernelJumpsData, compileFromDill); |
+ } |
+ }); |
+} |
+ |
+/// Compute closure data mapping for [_member] as a [MemberElement]. |
+/// |
+/// Fills [actualMap] with the data and [sourceSpanMap] with the source spans |
+/// for the data origin. |
+void computeJumpsData( |
+ Compiler compiler, MemberEntity _member, Map<Id, ActualData> actualMap, |
+ {bool verbose: false}) { |
+ MemberElement member = _member; |
+ new JumpsAstComputer(compiler.reporter, actualMap, member.resolvedAst).run(); |
+} |
+ |
+/// Compute closure data mapping for [member] as a kernel based element. |
+/// |
+/// Fills [actualMap] with the data and [sourceSpanMap] with the source spans |
+/// for the data origin. |
+void computeKernelJumpsData( |
+ Compiler compiler, MemberEntity member, Map<Id, ActualData> actualMap, |
+ {bool verbose: false}) { |
+ KernelBackendStrategy backendStrategy = compiler.backendStrategy; |
+ KernelToElementMapForBuilding elementMap = backendStrategy.elementMap; |
+ GlobalLocalsMap localsMap = backendStrategy.globalLocalsMapForTesting; |
+ MemberDefinition definition = elementMap.getMemberDefinition(member); |
+ new JumpsIrChecker( |
+ actualMap, elementMap, member, localsMap.getLocalsMap(member)) |
+ .run(definition.node); |
+} |
+ |
+class TargetData { |
+ final int index; |
+ final NodeId id; |
+ final SourceSpan sourceSpan; |
+ final JumpTarget target; |
+ |
+ TargetData(this.index, this.id, this.sourceSpan, this.target); |
+} |
+ |
+class GotoData { |
+ final NodeId id; |
+ final SourceSpan sourceSpan; |
+ final JumpTarget target; |
+ |
+ GotoData(this.id, this.sourceSpan, this.target); |
+} |
+ |
+abstract class JumpsMixin { |
+ int index = 0; |
+ Map<JumpTarget, TargetData> targets = <JumpTarget, TargetData>{}; |
+ List<GotoData> gotos = <GotoData>[]; |
+ |
+ void registerValue(SourceSpan sourceSpan, Id id, String value, Object object); |
+ |
+ void processData() { |
+ targets.forEach((JumpTarget target, TargetData data) { |
+ StringBuffer sb = new StringBuffer(); |
+ sb.write(data.index); |
+ sb.write('@'); |
+ bool needsComma = false; |
+ if (target.isBreakTarget) { |
+ sb.write('break'); |
+ needsComma = true; |
+ } |
+ if (target.isContinueTarget) { |
+ if (needsComma) { |
+ sb.write(','); |
+ } |
+ sb.write('continue'); |
+ needsComma = true; |
+ } |
+ String value = sb.toString(); |
+ registerValue(data.sourceSpan, data.id, value, target); |
+ }); |
+ gotos.forEach((GotoData data) { |
+ StringBuffer sb = new StringBuffer(); |
+ sb.write('target='); |
+ TargetData targetData = targets[data.target]; |
+ sb.write(targetData.index); |
+ String value = sb.toString(); |
+ registerValue(data.sourceSpan, data.id, value, data); |
+ }); |
+ } |
+} |
+ |
+/// Ast visitor for computing jump data. |
+class JumpsAstComputer extends AstDataExtractor with JumpsMixin { |
+ JumpsAstComputer(DiagnosticReporter reporter, Map<Id, ActualData> actualMap, |
+ ResolvedAst resolvedAst) |
+ : super(reporter, actualMap, resolvedAst); |
+ |
+ void run() { |
+ super.run(); |
+ processData(); |
+ } |
+ |
+ @override |
+ String computeNodeValue(ast.Node node, [AstElement element]) { |
+ // Node values are computed post-visit in [processData]. |
+ return null; |
+ } |
+ |
+ @override |
+ String computeElementValue(AstElement element) { |
+ return null; |
+ } |
+ |
+ @override |
+ visitLoop(ast.Loop node) { |
+ JumpTarget target = elements.getTargetDefinition(node); |
+ if (target != null) { |
+ NodeId id = computeLoopNodeId(node); |
+ SourceSpan sourceSpan = computeSourceSpan(node); |
+ targets[target] = new TargetData(index++, id, sourceSpan, target); |
+ } |
+ super.visitLoop(node); |
+ } |
+ |
+ @override |
+ visitGotoStatement(ast.GotoStatement node) { |
+ JumpTarget target = elements.getTargetOf(node); |
+ NodeId id = computeGotoNodeId(node); |
+ SourceSpan sourceSpan = computeSourceSpan(node); |
+ gotos.add(new GotoData(id, sourceSpan, target)); |
+ super.visitGotoStatement(node); |
+ } |
+} |
+ |
+/// Kernel IR visitor for computing jump data. |
+class JumpsIrChecker extends IrDataExtractor with JumpsMixin { |
+ final KernelToLocalsMap _localsMap; |
+ |
+ JumpsIrChecker(Map<Id, ActualData> actualMap, KernelToElementMap elementMap, |
+ MemberEntity member, this._localsMap) |
+ : super(actualMap); |
+ |
+ void run(ir.Node root) { |
+ super.run(root); |
+ processData(); |
+ } |
+ |
+ @override |
+ String computeNodeValue(ir.Node node) { |
+ // Node values are computed post-visit in [processData]. |
+ return null; |
+ } |
+ |
+ @override |
+ String computeMemberValue(ir.Member member) { |
+ return null; |
+ } |
+ |
+ visitForStatement(ir.ForStatement node) { |
+ JumpTarget target = _localsMap.getJumpTargetForFor(node); |
+ if (target != null) { |
+ NodeId id = computeLoopNodeId(node); |
+ SourceSpan sourceSpan = computeSourceSpan(node); |
+ targets[target] = new TargetData(index++, id, sourceSpan, target); |
+ } |
+ super.visitForStatement(node); |
+ } |
+ |
+ // TODO(johnniwinther): Support testing of do, for-in and while loops. |
+ |
+ visitBreakStatement(ir.BreakStatement node) { |
+ JumpTarget target = _localsMap.getJumpTargetForBreak(node); |
+ assert(target != null, 'No target for $node.'); |
+ NodeId id = computeGotoNodeId(node); |
+ SourceSpan sourceSpan = computeSourceSpan(node); |
+ gotos.add(new GotoData(id, sourceSpan, target)); |
+ super.visitBreakStatement(node); |
+ } |
+} |