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']); |
+ } |
+} |