| Index: tests/compiler/dart2js/sourcemaps/output_structure.dart
|
| diff --git a/tests/compiler/dart2js/sourcemaps/output_structure.dart b/tests/compiler/dart2js/sourcemaps/output_structure.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..4962ba9012136b152a43d515d852c5faf9024a42
|
| --- /dev/null
|
| +++ b/tests/compiler/dart2js/sourcemaps/output_structure.dart
|
| @@ -0,0 +1,668 @@
|
| +// 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.output_structure;
|
| +
|
| +import 'html_parts.dart' show
|
| + CodeLine;
|
| +
|
| +// Constants used to identify the subsection of the JavaScript output. These
|
| +// are specifically for the unminified full_emitter output.
|
| +const String HEAD = ' var dart = [';
|
| +const String TAIL = ' }], ';
|
| +const String END = ' setupProgram(dart';
|
| +
|
| +final RegExp TOP_LEVEL_VALUE = new RegExp(r'^ (".+?"):');
|
| +final RegExp TOP_LEVEL_FUNCTION =
|
| + new RegExp(r'^ ([a-zA-Z0-9_$]+): \[?function');
|
| +final RegExp TOP_LEVEL_CLASS = new RegExp(r'^ ([a-zA-Z0-9_$]+): \[?\{');
|
| +
|
| +final RegExp STATICS = new RegExp(r'^ static:');
|
| +final RegExp MEMBER_VALUE = new RegExp(r'^ (".+?"):');
|
| +final RegExp MEMBER_FUNCTION =
|
| + new RegExp(r'^ ([a-zA-Z0-9_$]+): \[?function');
|
| +final RegExp MEMBER_OBJECT = new RegExp(r'^ ([a-zA-Z0-9_$]+): \[?\{');
|
| +
|
| +final RegExp STATIC_FUNCTION =
|
| + new RegExp(r'^ ([a-zA-Z0-9_$]+): \[?function');
|
| +
|
| +/// Subrange of the JavaScript output.
|
| +abstract class OutputEntity {
|
| + Interval get interval;
|
| + Interval get header;
|
| + Interval get footer;
|
| +
|
| + bool get canHaveChildren => false;
|
| +
|
| + List<OutputEntity> get children;
|
| +
|
| + CodeSource codeSource;
|
| +
|
| + Interval getChildInterval(Interval childIndex) {
|
| + return new Interval(
|
| + children[childIndex.from].interval.from,
|
| + children[childIndex.to - 1].interval.to);
|
| +
|
| + }
|
| +
|
| + OutputEntity getChild(int index) {
|
| + return children[index];
|
| + }
|
| +
|
| + accept(OutputVisitor visitor, arg);
|
| +
|
| + EntityKind get kind;
|
| +
|
| + Map toJson();
|
| +
|
| + OutputEntity getEntityForLine(int line);
|
| +}
|
| +
|
| +enum EntityKind {
|
| + STRUCTURE,
|
| + LIBRARY,
|
| + CLASS,
|
| + TOP_LEVEL_FUNCTION,
|
| + TOP_LEVEL_VALUE,
|
| + MEMBER_FUNCTION,
|
| + MEMBER_OBJECT,
|
| + MEMBER_VALUE,
|
| + STATICS,
|
| + STATIC_FUNCTION,
|
| +}
|
| +
|
| +abstract class OutputVisitor<R, A> {
|
| + R visitStructure(OutputStructure entity, A arg);
|
| + R visitLibrary(LibraryBlock entity, A arg);
|
| + R visitClass(LibraryClass entity, A arg);
|
| + R visitTopLevelFunction(TopLevelFunction entity, A arg);
|
| + R visitTopLevelValue(TopLevelValue entity, A arg);
|
| + R visitMemberObject(MemberObject entity, A arg);
|
| + R visitMemberFunction(MemberFunction entity, A arg);
|
| + R visitMemberValue(MemberValue entity, A arg);
|
| + R visitStatics(Statics entity, A arg);
|
| + R visitStaticFunction(StaticFunction entity, A arg);
|
| +}
|
| +
|
| +abstract class BaseOutputVisitor<R, A> extends OutputVisitor<R, A> {
|
| + R visitEntity(OutputEntity entity, A arg) => null;
|
| +
|
| + R visitStructure(OutputStructure entity, A arg) => visitEntity(entity, arg);
|
| + R visitLibrary(LibraryBlock entity, A arg) => visitEntity(entity, arg);
|
| + R visitClass(LibraryClass entity, A arg) => visitEntity(entity, arg);
|
| +
|
| + R visitMember(BasicEntity entity, A arg) => visitEntity(entity, arg);
|
| +
|
| + R visitTopLevelMember(BasicEntity entity, A arg) => visitMember(entity, arg);
|
| +
|
| + R visitTopLevelFunction(TopLevelFunction entity, A arg) {
|
| + return visitTopLevelMember(entity, arg);
|
| + }
|
| +
|
| + R visitTopLevelValue(TopLevelValue entity, A arg) {
|
| + return visitTopLevelMember(entity, arg);
|
| + }
|
| +
|
| + R visitClassMember(BasicEntity entity, A arg) => visitMember(entity, arg);
|
| +
|
| + R visitMemberObject(MemberObject entity, A arg) {
|
| + return visitClassMember(entity, arg);
|
| + }
|
| +
|
| + R visitMemberFunction(MemberFunction entity, A arg) {
|
| + return visitClassMember(entity, arg);
|
| + }
|
| +
|
| + R visitMemberValue(MemberValue entity, A arg) {
|
| + return visitClassMember(entity, arg);
|
| + }
|
| +
|
| + R visitStatics(Statics entity, A arg) {
|
| + return visitClassMember(entity, arg);
|
| + }
|
| +
|
| + R visitStaticFunction(StaticFunction entity, A arg) {
|
| + return visitClassMember(entity, arg);
|
| + }
|
| +}
|
| +
|
| +/// The whole JavaScript output.
|
| +class OutputStructure extends OutputEntity {
|
| + final List<CodeLine> lines;
|
| + final int headerEnd;
|
| + final int footerStart;
|
| + final List<LibraryBlock> children;
|
| +
|
| + OutputStructure(
|
| + this.lines,
|
| + this.headerEnd,
|
| + this.footerStart,
|
| + this.children);
|
| +
|
| + @override
|
| + EntityKind get kind => EntityKind.STRUCTURE;
|
| +
|
| + Interval get interval => new Interval(0, lines.length);
|
| +
|
| + Interval get header => new Interval(0, headerEnd);
|
| +
|
| + Interval get footer => new Interval(footerStart, lines.length);
|
| +
|
| + bool get canHaveChildren => true;
|
| +
|
| + OutputEntity getEntityForLine(int line) {
|
| + if (line < headerEnd || line >= footerStart) {
|
| + return this;
|
| + }
|
| + for (LibraryBlock library in children) {
|
| + if (library.interval.contains(line)) {
|
| + return library.getEntityForLine(line);
|
| + }
|
| + }
|
| + return null;
|
| + }
|
| +
|
| + /// Compute the structure of the JavaScript [lines].
|
| + static OutputStructure parse(List<CodeLine> lines) {
|
| +
|
| + int findHeaderStart(List<CodeLine> lines) {
|
| + int index = 0;
|
| + for (CodeLine line in lines) {
|
| + if (line.code.startsWith(HEAD)) {
|
| + return index;
|
| + }
|
| + index++;
|
| + }
|
| + return lines.length;
|
| + }
|
| +
|
| + int findHeaderEnd(int start, List<CodeLine> lines) {
|
| + int index = start;
|
| + for (CodeLine line in lines.skip(start)) {
|
| + if (line.code.startsWith(END)) {
|
| + return index;
|
| + }
|
| + index++;
|
| + }
|
| + return lines.length;
|
| + }
|
| +
|
| + String readHeader(CodeLine line) {
|
| + String code = line.code;
|
| + String ssaLineHeader;
|
| + if (code.startsWith(HEAD)) {
|
| + return code.substring(HEAD.length);
|
| + } else if (code.startsWith(TAIL)) {
|
| + return code.substring(TAIL.length);
|
| + }
|
| + return null;
|
| + }
|
| +
|
| + List<LibraryBlock> computeHeaderMap(
|
| + List<CodeLine> lines, int start, int end) {
|
| + List<LibraryBlock> libraryBlocks = <LibraryBlock>[];
|
| + LibraryBlock current;
|
| + for (int index = start; index < end; index++) {
|
| + String header = readHeader(lines[index]);
|
| + if (header != null) {
|
| + if (current != null) {
|
| + current.to = index;
|
| + }
|
| + libraryBlocks.add(current = new LibraryBlock(header, index));
|
| + }
|
| + }
|
| + if (current != null) {
|
| + current.to = end;
|
| + }
|
| + return libraryBlocks;
|
| + }
|
| +
|
| + int headerEnd = findHeaderStart(lines);
|
| + int footerStart = findHeaderEnd(headerEnd, lines);
|
| + List<LibraryBlock> libraryBlocks =
|
| + computeHeaderMap(lines, headerEnd, footerStart);
|
| + for (LibraryBlock block in libraryBlocks) {
|
| + block.preprocess(lines);
|
| + }
|
| +
|
| + return new OutputStructure(
|
| + lines, headerEnd, footerStart, libraryBlocks);
|
| + }
|
| +
|
| + accept(OutputVisitor visitor, arg) => visitor.visitStructure(this, arg);
|
| +
|
| + @override
|
| + Map toJson() {
|
| + return {
|
| + 'lines': lines.map((line) => line.toJson()).toList(),
|
| + 'headerEnd': headerEnd,
|
| + 'footerStart': footerStart,
|
| + 'children': children.map((child) => child.toJson()).toList(),
|
| + };
|
| + }
|
| +
|
| + static OutputStructure fromJson(Map json) {
|
| + List<CodeLine> lines = json['lines'].map(CodeLine.fromJson).toList();
|
| + int headerEnd = json['headerEnd'];
|
| + int footerStart = json['footerStart'];
|
| + List<LibraryBlock> children =
|
| + json['children'].map(AbstractEntity.fromJson).toList();
|
| + return new OutputStructure(lines, headerEnd, footerStart, children);
|
| + }
|
| +}
|
| +
|
| +abstract class AbstractEntity extends OutputEntity {
|
| + final String name;
|
| + final int from;
|
| + int to;
|
| +
|
| + AbstractEntity(this.name, this.from);
|
| +
|
| + Interval get interval => new Interval(from, to);
|
| +
|
| + @override
|
| + Map toJson() {
|
| + return {
|
| + 'kind': kind.index,
|
| + 'name': name,
|
| + 'from': from,
|
| + 'to': to,
|
| + 'children': children.map((child) => child.toJson()).toList(),
|
| + 'codeSource': codeSource != null ? codeSource.toJson() : null,
|
| + };
|
| + }
|
| +
|
| + static AbstractEntity fromJson(Map json) {
|
| + EntityKind kind = EntityKind.values[json['kind']];
|
| + String name = json['name'];
|
| + int from = json['from'];
|
| + int to = json['to'];
|
| + CodeSource codeSource = CodeSource.fromJson(json['codeSource']);
|
| +
|
| + switch (kind) {
|
| + case EntityKind.STRUCTURE:
|
| + throw new StateError('Unexpected entity kind $kind');
|
| + case EntityKind.LIBRARY:
|
| + LibraryBlock lib = new LibraryBlock(name, from)
|
| + ..to = to
|
| + ..codeSource = codeSource;
|
| + json['children'].forEach((child) => lib.children.add(fromJson(child)));
|
| + return lib;
|
| + case EntityKind.CLASS:
|
| + LibraryClass cls = new LibraryClass(name, from)
|
| + ..to = to
|
| + ..codeSource = codeSource;
|
| + json['children'].forEach((child) => cls.children.add(fromJson(child)));
|
| + return cls;
|
| + case EntityKind.TOP_LEVEL_FUNCTION:
|
| + return new TopLevelFunction(name, from)
|
| + ..to = to
|
| + ..codeSource = codeSource;
|
| + case EntityKind.TOP_LEVEL_VALUE:
|
| + return new TopLevelValue(name, from)
|
| + ..to = to
|
| + ..codeSource = codeSource;
|
| + case EntityKind.MEMBER_FUNCTION:
|
| + return new MemberFunction(name, from)
|
| + ..to = to
|
| + ..codeSource = codeSource;
|
| + case EntityKind.MEMBER_OBJECT:
|
| + return new MemberObject(name, from)
|
| + ..to = to
|
| + ..codeSource = codeSource;
|
| + case EntityKind.MEMBER_VALUE:
|
| + return new MemberValue(name, from)
|
| + ..to = to
|
| + ..codeSource = codeSource;
|
| + case EntityKind.STATICS:
|
| + Statics statics = new Statics(from)
|
| + ..to = to
|
| + ..codeSource = codeSource;
|
| + json['children'].forEach(
|
| + (child) => statics.children.add(fromJson(child)));
|
| + return statics;
|
| + case EntityKind.STATIC_FUNCTION:
|
| + return new StaticFunction(name, from)
|
| + ..to = to
|
| + ..codeSource = codeSource;
|
| + }
|
| + }
|
| +}
|
| +
|
| +/// A block defining the content of a Dart library.
|
| +class LibraryBlock extends AbstractEntity {
|
| + List<BasicEntity> children = <BasicEntity>[];
|
| + int get headerEnd => from + 2;
|
| + int get footerStart => to - 1;
|
| +
|
| + LibraryBlock(String name, int from) : super(name, from);
|
| +
|
| + @override
|
| + EntityKind get kind => EntityKind.LIBRARY;
|
| +
|
| + Interval get header => new Interval(from, headerEnd);
|
| +
|
| + Interval get footer => new Interval(footerStart, to);
|
| +
|
| + bool get canHaveChildren => true;
|
| +
|
| + void preprocess(List<CodeLine> lines) {
|
| + int index = headerEnd;
|
| + BasicEntity current;
|
| + while (index < footerStart) {
|
| + String line = lines[index].code;
|
| + BasicEntity next;
|
| + Match matchFunction = TOP_LEVEL_FUNCTION.firstMatch(line);
|
| + if (matchFunction != null) {
|
| + next = new TopLevelFunction(matchFunction.group(1), index);
|
| + } else {
|
| + Match matchClass = TOP_LEVEL_CLASS.firstMatch(line);
|
| + if (matchClass != null) {
|
| + next = new LibraryClass(matchClass.group(1), index);
|
| + } else {
|
| + Match matchValue = TOP_LEVEL_VALUE.firstMatch(line);
|
| + if (matchValue != null) {
|
| + next = new TopLevelValue(matchValue.group(1), index);
|
| + }
|
| + }
|
| + }
|
| + if (next != null) {
|
| + if (current != null) {
|
| + current.to = index;
|
| + }
|
| + children.add(current = next);
|
| + } else if (index == headerEnd) {
|
| + throw 'Failed to match first library block line:\n$line';
|
| + }
|
| +
|
| + index++;
|
| + }
|
| + if (current != null) {
|
| + current.to = footerStart;
|
| + }
|
| +
|
| + for (BasicEntity entity in children) {
|
| + entity.preprocess(lines);
|
| + }
|
| + }
|
| +
|
| + accept(OutputVisitor visitor, arg) => visitor.visitLibrary(this, arg);
|
| +
|
| + OutputEntity getEntityForLine(int line) {
|
| + if (line < headerEnd || line >= footerStart) {
|
| + return this;
|
| + }
|
| + for (BasicEntity child in children) {
|
| + if (child.interval.contains(line)) {
|
| + return child.getEntityForLine(line);
|
| + }
|
| + }
|
| + return null;
|
| + }
|
| +}
|
| +
|
| +/// A simple member of a library or class.
|
| +abstract class BasicEntity extends AbstractEntity {
|
| + BasicEntity(String name, int from) : super(name, from);
|
| +
|
| + Interval get header => new Interval(from, to);
|
| +
|
| + Interval get footer => new Interval(to, to);
|
| +
|
| + List<OutputEntity> get children => const <OutputEntity>[];
|
| +
|
| + void preprocess(List<CodeLine> lines) {}
|
| +
|
| + @override
|
| + OutputEntity getEntityForLine(int line) {
|
| + if (interval.contains(line)) {
|
| + return this;
|
| + }
|
| + return null;
|
| + }
|
| +}
|
| +
|
| +class TopLevelFunction extends BasicEntity {
|
| + TopLevelFunction(String name, int from) : super(name, from);
|
| +
|
| + @override
|
| + EntityKind get kind => EntityKind.TOP_LEVEL_FUNCTION;
|
| +
|
| + accept(OutputVisitor visitor, arg) {
|
| + return visitor.visitTopLevelFunction(this, arg);
|
| + }
|
| +}
|
| +
|
| +class TopLevelValue extends BasicEntity {
|
| + TopLevelValue(String name, int from) : super(name, from);
|
| +
|
| + @override
|
| + EntityKind get kind => EntityKind.TOP_LEVEL_VALUE;
|
| +
|
| + accept(OutputVisitor visitor, arg) {
|
| + return visitor.visitTopLevelValue(this, arg);
|
| + }
|
| +}
|
| +
|
| +/// A block defining a Dart class.
|
| +class LibraryClass extends BasicEntity {
|
| + List<BasicEntity> children = <BasicEntity>[];
|
| + int get headerEnd => from + 1;
|
| + int get footerStart => to - 1;
|
| +
|
| + LibraryClass(String name, int from) : super(name, from);
|
| +
|
| + @override
|
| + EntityKind get kind => EntityKind.CLASS;
|
| +
|
| + Interval get header => new Interval(from, headerEnd);
|
| +
|
| + Interval get footer => new Interval(footerStart, to);
|
| +
|
| + bool get canHaveChildren => true;
|
| +
|
| + void preprocess(List<CodeLine> lines) {
|
| + int index = headerEnd;
|
| + BasicEntity current;
|
| + while (index < footerStart) {
|
| + String line = lines[index].code;
|
| + BasicEntity next;
|
| + Match match = MEMBER_FUNCTION.firstMatch(line);
|
| + if (match != null) {
|
| + next = new MemberFunction(match.group(1), index);
|
| + } else {
|
| + match = STATICS.firstMatch(line);
|
| + if (match != null) {
|
| + next = new Statics(index);
|
| + } else {
|
| + match = MEMBER_OBJECT.firstMatch(line);
|
| + if (match != null) {
|
| + next = new MemberObject(match.group(1), index);
|
| + } else {
|
| + match = MEMBER_VALUE.firstMatch(line);
|
| + if (match != null) {
|
| + next = new MemberValue(match.group(1), index);
|
| + }
|
| + }
|
| + }
|
| + }
|
| + if (next != null) {
|
| + if (current != null) {
|
| + current.to = index;
|
| + }
|
| + children.add(current = next);
|
| + } else if (index == headerEnd) {
|
| + throw 'Failed to match first library block line:\n$line';
|
| + }
|
| +
|
| + index++;
|
| + }
|
| + if (current != null) {
|
| + current.to = footerStart;
|
| + }
|
| +
|
| + for (BasicEntity entity in children) {
|
| + entity.preprocess(lines);
|
| + }
|
| + }
|
| +
|
| + accept(OutputVisitor visitor, arg) => visitor.visitClass(this, arg);
|
| +
|
| + OutputEntity getEntityForLine(int line) {
|
| + if (line < headerEnd || line >= footerStart) {
|
| + return this;
|
| + }
|
| + for (BasicEntity child in children) {
|
| + if (child.interval.contains(line)) {
|
| + return child.getEntityForLine(line);
|
| + }
|
| + }
|
| + return null;
|
| + }
|
| +}
|
| +
|
| +/// A block defining static members of a Dart class.
|
| +class Statics extends BasicEntity {
|
| + List<BasicEntity> children = <BasicEntity>[];
|
| + int get headerEnd => from + 1;
|
| + int get footerStart => to - 1;
|
| +
|
| + Statics(int from) : super('statics', from);
|
| +
|
| + @override
|
| + EntityKind get kind => EntityKind.STATICS;
|
| +
|
| + Interval get header => new Interval(from, headerEnd);
|
| +
|
| + Interval get footer => new Interval(footerStart, to);
|
| +
|
| + bool get canHaveChildren => true;
|
| +
|
| + void preprocess(List<CodeLine> lines) {
|
| + int index = headerEnd;
|
| + BasicEntity current;
|
| + while (index < footerStart) {
|
| + String line = lines[index].code;
|
| + BasicEntity next;
|
| + Match matchFunction = STATIC_FUNCTION.firstMatch(line);
|
| + if (matchFunction != null) {
|
| + next = new MemberFunction(matchFunction.group(1), index);
|
| + }
|
| + if (next != null) {
|
| + if (current != null) {
|
| + current.to = index;
|
| + }
|
| + children.add(current = next);
|
| + } else if (index == headerEnd) {
|
| + throw 'Failed to match first statics line:\n$line';
|
| + }
|
| +
|
| + index++;
|
| + }
|
| + if (current != null) {
|
| + current.to = footerStart;
|
| + }
|
| + }
|
| +
|
| + accept(OutputVisitor visitor, arg) => visitor.visitStatics(this, arg);
|
| +
|
| + OutputEntity getEntityForLine(int line) {
|
| + if (line < headerEnd || line >= footerStart) {
|
| + return this;
|
| + }
|
| + for (BasicEntity child in children) {
|
| + if (child.interval.contains(line)) {
|
| + return child.getEntityForLine(line);
|
| + }
|
| + }
|
| + return null;
|
| + }
|
| +}
|
| +
|
| +class MemberFunction extends BasicEntity {
|
| + MemberFunction(String name, int from) : super(name, from);
|
| +
|
| + @override
|
| + EntityKind get kind => EntityKind.MEMBER_FUNCTION;
|
| +
|
| + accept(OutputVisitor visitor, arg) => visitor.visitMemberFunction(this, arg);
|
| +}
|
| +
|
| +class MemberObject extends BasicEntity {
|
| + MemberObject(String name, int from) : super(name, from);
|
| +
|
| + @override
|
| + EntityKind get kind => EntityKind.MEMBER_OBJECT;
|
| +
|
| + accept(OutputVisitor visitor, arg) => visitor.visitMemberObject(this, arg);
|
| +}
|
| +
|
| +class MemberValue extends BasicEntity {
|
| + MemberValue(String name, int from) : super(name, from);
|
| +
|
| + @override
|
| + EntityKind get kind => EntityKind.MEMBER_VALUE;
|
| +
|
| + accept(OutputVisitor visitor, arg) => visitor.visitMemberValue(this, arg);
|
| +}
|
| +
|
| +class StaticFunction extends BasicEntity {
|
| + StaticFunction(String name, int from) : super(name, from);
|
| +
|
| + @override
|
| + EntityKind get kind => EntityKind.STATIC_FUNCTION;
|
| +
|
| + accept(OutputVisitor visitor, arg) => visitor.visitStaticFunction(this, arg);
|
| +}
|
| +
|
| +class Interval {
|
| + final int from;
|
| + final int to;
|
| +
|
| + const Interval(this.from, this.to);
|
| +
|
| + int get length => to - from;
|
| +
|
| + bool contains(int value) {
|
| + return from <= value && value < to;
|
| + }
|
| +}
|
| +
|
| +enum CodeKind {
|
| + LIBRARY,
|
| + CLASS,
|
| + MEMBER,
|
| +}
|
| +
|
| +class CodeSource {
|
| + final CodeKind kind;
|
| + final Uri uri;
|
| + final String name;
|
| + final int begin;
|
| + final int end;
|
| +
|
| + CodeSource(this.kind, this.uri, this.name, this.begin, this.end);
|
| +
|
| + String toString() => '${toJson()}';
|
| +
|
| + Map toJson() {
|
| + return {
|
| + 'kind': kind.index,
|
| + 'uri': uri.toString(),
|
| + 'name': name,
|
| + 'begin': begin,
|
| + 'end': end,
|
| + };
|
| + }
|
| +
|
| + static CodeSource fromJson(Map json) {
|
| + if (json == null) return null;
|
| + return new CodeSource(
|
| + CodeKind.values[json['kind']],
|
| + Uri.parse(json['uri']),
|
| + json['name'],
|
| + json['begin'],
|
| + json['end']);
|
| + }
|
| +}
|
|
|