blob: 7a11ea1f22291f9392878bae4065ebe99033434b [file] [log] [blame]
// Copyright (c) 2016, 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.
library sourcemap.js_tracer;
import 'package:compiler/src/io/code_output.dart';
import 'package:compiler/src/io/source_information.dart';
import 'package:compiler/src/io/position_information.dart';
import 'package:compiler/src/js/js.dart' as js;
import 'package:compiler/src/js/js_source_mapping.dart';
import 'sourcemap_helper.dart';
import 'trace_graph.dart';
/// Create a [TraceGraph] for [info] registering usage in [coverage].
TraceGraph createTraceGraph(SourceMapInfo info, Coverage coverage) {
TraceGraph graph = TraceGraph();
TraceListener listener = StepTraceListener(graph);
final outBuffer = NoopCodeOutput();
SourceInformationProcessor sourceInformationProcessor =
HelperOnlinePositionSourceInformationStrategy([
CoverageListener(coverage, const SourceInformationReader()),
listener,
]).createProcessor(
SourceMapperProviderImpl(outBuffer),
const SourceInformationReader(),
);
js.Dart2JSJavaScriptPrintingContext context =
js.Dart2JSJavaScriptPrintingContext(
null,
outBuffer,
sourceInformationProcessor,
const js.JavaScriptAnnotationMonitor(),
);
js.Printer printer = js.Printer(
const js.JavaScriptPrintingOptions(),
context,
);
printer.visit(info.node);
return graph;
}
class StepTraceListener extends TraceListener
with NodeToSourceInformationMixin {
Map<js.Node, TraceStep> steppableMap = <js.Node, TraceStep>{};
final TraceGraph graph;
StepTraceListener(this.graph);
@override
SourceInformationReader get reader => const SourceInformationReader();
@override
void onStep(js.Node node, Offset offset, StepKind kind) {
SourceInformation? sourceInformation = computeSourceInformation(node);
SourcePositionKind sourcePositionKind = SourcePositionKind.start;
List text = [node];
switch (kind) {
case StepKind.funEntry:
text = ['<entry>'];
break;
case StepKind.funExit:
sourcePositionKind = SourcePositionKind.inner;
text = ['<exit>'];
break;
case StepKind.call:
CallPosition callPosition = CallPosition.getSemanticPositionForCall(
node as js.Call,
);
sourcePositionKind = callPosition.sourcePositionKind;
break;
case StepKind.access:
case StepKind.new_:
case StepKind.return_:
case StepKind.break_:
case StepKind.continue_:
case StepKind.throw_:
case StepKind.expressionStatement:
break;
case StepKind.ifCondition:
final ifNode = node as js.If;
text = ['if(', ifNode.condition, ') ...'];
break;
case StepKind.forInitializer:
final forNode = node as js.For;
text = ['for(', forNode.init, '; ...) ...'];
break;
case StepKind.forCondition:
final forNode = node as js.For;
text = ['for(...;', forNode.condition, '; ...) ...'];
break;
case StepKind.forUpdate:
final forNode = node as js.For;
text = ['for(...; ...', forNode.update, ') ...'];
break;
case StepKind.whileCondition:
final whileNode = node as js.While;
text = ['while(', whileNode.condition, ') ...'];
break;
case StepKind.doCondition:
final doNode = node as js.Do;
text = ['do {... } (', doNode.condition, ')'];
break;
case StepKind.switchExpression:
final switchNode = node as js.Switch;
text = ['switch(', switchNode.key, ') ...'];
break;
case StepKind.noInfo:
break;
}
createTraceStep(
kind,
node,
offset: offset,
sourceLocation: getSourceLocation(sourceInformation!, sourcePositionKind),
text: text,
);
}
void createTraceStep(
StepKind kind,
js.Node node, {
required Offset offset,
List? text,
String? note,
SourceLocation? sourceLocation,
}) {
int id = steppableMap.length;
if (text == null) {
text = [node];
}
TraceStep step = TraceStep(kind, id, node, offset, text, sourceLocation);
graph.addStep(step);
steppableMap[node] = step;
}
@override
void pushBranch(BranchKind kind, [int? value]) {
var branch;
switch (kind) {
case BranchKind.condition:
branch = value == 1 ? 't' : 'f';
break;
case BranchKind.loop:
branch = 'l';
break;
case BranchKind.catch_:
branch = 'c';
break;
case BranchKind.finally_:
branch = 'F';
break;
case BranchKind.case_:
branch = '$value';
break;
}
graph.pushBranch(branch);
}
@override
void popBranch() {
graph.popBranch();
}
}