| // 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. |
| |
| // @dart = 2.7 |
| |
| 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/elements/entities.dart'; |
| import 'package:compiler/src/elements/jumps.dart'; |
| import 'package:compiler/src/js_model/element_map.dart'; |
| import 'package:compiler/src/js_model/js_world.dart'; |
| import 'package:compiler/src/js_model/locals.dart'; |
| import '../equivalence/id_equivalence.dart'; |
| import '../equivalence/id_equivalence_helper.dart'; |
| import 'package:kernel/ast.dart' as ir; |
| |
| main(List<String> args) { |
| asyncTest(() async { |
| Directory dataDir = new Directory.fromUri(Platform.script.resolve('data')); |
| await checkTests(dataDir, const JumpDataComputer(), |
| options: [stopAfterTypeInference], args: args); |
| }); |
| } |
| |
| class JumpDataComputer extends DataComputer<String> { |
| const JumpDataComputer(); |
| |
| /// 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. |
| @override |
| void computeMemberData(Compiler compiler, MemberEntity member, |
| Map<Id, ActualData<String>> actualMap, |
| {bool verbose: false}) { |
| JsClosedWorld closedWorld = compiler.backendClosedWorldForTesting; |
| JsToElementMap elementMap = closedWorld.elementMap; |
| GlobalLocalsMap localsMap = |
| compiler.globalInference.resultsForTesting.globalLocalsMap; |
| MemberDefinition definition = elementMap.getMemberDefinition(member); |
| new JumpsIrChecker( |
| compiler.reporter, actualMap, localsMap.getLocalsMap(member)) |
| .run(definition.node); |
| } |
| |
| @override |
| DataInterpreter<String> get dataValidator => const StringDataInterpreter(); |
| } |
| |
| class TargetData { |
| final int index; |
| final NodeId id; |
| final SourceSpan sourceSpan; |
| final JumpTarget target; |
| |
| TargetData(this.index, this.id, this.sourceSpan, this.target); |
| |
| @override |
| String toString() => 'TargetData(index=$index,id=$id,' |
| 'sourceSpan=$sourceSpan,target=$target)'; |
| } |
| |
| class GotoData { |
| final NodeId id; |
| final SourceSpan sourceSpan; |
| final JumpTarget target; |
| |
| GotoData(this.id, this.sourceSpan, this.target); |
| |
| @override |
| String toString() => 'GotoData(id=$id,sourceSpan=$sourceSpan,target=$target)'; |
| } |
| |
| /// Kernel IR visitor for computing jump data. |
| class JumpsIrChecker extends IrDataExtractor<String> { |
| final KernelToLocalsMap _localsMap; |
| |
| int index = 0; |
| Map<JumpTarget, TargetData> targets = <JumpTarget, TargetData>{}; |
| List<GotoData> gotos = <GotoData>[]; |
| |
| JumpsIrChecker(DiagnosticReporter reporter, |
| Map<Id, ActualData<String>> actualMap, this._localsMap) |
| : super(reporter, actualMap); |
| |
| 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.uri, data.sourceSpan.begin, data.id, value, target); |
| }); |
| gotos.forEach((GotoData data) { |
| StringBuffer sb = new StringBuffer(); |
| sb.write('target='); |
| TargetData targetData = targets[data.target]; |
| assert(targetData != null, "No TargetData for ${data.target}"); |
| sb.write(targetData.index); |
| String value = sb.toString(); |
| registerValue( |
| data.sourceSpan.uri, data.sourceSpan.begin, data.id, value, data); |
| }); |
| } |
| |
| @override |
| void run(ir.Node root) { |
| super.run(root); |
| processData(); |
| } |
| |
| @override |
| String computeNodeValue(Id id, ir.Node node) { |
| // Node values are computed post-visit in [processData]. |
| return null; |
| } |
| |
| void addTargetData(ir.TreeNode node, NodeId id, JumpTarget target) { |
| if (target != null) { |
| SourceSpan sourceSpan = computeSourceSpan(node); |
| targets[target] = new TargetData(index++, id, sourceSpan, target); |
| } |
| } |
| |
| @override |
| visitForStatement(ir.ForStatement node) { |
| addTargetData( |
| node, createLoopId(node), _localsMap.getJumpTargetForFor(node)); |
| super.visitForStatement(node); |
| } |
| |
| @override |
| visitForInStatement(ir.ForInStatement node) { |
| addTargetData( |
| node, createLoopId(node), _localsMap.getJumpTargetForForIn(node)); |
| super.visitForInStatement(node); |
| } |
| |
| @override |
| visitWhileStatement(ir.WhileStatement node) { |
| addTargetData( |
| node, createLoopId(node), _localsMap.getJumpTargetForWhile(node)); |
| super.visitWhileStatement(node); |
| } |
| |
| @override |
| visitDoStatement(ir.DoStatement node) { |
| addTargetData( |
| node, createLoopId(node), _localsMap.getJumpTargetForDo(node)); |
| super.visitDoStatement(node); |
| } |
| |
| @override |
| visitBreakStatement(ir.BreakStatement node) { |
| JumpTarget target = _localsMap.getJumpTargetForBreak(node); |
| assert(target != null, 'No target for $node.'); |
| NodeId id = createGotoId(node); |
| SourceSpan sourceSpan = computeSourceSpan(node); |
| gotos.add(new GotoData(id, sourceSpan, target)); |
| super.visitBreakStatement(node); |
| } |
| |
| @override |
| visitLabeledStatement(ir.LabeledStatement node) { |
| JumpTarget target = _localsMap.getJumpTargetForLabel(node); |
| if (target != null) { |
| NodeId id = createLabeledStatementId(node); |
| SourceSpan sourceSpan = computeSourceSpan(node); |
| targets[target] = new TargetData(index++, id, sourceSpan, target); |
| } |
| super.visitLabeledStatement(node); |
| } |
| |
| @override |
| visitSwitchStatement(ir.SwitchStatement node) { |
| addTargetData( |
| node, createSwitchId(node), _localsMap.getJumpTargetForSwitch(node)); |
| super.visitSwitchStatement(node); |
| } |
| |
| @override |
| visitSwitchCase(ir.SwitchCase node) { |
| addTargetData(node, createSwitchCaseId(node), |
| _localsMap.getJumpTargetForSwitchCase(node)); |
| super.visitSwitchCase(node); |
| } |
| |
| @override |
| visitContinueSwitchStatement(ir.ContinueSwitchStatement node) { |
| JumpTarget target = _localsMap.getJumpTargetForContinueSwitch(node); |
| assert(target != null, 'No target for $node.'); |
| NodeId id = createGotoId(node); |
| SourceSpan sourceSpan = computeSourceSpan(node); |
| gotos.add(new GotoData(id, sourceSpan, target)); |
| super.visitContinueSwitchStatement(node); |
| } |
| } |